1mod classification;
3mod locations;
4mod matches;
5mod support;
6use crate::{OutlineTrail, Producer};
7pub use classification::*;
8pub use locations::ExplanationTextRoot;
9use mant_ir::{
10 Diagnostic, DocumentAddress, EntryKind, Inline, NameCase, NodeId, SourceSpan, ValueDomain,
11};
12pub use matches::*;
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15pub use support::*;
16
17pub const MAX_EXPLANATION_RESULTS: u32 = 256;
19pub const MAX_EXPLANATION_CANDIDATES: usize = 10_000;
21pub const MAX_EXPLANATION_CONTENT_BYTES: u32 = 4 * 1024 * 1024;
23pub const MAX_EXPLANATION_RELATIONS: usize = 4096;
25pub const MAX_EXPLANATION_RELATION_DEPTH: usize = 32;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
30#[serde(rename_all = "camelCase", deny_unknown_fields)]
31pub struct ExplanationOptions {
32 #[serde(default = "default_explanation_limit")]
34 #[schemars(range(min = 1, max = 256))]
35 pub limit: u32,
36 #[serde(default)]
38 pub offset: u32,
39 #[serde(default = "default_explanation_content_bytes")]
42 #[schemars(range(min = 1, max = 4_194_304))]
43 pub content_bytes: u32,
44}
45impl Default for ExplanationOptions {
46 fn default() -> Self {
47 Self {
48 limit: default_explanation_limit(),
49 offset: 0,
50 content_bytes: default_explanation_content_bytes(),
51 }
52 }
53}
54#[must_use]
56pub const fn default_explanation_limit() -> u32 {
57 50
58}
59#[must_use]
61pub const fn default_explanation_content_bytes() -> u32 {
62 1024 * 1024
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
67#[serde(rename_all = "camelCase", deny_unknown_fields)]
68pub struct ExplanationQuery {
69 #[schemars(length(min = 1, max = 512))]
71 pub entry: String,
72 #[serde(default)]
74 pub options: ExplanationOptions,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
79pub enum ExplanationSchema {
80 #[serde(rename = "mant.explanation/v0.11")]
82 V0Dot11,
83}
84impl ExplanationSchema {
85 pub const ID: &'static str = "mant.explanation/v0.11";
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
91#[serde(rename_all = "kebab-case")]
92pub enum ExplanationOutcome {
93 Evidence,
95 NoEvidence,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]
101#[serde(
102 tag = "kind",
103 rename_all = "kebab-case",
104 rename_all_fields = "camelCase",
105 deny_unknown_fields
106)]
107pub enum EvidenceBasis {
108 Name {
110 matches: Vec<ExplanationNameMatch>,
112 },
113 Form {
115 matches: Vec<ExplanationFormMatch>,
117 },
118 Literal,
120 Identity {
122 fields: Vec<ExplanationIdentityField>,
124 },
125 AliasGroup {
127 members: Vec<String>,
129 },
130 Related {
132 from: NodeId,
134 declarations: Vec<NodeId>,
136 },
137}
138
139#[derive(Deserialize)]
144#[serde(
145 tag = "kind",
146 rename_all = "kebab-case",
147 rename_all_fields = "camelCase",
148 deny_unknown_fields
149)]
150enum ClosedEvidenceBasis {
151 Name {
152 matches: Vec<ExplanationNameMatch>,
153 },
154 Form {
155 matches: Vec<ExplanationFormMatch>,
156 },
157 Literal {},
158 Identity {
159 fields: Vec<ExplanationIdentityField>,
160 },
161 AliasGroup {
162 members: Vec<String>,
163 },
164 Related {
165 from: NodeId,
166 declarations: Vec<NodeId>,
167 },
168}
169
170impl<'de> Deserialize<'de> for EvidenceBasis {
171 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
172 Ok(match ClosedEvidenceBasis::deserialize(deserializer)? {
173 ClosedEvidenceBasis::Name { matches } => Self::Name { matches },
174 ClosedEvidenceBasis::Form { matches } => Self::Form { matches },
175 ClosedEvidenceBasis::Literal {} => Self::Literal,
176 ClosedEvidenceBasis::Identity { fields } => Self::Identity { fields },
177 ClosedEvidenceBasis::AliasGroup { members } => Self::AliasGroup { members },
178 ClosedEvidenceBasis::Related { from, declarations } => {
179 Self::Related { from, declarations }
180 }
181 })
182 }
183}
184
185#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
187#[serde(rename_all = "camelCase", deny_unknown_fields)]
188pub struct ExplanationEntry {
189 pub kind: EntryKind,
191 pub case: NameCase,
193 pub names: Vec<String>,
195 pub forms: Vec<Vec<Inline>>,
197 pub name_bindings: Vec<ExplanationNameBinding>,
199 pub alias_groups: Vec<Vec<String>>,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub alias_of: Option<NodeId>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub value_domain: Option<ValueDomain>,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
211#[allow(clippy::struct_excessive_bools)]
213#[serde(rename_all = "camelCase", deny_unknown_fields)]
214pub struct ExplanationEvidence {
215 #[serde(skip_serializing_if = "Option::is_none")]
218 pub support: Option<usize>,
219 pub support_omitted: bool,
221 pub class: EvidenceClass,
223 pub ordinal: u32,
225 pub outline: OutlineTrail,
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub block_path: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub source: Option<SourceSpan>,
233 pub bases: Vec<EvidenceBasis>,
235 pub previews: Vec<ExplanationPreview>,
237 pub previews_omitted: bool,
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub entry: Option<ExplanationEntry>,
242 #[serde(skip_serializing_if = "Option::is_none")]
245 pub content: Option<ExplanationContent>,
246 pub details_omitted: bool,
248 pub match_details_omitted: bool,
250 pub name_bindings_omitted: bool,
252 pub content_omitted: bool,
254}
255
256impl ExplanationEvidence {
257 #[must_use]
260 pub const fn has_omitted_content(&self) -> bool {
261 self.content_omitted
262 || self.support_omitted
263 || self.details_omitted
264 || self.previews_omitted
265 || self.match_details_omitted
266 || self.name_bindings_omitted
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
272#[serde(tag = "kind", rename_all = "kebab-case", deny_unknown_fields)]
273pub enum ExplanationContent {
274 SharedEntry {
277 support: usize,
279 path: Vec<ExplanationBlockStep>,
281 #[serde(rename = "itemIndex")]
283 item_index: usize,
284 },
285 DeclarationMember {
289 support: usize,
291 #[serde(rename = "itemIndex")]
293 item_index: usize,
294 },
295 Entry {
297 block: mant_ir::Block,
299 },
300 Block {
302 block: mant_ir::Block,
304 },
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]
309#[serde(rename_all = "camelCase", deny_unknown_fields)]
310#[schemars(extend("$id" = "urn:mant:explanation:v0.11"))]
311pub struct QueryExplanation {
312 pub supports: Vec<ExplanationSupport>,
314 pub order: EvidenceOrder,
316 pub counts: EvidenceCounts,
318 pub schema: ExplanationSchema,
320 pub query: ExplanationQuery,
322 pub label: String,
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub address: Option<DocumentAddress>,
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub producer: Option<Producer>,
330 pub outcome: ExplanationOutcome,
332 pub total: u32,
335 pub returned: u32,
337 #[serde(skip_serializing_if = "Option::is_none")]
339 pub next_offset: Option<u32>,
340 pub truncation: ExplanationTruncation,
342 pub semantics_complete: bool,
344 pub diagnostics: Vec<Diagnostic>,
346 pub evidence: Vec<ExplanationEvidence>,
348}
349
350#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
352#[serde(rename_all = "camelCase", deny_unknown_fields)]
353pub struct ExplanationTruncation {
354 pub candidates: bool,
356 pub relations: bool,
358 pub content: bool,
360}
361
362#[derive(Deserialize)]
365#[serde(
366 remote = "QueryExplanation",
367 rename_all = "camelCase",
368 deny_unknown_fields
369)]
370struct QueryExplanationWire {
371 pub supports: Vec<ExplanationSupport>,
372 pub order: EvidenceOrder,
373 pub counts: EvidenceCounts,
374 pub schema: ExplanationSchema,
375 pub query: ExplanationQuery,
376 pub label: String,
377 #[serde(skip_serializing_if = "Option::is_none")]
378 pub address: Option<DocumentAddress>,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 pub producer: Option<Producer>,
381 pub outcome: ExplanationOutcome,
382 pub total: u32,
383 pub returned: u32,
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub next_offset: Option<u32>,
386 pub truncation: ExplanationTruncation,
387 pub semantics_complete: bool,
388 pub diagnostics: Vec<Diagnostic>,
389 pub evidence: Vec<ExplanationEvidence>,
390}
391impl<'de> Deserialize<'de> for QueryExplanation {
392 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
393 let value = QueryExplanationWire::deserialize(deserializer)?;
394 value
395 .validate_references()
396 .map_err(serde::de::Error::custom)?;
397 Ok(value)
398 }
399}