panproto_schema/protocol.rs
1//! Protocol definition.
2//!
3//! A [`Protocol`] identifies which schema theory and instance theory a
4//! particular data format uses, together with well-formedness rules
5//! for edges and the set of recognized vertex/constraint kinds.
6
7use panproto_gat::CompositionSpec;
8use serde::{Deserialize, Serialize};
9
10/// A well-formedness rule for edges of a given kind.
11///
12/// When `src_kinds` is non-empty, only vertices whose kind appears in the
13/// list may serve as the source of an edge of this kind. An empty list
14/// means any vertex kind is allowed. The same applies to `tgt_kinds`.
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub struct EdgeRule {
17 /// The edge kind this rule governs (e.g., `"prop"`, `"record-schema"`).
18 pub edge_kind: String,
19 /// Permitted source vertex kinds (empty = any).
20 pub src_kinds: Vec<String>,
21 /// Permitted target vertex kinds (empty = any).
22 pub tgt_kinds: Vec<String>,
23}
24
25/// Identifies the schema and instance theories for a data-format protocol,
26/// together with structural well-formedness rules.
27///
28/// Protocols are the Level-1 configuration objects that drive schema
29/// construction and validation. Each protocol names a schema theory GAT
30/// and an instance theory GAT (both defined in `panproto-protocols`),
31/// and supplies edge rules, recognized vertex kinds, and constraint sorts.
32#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
33#[allow(clippy::struct_excessive_bools)]
34pub struct Protocol {
35 /// Human-readable protocol name (e.g., `"atproto"`, `"sql"`).
36 pub name: String,
37 /// Name of the schema theory GAT in the theory registry.
38 pub schema_theory: String,
39 /// Name of the instance theory GAT in the theory registry.
40 pub instance_theory: String,
41 /// Composition recipe that produced the schema theory.
42 pub schema_composition: Option<CompositionSpec>,
43 /// Composition recipe that produced the instance theory.
44 pub instance_composition: Option<CompositionSpec>,
45 /// Well-formedness rules for each edge kind.
46 pub edge_rules: Vec<EdgeRule>,
47 /// Vertex kinds that are considered "object-like" (containers).
48 pub obj_kinds: Vec<String>,
49 /// Recognized constraint sorts (e.g., `"maxLength"`, `"format"`).
50 pub constraint_sorts: Vec<String>,
51
52 // -- structural feature flags (all default to false) --
53 /// Whether this protocol uses ordered collections (`ThOrder`).
54 #[serde(default)]
55 pub has_order: bool,
56 /// Whether this protocol has coproduct/union types (`ThCoproduct`).
57 #[serde(default)]
58 pub has_coproducts: bool,
59 /// Whether this protocol supports recursive types (`ThRecursion`).
60 #[serde(default)]
61 pub has_recursion: bool,
62 /// Whether this protocol has causal/temporal ordering (`ThCausal`).
63 #[serde(default)]
64 pub has_causal: bool,
65 /// Whether this protocol uses nominal identity (`ThNominal`).
66 #[serde(default)]
67 pub nominal_identity: bool,
68
69 // -- enrichment feature flags (all default to false) --
70 /// Whether this protocol supports default value expressions (`ThValued`).
71 #[serde(default)]
72 pub has_defaults: bool,
73 /// Whether this protocol supports type coercion expressions (`ThCoercible`).
74 #[serde(default)]
75 pub has_coercions: bool,
76 /// Whether this protocol supports merge/split expressions (`ThMergeable`).
77 #[serde(default)]
78 pub has_mergers: bool,
79 /// Whether this protocol supports conflict resolution policies (`ThPolicied`).
80 #[serde(default)]
81 pub has_policies: bool,
82}
83
84impl Protocol {
85 /// Returns the [`EdgeRule`] for the given edge kind, if one exists.
86 #[must_use]
87 pub fn find_edge_rule(&self, edge_kind: &str) -> Option<&EdgeRule> {
88 self.edge_rules.iter().find(|r| r.edge_kind == edge_kind)
89 }
90
91 /// Returns `true` if `kind` is a recognized vertex kind in this protocol.
92 ///
93 /// The set of recognized kinds is the union of all kinds mentioned in
94 /// edge rules (both source and target) plus `obj_kinds`.
95 #[must_use]
96 pub fn is_known_vertex_kind(&self, kind: &str) -> bool {
97 if self.obj_kinds.iter().any(|k| k == kind) {
98 return true;
99 }
100 for rule in &self.edge_rules {
101 if rule.src_kinds.iter().any(|k| k == kind) {
102 return true;
103 }
104 if rule.tgt_kinds.iter().any(|k| k == kind) {
105 return true;
106 }
107 }
108 false
109 }
110}