Skip to main content

mant_protocol/
document.rs

1//! Versioned wire representation of normalized document IR.
2
3use mant_ir::{
4    Block, Diagnostic, Document as IrDocument, DocumentMeta, DocumentSource, ParserInfo, Section,
5};
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8
9/// Exact schema marker for a normalized structured document response.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
11pub enum DocumentSchema {
12    /// Version 0.8 of the pre-stable structured-document protocol.
13    #[serde(rename = "mant.document/v0.8")]
14    V0Dot8,
15}
16
17impl DocumentSchema {
18    /// Serialized identifier of the current document contract.
19    pub const ID: &'static str = "mant.document/v0.8";
20}
21
22/// Identifies `ManT` and the parser used to build a wire document.
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
24#[serde(rename_all = "camelCase")]
25pub struct Producer {
26    /// Process implementation name.
27    pub name: String,
28    /// Process package version.
29    pub version: String,
30    /// Parser implementation, when an authoritative document was parsed.
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub engine: Option<Engine>,
33}
34
35/// Parser implementation recorded at the process boundary.
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
37#[serde(rename_all = "camelCase")]
38pub struct Engine {
39    /// Parser implementation name.
40    pub name: String,
41    /// Parser implementation version.
42    pub version: String,
43}
44
45/// Serializable v0.8 envelope around `ManT`'s protocol-independent document IR.
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
47#[serde(rename_all = "camelCase")]
48pub struct DocumentResponse {
49    /// Exact response schema discriminator.
50    pub schema: DocumentSchema,
51    /// Process and parser provenance.
52    pub producer: Producer,
53    /// Original source identity.
54    pub source: DocumentSource,
55    /// Source-neutral document metadata.
56    pub meta: DocumentMeta,
57    /// Recoverable parsing and validation findings.
58    #[serde(default, skip_serializing_if = "Vec::is_empty")]
59    pub diagnostics: Vec<Diagnostic>,
60    /// Content preceding the first section.
61    #[serde(default, skip_serializing_if = "Vec::is_empty")]
62    pub blocks: Vec<Block>,
63    /// Top-level semantic sections in source order.
64    pub sections: Vec<Section>,
65}
66
67impl Producer {
68    /// Construct process provenance for a normalized document.
69    #[must_use]
70    pub fn for_document(document: &IrDocument) -> Self {
71        Self {
72            name: "mant".to_owned(),
73            version: env!("CARGO_PKG_VERSION").to_owned(),
74            engine: document.parser.as_ref().map(|parser| Engine {
75                name: parser.name.clone(),
76                version: parser.version.clone(),
77            }),
78        }
79    }
80}
81
82impl From<&IrDocument> for DocumentResponse {
83    fn from(document: &IrDocument) -> Self {
84        Self {
85            schema: DocumentSchema::V0Dot8,
86            producer: Producer::for_document(document),
87            source: document.source.clone(),
88            meta: document.meta.clone(),
89            diagnostics: document.diagnostics.clone(),
90            blocks: document.blocks.clone(),
91            sections: document.sections.clone(),
92        }
93    }
94}
95
96impl From<DocumentResponse> for IrDocument {
97    fn from(document: DocumentResponse) -> Self {
98        Self {
99            parser: document.producer.engine.map(|engine| ParserInfo {
100                name: engine.name,
101                version: engine.version,
102            }),
103            source: document.source,
104            meta: document.meta,
105            diagnostics: document.diagnostics,
106            blocks: document.blocks,
107            sections: document.sections,
108        }
109    }
110}