Skip to main content

made_api/
authoring_views.rs

1use serde::{Deserialize, Serialize};
2
3/// One defect found in a definition draft.
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5pub struct DefinitionDefectView {
6    /// `error` or `warning`.
7    pub severity: String,
8    /// Where, in the author's terms: "state `X`", "guard `Y`", …
9    pub locus: String,
10    /// What is wrong, in a sentence.
11    pub defect: String,
12    /// Whether this alone prevents publication.
13    pub blocking: bool,
14}
15
16/// What analysis found — all of it.
17///
18/// Every defect at once, never the first one (ADR-002 upstream): fixing
19/// defects one at a time spends the author's attention on round trips.
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct DefinitionAnalysisView {
22    /// Identity declared by the parsed draft.
23    pub definition_name: String,
24    pub definition_version: String,
25    /// Whether the draft, as analyzed, could be published.
26    pub publishable: bool,
27    /// Canonical hex digest the executable definition will publish with.
28    ///
29    /// Present exactly when the draft is publishable. This is the same
30    /// identity [`PublishedDefinitionView::digest`] returns and ceremony
31    /// instances bind to; it is not a hash of the source bytes.
32    pub definition_digest: Option<String>,
33    pub defects: Vec<DefinitionDefectView>,
34}
35
36/// A definition that is now published, or already was.
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub struct PublishedDefinitionView {
39    pub name: String,
40    pub version: String,
41    /// Hex digest of the published content — what an instance binds to, and
42    /// what makes "this exact procedure ran" provable.
43    pub digest: String,
44    /// True when the identical content was already published under this name
45    /// and version. Nothing changed, and nothing needed to — which is what
46    /// makes a retried publish safe.
47    pub already_published: bool,
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn an_analysis_survives_the_wire() {
56        let analysis = DefinitionAnalysisView {
57            definition_name: "scope_discovery".to_owned(),
58            definition_version: "1.0".to_owned(),
59            publishable: false,
60            definition_digest: None,
61            defects: vec![DefinitionDefectView {
62                severity: "error".to_owned(),
63                locus: "state `ORPHAN`".to_owned(),
64                defect: "state is unreachable".to_owned(),
65                blocking: true,
66            }],
67        };
68        let bytes = serde_json::to_vec(&analysis).expect("serializes");
69        assert_eq!(
70            serde_json::from_slice::<DefinitionAnalysisView>(&bytes).expect("deserializes"),
71            analysis
72        );
73    }
74
75    #[test]
76    fn a_publication_names_what_an_instance_will_bind_to() {
77        let published = PublishedDefinitionView {
78            name: "scope_discovery".to_owned(),
79            version: "1.0".to_owned(),
80            digest: "abc123".to_owned(),
81            already_published: false,
82        };
83        assert!(
84            !published.digest.is_empty(),
85            "a publication without a digest cannot be bound to, only believed"
86        );
87    }
88}