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    source_key: &'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(source_key: &'static str, kind: SourceRuleKind, args: Args) -> Self {
88        Self {
89            source_key,
90            kind,
91            args,
92        }
93    }
94
95    /// Return the immutable base-rule identity.
96    #[must_use]
97    pub const fn source_key(&self) -> &'static str {
98        self.source_key
99    }
100
101    /// Return the frozen rule operation.
102    #[must_use]
103    pub const fn kind(&self) -> SourceRuleKind {
104        self.kind
105    }
106
107    /// Borrow rule operands.
108    #[must_use]
109    pub const fn args(&self) -> &Args {
110        &self.args
111    }
112}
113
114impl ValidateNode for SourceRule {
115    fn validate(&self) -> Result<(), ErrorTree> {
116        let mut errs = ErrorTree::new();
117        validate_source_key(
118            &mut errs,
119            "rule",
120            self.source_key(),
121            icydb_schema::RuleSourceKey::try_new,
122        );
123        let expected_args = match self.kind() {
124            SourceRuleKind::NumericMinimum => 1,
125            SourceRuleKind::LengthRange | SourceRuleKind::NumericRange => 2,
126        };
127        if self.args().0.len() != expected_args
128            || self
129                .args()
130                .0
131                .iter()
132                .any(|arg| !matches!(arg, Arg::Number(_)))
133        {
134            err!(
135                errs,
136                "rule '{}' requires {expected_args} numeric argument(s)",
137                self.source_key(),
138            );
139        }
140        errs.result()
141    }
142}
143
144impl VisitableNode for SourceRule {}
145
146///
147/// SourceRuleKind
148///
149/// Closed durable-rule vocabulary translated into accepted constraints.
150/// This enum describes authoring metadata only; accepted schema owns runtime
151/// evaluation after fragment lowering.
152///
153
154#[derive(Clone, Copy, Debug, Serialize)]
155pub enum SourceRuleKind {
156    /// Inclusive character/octet/collection length range.
157    LengthRange,
158    /// Inclusive numeric minimum.
159    NumericMinimum,
160    /// Inclusive numeric range.
161    NumericRange,
162}
163
164///
165/// TypeNormalizer
166///
167/// Reference to one normalizer node plus its bound argument list.
168///
169
170#[derive(Clone, Debug, Serialize)]
171pub struct TypeNormalizer {
172    path: &'static str,
173    args: Args,
174}
175
176impl TypeNormalizer {
177    #[must_use]
178    pub const fn new(path: &'static str, args: Args) -> Self {
179        Self { path, args }
180    }
181
182    #[must_use]
183    pub const fn path(&self) -> &'static str {
184        self.path
185    }
186
187    #[must_use]
188    pub const fn args(&self) -> &Args {
189        &self.args
190    }
191}
192
193impl ValidateNode for TypeNormalizer {
194    fn validate(&self) -> Result<(), ErrorTree> {
195        let mut errs = ErrorTree::new();
196
197        // Resolve the referenced normalizer path against the schema graph.
198        let res = schema_read().check_node_as::<Normalizer>(self.path());
199        if let Err(e) = res {
200            errs.add(e.to_string());
201        }
202
203        errs.result()
204    }
205}
206
207impl VisitableNode for TypeNormalizer {}
208
209///
210/// TypeValidator
211///
212/// Reference to one validator node plus its bound argument list.
213///
214
215#[derive(Clone, Debug, Serialize)]
216pub struct TypeValidator {
217    path: &'static str,
218    args: Args,
219}
220
221impl TypeValidator {
222    #[must_use]
223    pub const fn new(path: &'static str, args: Args) -> Self {
224        Self { path, args }
225    }
226
227    #[must_use]
228    pub const fn path(&self) -> &'static str {
229        self.path
230    }
231
232    #[must_use]
233    pub const fn args(&self) -> &Args {
234        &self.args
235    }
236}
237
238impl ValidateNode for TypeValidator {
239    fn validate(&self) -> Result<(), ErrorTree> {
240        let mut errs = ErrorTree::new();
241
242        // Resolve the referenced validator path against the schema graph.
243        let res = schema_read().check_node_as::<Validator>(self.path());
244        if let Err(e) = res {
245            errs.add(e.to_string());
246        }
247
248        errs.result()
249    }
250}
251
252impl VisitableNode for TypeValidator {}