Skip to main content

zenkey_fleet/report/
schema.rs

1//! The schema plane (RFC 08 §7): what a producer serves for a type name,
2//! and the drift between what was served and what was declared.
3
4use super::asked::Asked;
5use serde::Serialize;
6
7/// One type's schema entry as one producer serves it (issue #51).
8#[derive(Debug, Clone, Serialize)]
9pub struct SchemaRow {
10    pub producer: String,
11    pub type_name: String,
12    pub kind: String,
13    pub hash: String,
14    /// The schema document, when the caller asked for the full form.
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub document: Option<serde_json::Value>,
17}
18
19/// One producer's served `describe` reply, rendered (issue #51).
20///
21/// `served = false` is the honest degradation RFC 08 §7 leaves room for —
22/// `describe` is a SHOULD, so a producer that serves none has said nothing
23/// about its types, which is not the same as having no types.
24#[derive(Debug, Clone, Serialize)]
25pub struct SchemaDump {
26    pub producer: String,
27    pub served: bool,
28    /// The declaring app, as the served set names it.
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub app: Option<String>,
31    pub types: Vec<SchemaRow>,
32    /// Registry-declared type names this producer's set does **not** cover —
33    /// RFC 08 §7's totality clause, checked where the user is already looking.
34    /// `NotAsked` = no registry was loaded, so totality was never checked —
35    /// not asked is not answered no (RFC 09 §5.1 O4); `Asked(vec![])` is the
36    /// actual clean bill.
37    #[serde(skip_serializing_if = "Asked::is_not_asked", default)]
38    pub missing: Asked<Vec<String>>,
39}
40
41/// One producer's identity claim for a type name.
42#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
43pub struct SchemaServer {
44    pub producer: String,
45    /// The `sha256:` identity this producer served, if it served one.
46    ///
47    /// `NotAsked` means the describe reply carried **no** hash — which is not
48    /// an empty hash, and is the distinction the flat `(String, String)` shape
49    /// could not make: two producers that each said nothing compared equal and
50    /// were reported as agreeing (#370).
51    #[serde(skip_serializing_if = "Asked::is_not_asked")]
52    pub hash: Asked<String>,
53}
54
55/// What comparing a type name's identity claims established.
56#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
57#[serde(rename_all = "snake_case")]
58pub enum DriftVerdict {
59    /// Two or more producers served *different* identities. A defect —
60    /// RFC 08 §7 calls it a `doctor` finding in as many words.
61    Disagree,
62    /// At least one producer served no identity at all, so agreement cannot
63    /// be established. **Not a defect**: an unanswered question, and reporting
64    /// it as agreement was the O4 failure (RFC 09 §5.1) this exists to name.
65    Unjudgeable,
66}
67
68/// One type name's identity claims across the fleet — "a `doctor` finding" by
69/// RFC 08 §7's own words (issue #41), with the O4 split #370 added.
70#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
71pub struct SchemaDrift {
72    pub type_name: String,
73    /// Every producer observed serving the name, and what it claimed.
74    pub servers: Vec<SchemaServer>,
75    pub verdict: DriftVerdict,
76}
77
78/// A type the producer's slice references that its served describe set does
79/// not cover — a violation of RFC 08 §7's totality clause.
80#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
81pub struct TotalityGap {
82    pub producer: String,
83    pub missing: Vec<String>,
84}