1use crate::{ScopeKind, StatusCode, SubjectKind, UncertaintyCode, WarningCode};
2use blazingly_json::Value;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use std::collections::BTreeMap;
5
6#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
8pub enum GraphRevision {
9 #[default]
10 Missing,
11 Null,
12 Value(String),
13}
14
15impl GraphRevision {
16 #[must_use]
17 pub const fn is_missing(&self) -> bool {
18 matches!(self, Self::Missing)
19 }
20
21 #[must_use]
22 pub fn as_deref(&self) -> Option<&str> {
23 match self {
24 Self::Value(value) => Some(value),
25 Self::Missing | Self::Null => None,
26 }
27 }
28}
29
30impl Serialize for GraphRevision {
31 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
32 where
33 S: Serializer,
34 {
35 match self {
36 Self::Missing | Self::Null => serializer.serialize_none(),
37 Self::Value(value) => serializer.serialize_str(value),
38 }
39 }
40}
41
42impl<'de> Deserialize<'de> for GraphRevision {
43 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
44 where
45 D: Deserializer<'de>,
46 {
47 Option::<String>::deserialize(deserializer)
48 .map(|value| value.map_or(Self::Null, Self::Value))
49 }
50}
51
52#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct TypedSubject {
56 pub kind: SubjectKind,
57 pub value: String,
58 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
59 pub extensions: BTreeMap<String, Value>,
60}
61
62impl TypedSubject {
63 #[must_use]
64 pub fn new(kind: impl Into<SubjectKind>, value: impl Into<String>) -> Self {
65 Self {
66 kind: kind.into(),
67 value: value.into(),
68 extensions: BTreeMap::new(),
69 }
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct EvidenceScope {
77 pub kind: ScopeKind,
78 pub value: String,
79 #[serde(default, skip_serializing_if = "Vec::is_empty")]
80 pub roots: Vec<String>,
81 #[serde(default, skip_serializing_if = "Vec::is_empty")]
82 pub languages: Vec<String>,
83 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
84 pub extensions: BTreeMap<String, Value>,
85}
86
87impl EvidenceScope {
88 #[must_use]
89 pub fn new(kind: impl Into<ScopeKind>, value: impl Into<String>) -> Self {
90 Self {
91 kind: kind.into(),
92 value: value.into(),
93 roots: Vec::new(),
94 languages: Vec::new(),
95 extensions: BTreeMap::new(),
96 }
97 }
98}
99
100#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct PlannerIdentity {
104 pub name: String,
105 pub version: String,
106 pub backend: String,
107 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub backend_version: Option<String>,
109 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
110 pub extensions: BTreeMap<String, Value>,
111}
112
113impl PlannerIdentity {
114 #[must_use]
115 pub fn new(
116 name: impl Into<String>,
117 version: impl Into<String>,
118 backend: impl Into<String>,
119 ) -> Self {
120 Self {
121 name: name.into(),
122 version: version.into(),
123 backend: backend.into(),
124 backend_version: None,
125 extensions: BTreeMap::new(),
126 }
127 }
128}
129
130#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct CompletenessProof {
134 pub scope: EvidenceScope,
135 pub planner: PlannerIdentity,
136 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
137 pub extensions: BTreeMap<String, Value>,
138}
139
140impl CompletenessProof {
141 #[must_use]
142 pub fn new(scope: EvidenceScope, planner: PlannerIdentity) -> Self {
143 Self {
144 scope,
145 planner,
146 extensions: BTreeMap::new(),
147 }
148 }
149}
150
151#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
153#[serde(rename_all = "camelCase")]
154pub struct UncertainReference {
155 #[serde(default, skip_serializing_if = "Option::is_none")]
156 pub path: Option<String>,
157 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub file: Option<String>,
159 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub line: Option<u32>,
161 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub subject: Option<TypedSubject>,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub kind: Option<UncertaintyCode>,
165 #[serde(default, skip_serializing_if = "Option::is_none")]
166 pub reason: Option<UncertaintyCode>,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub excerpt: Option<String>,
169 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
170 pub extensions: BTreeMap<String, Value>,
171}
172
173impl UncertainReference {
174 #[must_use]
175 pub fn repo_path(&self) -> Option<&str> {
176 self.path.as_deref().or(self.file.as_deref())
177 }
178}
179
180#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
182#[serde(rename_all = "camelCase")]
183pub struct NotModified {
184 #[serde(default, skip_serializing_if = "Option::is_none")]
185 pub path: Option<String>,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub file: Option<String>,
188 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub subject: Option<TypedSubject>,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
191 pub operation_index: Option<u32>,
192 pub reason: String,
193 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
194 pub extensions: BTreeMap<String, Value>,
195}
196
197#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct PlanEvidence {
201 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub created_at: Option<String>,
203 #[serde(default, skip_serializing_if = "GraphRevision::is_missing")]
204 pub graph_revision: GraphRevision,
205 #[serde(default, skip_serializing_if = "Option::is_none")]
206 pub completeness_proof: Option<CompletenessProof>,
207 #[serde(default, skip_serializing_if = "Option::is_none")]
208 pub uncertain_references: Option<Vec<UncertainReference>>,
209 #[serde(default, skip_serializing_if = "Option::is_none")]
210 pub not_modified: Option<Vec<NotModified>>,
211 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub warnings: Option<Vec<WarningCode>>,
213 #[serde(default, skip_serializing_if = "Option::is_none")]
214 pub follow_up: Option<String>,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
216 pub syntax_check: Option<StatusCode>,
217 #[serde(flatten, default, skip_serializing_if = "BTreeMap::is_empty")]
218 pub extensions: BTreeMap<String, Value>,
219}
220
221pub type PlanAnnotations = PlanEvidence;