Skip to main content

icydb_model/node/
type.rs

1use crate::prelude::*;
2
3///
4/// Type
5///
6/// Canonical runtime type descriptor for one schema node's attached normalizers
7/// and validators.
8///
9
10#[derive(Clone, Debug, Serialize)]
11pub struct Type {
12    #[serde(skip_serializing_if = "<[_]>::is_empty")]
13    normalizers: &'static [TypeNormalizer],
14
15    #[serde(skip_serializing_if = "<[_]>::is_empty")]
16    validators: &'static [TypeValidator],
17
18    #[serde(skip_serializing_if = "<[_]>::is_empty")]
19    rules: &'static [SourceRule],
20}
21
22impl Type {
23    #[must_use]
24    pub const fn new(
25        normalizers: &'static [TypeNormalizer],
26        validators: &'static [TypeValidator],
27        rules: &'static [SourceRule],
28    ) -> Self {
29        Self {
30            normalizers,
31            validators,
32            rules,
33        }
34    }
35
36    #[must_use]
37    pub const fn normalizers(&self) -> &'static [TypeNormalizer] {
38        self.normalizers
39    }
40
41    #[must_use]
42    pub const fn validators(&self) -> &'static [TypeValidator] {
43        self.validators
44    }
45
46    /// Borrow explicitly declared durable rules.
47    #[must_use]
48    pub const fn rules(&self) -> &'static [SourceRule] {
49        self.rules
50    }
51}
52
53impl ValidateNode for Type {}
54
55impl VisitableNode for Type {
56    fn drive<V: Visitor>(&self, v: &mut V) {
57        for node in self.normalizers() {
58            node.accept(v);
59        }
60        for node in self.validators() {
61            node.accept(v);
62        }
63        for node in self.rules() {
64            node.accept(v);
65        }
66    }
67}
68
69///
70/// SourceRule
71///
72/// Compiler-authored durable rule template carried by one reusable type.
73/// Fragment lowering instantiates it for each persisted field use; it is not
74/// an application callback or a database runtime evaluator.
75///
76
77#[derive(Clone, Debug, Serialize)]
78pub struct SourceRule {
79    name: &'static str,
80    kind: SourceRuleKind,
81    args: Args,
82}
83
84impl SourceRule {
85    /// Construct one explicit reusable rule template.
86    #[must_use]
87    pub const fn new(name: &'static str, kind: SourceRuleKind, args: Args) -> Self {
88        Self { name, kind, args }
89    }
90
91    /// Return the current declared rule name.
92    #[must_use]
93    pub const fn name(&self) -> &'static str {
94        self.name
95    }
96
97    /// Return the frozen rule operation.
98    #[must_use]
99    pub const fn kind(&self) -> SourceRuleKind {
100        self.kind
101    }
102
103    /// Borrow rule operands.
104    #[must_use]
105    pub const fn args(&self) -> &Args {
106        &self.args
107    }
108}
109
110impl ValidateNode for SourceRule {
111    fn validate(&self) -> Result<(), ErrorTree> {
112        let mut errs = ErrorTree::new();
113        validate_source_name(
114            &mut errs,
115            "rule",
116            self.name(),
117            icydb_schema::RuleSourceKey::try_new,
118        );
119        let expected_args = match self.kind() {
120            SourceRuleKind::NumericMinimum => 1,
121            SourceRuleKind::LengthRange | SourceRuleKind::NumericRange => 2,
122        };
123        if self.args().0.len() != expected_args
124            || self
125                .args()
126                .0
127                .iter()
128                .any(|arg| !matches!(arg, Arg::Number(_)))
129        {
130            err!(
131                errs,
132                "rule '{}' requires {expected_args} numeric argument(s)",
133                self.name(),
134            );
135        }
136        errs.result()
137    }
138}
139
140impl VisitableNode for SourceRule {}
141
142///
143/// SourceRuleKind
144///
145/// Closed durable-rule vocabulary translated into accepted constraints.
146/// This enum describes authoring metadata only; accepted schema owns runtime
147/// evaluation after fragment lowering.
148///
149
150#[derive(Clone, Copy, Debug, Serialize)]
151pub enum SourceRuleKind {
152    /// Inclusive character/octet/collection length range.
153    LengthRange,
154    /// Inclusive numeric minimum.
155    NumericMinimum,
156    /// Inclusive numeric range.
157    NumericRange,
158}
159
160///
161/// TypeNormalizer
162///
163/// Reference to one normalizer node plus its bound argument list.
164///
165
166#[derive(Clone, Debug, Serialize)]
167pub struct TypeNormalizer {
168    path: &'static str,
169    args: Args,
170}
171
172impl TypeNormalizer {
173    #[must_use]
174    pub const fn new(path: &'static str, args: Args) -> Self {
175        Self { path, args }
176    }
177
178    #[must_use]
179    pub const fn path(&self) -> &'static str {
180        self.path
181    }
182
183    #[must_use]
184    pub const fn args(&self) -> &Args {
185        &self.args
186    }
187}
188
189impl ValidateNode for TypeNormalizer {
190    fn validate(&self) -> Result<(), ErrorTree> {
191        let mut errs = ErrorTree::new();
192
193        // Resolve the referenced normalizer path against the schema graph.
194        let res = schema_read().check_node_as::<Normalizer>(self.path());
195        if let Err(e) = res {
196            errs.add(e.to_string());
197        }
198
199        errs.result()
200    }
201}
202
203impl VisitableNode for TypeNormalizer {}
204
205///
206/// TypeValidator
207///
208/// Reference to one validator node plus its bound argument list.
209///
210
211#[derive(Clone, Debug, Serialize)]
212pub struct TypeValidator {
213    path: &'static str,
214    args: Args,
215}
216
217impl TypeValidator {
218    #[must_use]
219    pub const fn new(path: &'static str, args: Args) -> Self {
220        Self { path, args }
221    }
222
223    #[must_use]
224    pub const fn path(&self) -> &'static str {
225        self.path
226    }
227
228    #[must_use]
229    pub const fn args(&self) -> &Args {
230        &self.args
231    }
232}
233
234impl ValidateNode for TypeValidator {
235    fn validate(&self) -> Result<(), ErrorTree> {
236        let mut errs = ErrorTree::new();
237
238        // Resolve the referenced validator path against the schema graph.
239        let res = schema_read().check_node_as::<Validator>(self.path());
240        if let Err(e) = res {
241            errs.add(e.to_string());
242        }
243
244        errs.result()
245    }
246}
247
248impl VisitableNode for TypeValidator {}