Skip to main content

icydb_schema/node/
schema.rs

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