Skip to main content

icydb_schema/node/
mod.rs

1//! Schema node graph for validated canister/entity/type definitions.
2//!
3//! This module owns the typed node descriptors used by schema validation,
4//! derive code generation, and visitor traversal.
5
6mod arg;
7mod canister;
8mod def;
9mod entity;
10mod r#enum;
11mod field;
12mod index;
13mod item;
14mod list;
15mod map;
16mod newtype;
17mod primary_key;
18mod record;
19mod sanitizer;
20mod schema;
21mod set;
22mod store;
23mod tuple;
24mod r#type;
25mod validator;
26mod value;
27
28use crate::{
29    prelude::*,
30    visit::{Event, Visitor},
31};
32use std::any::Any;
33use thiserror::Error as ThisError;
34
35pub use arg::*;
36pub use canister::*;
37pub use def::*;
38pub use entity::*;
39pub use r#enum::*;
40pub use field::*;
41pub use index::*;
42pub use item::*;
43pub use list::*;
44pub use map::*;
45pub use newtype::*;
46pub use primary_key::*;
47pub use record::*;
48pub use sanitizer::*;
49pub use schema::*;
50pub use set::*;
51pub use store::*;
52pub use tuple::*;
53pub use r#type::*;
54pub use validator::*;
55pub use value::*;
56
57const RESERVED_INTERNAL_MEMORY_ID: u8 = u8::MAX;
58
59///
60/// NodeError
61///
62/// Error raised when schema-node lookup or downcasting crosses an invalid
63/// boundary.
64///
65
66#[derive(Debug, ThisError)]
67pub enum NodeError {
68    #[error("{0} is an incorrect node type")]
69    IncorrectNodeType(String),
70
71    #[error("path not found: {0}")]
72    PathNotFound(String),
73}
74
75///
76/// NODE TRAITS
77///
78
79///
80/// MacroNode
81///
82/// Shared trait implemented by every concrete schema node descriptor.
83/// `as_any` keeps type erasure and downcasting local to the schema-node
84/// boundary instead of leaking it into callers.
85///
86
87pub trait MacroNode: Any {
88    fn as_any(&self) -> &dyn Any;
89}
90
91///
92/// TypeNode
93///
94/// Shared trait for schema nodes that expose one canonical runtime `Type`
95/// descriptor to validators and code generators.
96///
97
98pub trait TypeNode: MacroNode {
99    fn ty(&self) -> &Type;
100}
101
102///
103/// ValidateNode
104///
105/// Trait implemented by schema nodes that validate local invariants against
106/// the surrounding schema graph.
107///
108
109pub trait ValidateNode {
110    fn validate(&self) -> Result<(), ErrorTree> {
111        Ok(())
112    }
113}
114
115///
116/// VisitableNode
117///
118/// Trait implemented by schema nodes that participate in recursive visitor
119/// traversal with canonical route-key ordering.
120///
121
122pub trait VisitableNode: ValidateNode {
123    // Route key contributes one node-local path segment to the visitor path.
124    fn route_key(&self) -> String {
125        String::new()
126    }
127
128    // Drive the enter/children/exit visitor sequence for this node.
129    fn accept<V: Visitor>(&self, visitor: &mut V) {
130        visitor.push(&self.route_key());
131        visitor.visit(self, Event::Enter);
132        self.drive(visitor);
133        visitor.visit(self, Event::Exit);
134        visitor.pop();
135    }
136
137    // Visit child nodes in canonical order.
138    fn drive<V: Visitor>(&self, _: &mut V) {}
139}
140
141// Validate one memory id against the declared canister range.
142pub(crate) fn validate_memory_id_in_range(
143    errs: &mut ErrorTree,
144    label: &str,
145    memory_id: u8,
146    min: u8,
147    max: u8,
148) {
149    if memory_id < min || memory_id > max {
150        err!(errs, "{label} {memory_id} outside of range {min}-{max}");
151    }
152}
153
154// Reject memory id values reserved by stable-structures internals.
155pub(crate) fn validate_memory_id_not_reserved(errs: &mut ErrorTree, label: &str, memory_id: u8) {
156    if memory_id == RESERVED_INTERNAL_MEMORY_ID {
157        err!(
158            errs,
159            "{label} {memory_id} is reserved for stable-structures internals",
160        );
161    }
162}