Skip to main content

icydb_schema/node/
schema.rs

1use crate::{Error, prelude::*};
2use canic_cdk::utils::time::now_secs;
3use std::{any::Any, collections::BTreeMap};
4
5///
6/// SchemaNode
7///
8
9#[remain::sorted]
10#[derive(Clone, Debug, Serialize)]
11pub enum SchemaNode {
12    Canister(Canister),
13    DataStore(DataStore),
14    Entity(Entity),
15    Enum(Enum),
16    IndexStore(IndexStore),
17    List(List),
18    Map(Map),
19    Newtype(Newtype),
20    Record(Record),
21    Sanitizer(Sanitizer),
22    Set(Set),
23    Tuple(Tuple),
24    Validator(Validator),
25}
26
27impl SchemaNode {
28    #[must_use]
29    pub fn get_type(&self) -> Option<Box<dyn TypeNode>> {
30        match self {
31            Self::Entity(n) => Some(Box::new(n.clone())),
32            Self::Enum(n) => Some(Box::new(n.clone())),
33            Self::List(n) => Some(Box::new(n.clone())),
34            Self::Map(n) => Some(Box::new(n.clone())),
35            Self::Newtype(n) => Some(Box::new(n.clone())),
36            Self::Record(n) => Some(Box::new(n.clone())),
37            Self::Set(n) => Some(Box::new(n.clone())),
38            Self::Tuple(n) => Some(Box::new(n.clone())),
39            _ => {
40                // NOTE: Non-type nodes are intentionally excluded from type lookups.
41                None
42            }
43        }
44    }
45}
46
47impl SchemaNode {
48    const fn def(&self) -> &Def {
49        match self {
50            Self::Canister(n) => &n.def,
51            Self::DataStore(n) => &n.def,
52            Self::Entity(n) => &n.def,
53            Self::Enum(n) => &n.def,
54            Self::IndexStore(n) => &n.def,
55            Self::List(n) => &n.def,
56            Self::Map(n) => &n.def,
57            Self::Newtype(n) => &n.def,
58            Self::Record(n) => &n.def,
59            Self::Sanitizer(n) => &n.def,
60            Self::Set(n) => &n.def,
61            Self::Tuple(n) => &n.def,
62            Self::Validator(n) => &n.def,
63        }
64    }
65}
66
67impl MacroNode for SchemaNode {
68    fn as_any(&self) -> &dyn Any {
69        match self {
70            Self::Canister(n) => n.as_any(),
71            Self::DataStore(n) => n.as_any(),
72            Self::Entity(n) => n.as_any(),
73            Self::Enum(n) => n.as_any(),
74            Self::IndexStore(n) => n.as_any(),
75            Self::List(n) => n.as_any(),
76            Self::Map(n) => n.as_any(),
77            Self::Newtype(n) => n.as_any(),
78            Self::Record(n) => n.as_any(),
79            Self::Sanitizer(n) => n.as_any(),
80            Self::Set(n) => n.as_any(),
81            Self::Tuple(n) => n.as_any(),
82            Self::Validator(n) => n.as_any(),
83        }
84    }
85}
86
87impl ValidateNode for SchemaNode {}
88
89impl VisitableNode for SchemaNode {
90    fn drive<V: Visitor>(&self, v: &mut V) {
91        match self {
92            Self::Canister(n) => n.accept(v),
93            Self::DataStore(n) => n.accept(v),
94            Self::Entity(n) => n.accept(v),
95            Self::Enum(n) => n.accept(v),
96            Self::IndexStore(n) => n.accept(v),
97            Self::List(n) => n.accept(v),
98            Self::Map(n) => n.accept(v),
99            Self::Newtype(n) => n.accept(v),
100            Self::Record(n) => n.accept(v),
101            Self::Sanitizer(n) => n.accept(v),
102            Self::Set(n) => n.accept(v),
103            Self::Tuple(n) => n.accept(v),
104            Self::Validator(n) => n.accept(v),
105        }
106    }
107}
108
109///
110/// Schema
111///
112
113#[derive(Clone, Debug, Serialize)]
114pub struct Schema {
115    pub nodes: BTreeMap<String, SchemaNode>,
116    pub hash: &'static str,
117    pub timestamp: u64,
118}
119
120impl Schema {
121    #[must_use]
122    pub fn new() -> Self {
123        Self {
124            nodes: BTreeMap::new(),
125            hash: "",
126            timestamp: now_secs(),
127        }
128    }
129
130    // insert_node
131    pub fn insert_node(&mut self, node: SchemaNode) {
132        self.nodes.insert(node.def().path(), node);
133    }
134
135    // get_node
136    #[must_use]
137    pub fn get_node<'a>(&'a self, path: &str) -> Option<&'a SchemaNode> {
138        self.nodes.get(path)
139    }
140
141    // try_get_node
142    pub fn try_get_node<'a>(&'a self, path: &str) -> Result<&'a SchemaNode, Error> {
143        let node = self
144            .get_node(path)
145            .ok_or_else(|| NodeError::PathNotFound(path.to_string()))?;
146
147        Ok(node)
148    }
149
150    // cast_node
151    pub fn cast_node<'a, T: 'static>(&'a self, path: &str) -> Result<&'a T, Error> {
152        let node = self.try_get_node(path)?;
153
154        node.as_any()
155            .downcast_ref::<T>()
156            .ok_or_else(|| NodeError::IncorrectNodeType(path.to_string()).into())
157    }
158
159    // check_node_as
160    pub fn check_node_as<T: 'static>(&self, path: &str) -> Result<(), Error> {
161        self.cast_node::<T>(path).map(|_| ())
162    }
163
164    // get_nodes
165    pub fn get_nodes<T: 'static>(&self) -> impl Iterator<Item = (&str, &T)> {
166        self.nodes
167            .iter()
168            .filter_map(|(key, node)| node.as_any().downcast_ref::<T>().map(|n| (key.as_str(), n)))
169    }
170
171    // get_node_values
172    pub fn get_node_values<T: 'static>(&'_ self) -> impl Iterator<Item = &'_ T> + '_ {
173        self.nodes
174            .values()
175            .filter_map(|node| node.as_any().downcast_ref::<T>())
176    }
177
178    // filter_nodes
179    // Generic method to filter key, and nodes of any type with a predicate
180    pub fn filter_nodes<'a, T: 'static>(
181        &'a self,
182        predicate: impl Fn(&T) -> bool + 'a,
183    ) -> impl Iterator<Item = (&'a str, &'a T)> + 'a {
184        self.nodes.iter().filter_map(move |(key, node)| {
185            node.as_any()
186                .downcast_ref::<T>()
187                .filter(|target| predicate(target))
188                .map(|target| (key.as_str(), target))
189        })
190    }
191}
192
193impl Default for Schema {
194    fn default() -> Self {
195        Self::new()
196    }
197}
198
199impl ValidateNode for Schema {}
200
201impl VisitableNode for Schema {
202    fn drive<V: Visitor>(&self, v: &mut V) {
203        for node in self.nodes.values() {
204            node.accept(v);
205        }
206    }
207}