1use crate::{Error, prelude::*};
2use canic_cdk::utils::time::now_secs;
3use std::{any::Any, collections::BTreeMap};
4
5#[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 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#[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 pub fn insert_node(&mut self, node: SchemaNode) {
132 self.nodes.insert(node.def().path(), node);
133 }
134
135 #[must_use]
137 pub fn get_node<'a>(&'a self, path: &str) -> Option<&'a SchemaNode> {
138 self.nodes.get(path)
139 }
140
141 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 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 pub fn check_node_as<T: 'static>(&self, path: &str) -> Result<(), Error> {
161 self.cast_node::<T>(path).map(|_| ())
162 }
163
164 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 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 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}