icydb_core/traits/
mod.rs

1#[macro_use]
2mod macros;
3mod view;
4mod visitor;
5
6pub use view::*;
7pub use visitor::*;
8
9// re-exports of other traits
10// for the standard traits::X pattern
11pub use canic_cdk::structures::storable::Storable;
12pub use num_traits::{FromPrimitive as NumFromPrimitive, NumCast, ToPrimitive as NumToPrimitive};
13pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
14pub use std::{
15    cmp::{Eq, Ordering, PartialEq},
16    convert::{AsRef, From, Into},
17    default::Default,
18    fmt::{Debug, Display},
19    hash::Hash,
20    iter::IntoIterator,
21    ops::{Add, AddAssign, Deref, DerefMut, Mul, MulAssign, Sub, SubAssign},
22    str::FromStr,
23};
24
25use crate::{
26    IndexSpec, Key, Value,
27    db::primitives::{
28        BoolEqualityFilterKind, BoolListFilterKind, FilterKind, Int64RangeFilterKind,
29        IntListFilterKind, Nat64RangeFilterKind, NatListFilterKind, TextFilterKind,
30        TextListFilterKind,
31    },
32};
33
34/// ------------------------
35/// KIND TRAITS
36/// the Schema uses the term "Node" but when they're built it's "Kind"
37/// ------------------------
38
39///
40/// Kind
41///
42
43pub trait Kind: Path + 'static {}
44
45impl<T> Kind for T where T: Path + 'static {}
46
47///
48/// CanisterKind
49///
50
51pub trait CanisterKind: Kind {}
52
53///
54/// EntityKind
55///
56
57pub trait EntityKind: Kind + TypeKind + FieldValues {
58    type PrimaryKey: Copy + Into<Key>;
59    type Store: StoreKind;
60    type Canister: CanisterKind; // Self::Store::Canister shortcut
61
62    const ENTITY_ID: u64;
63    const PRIMARY_KEY: &'static str;
64    const FIELDS: &'static [&'static str];
65    const INDEXES: &'static [&'static IndexSpec];
66
67    fn key(&self) -> Key;
68    fn primary_key(&self) -> Self::PrimaryKey;
69    fn set_primary_key(&mut self, key: Self::PrimaryKey);
70}
71
72///
73/// StoreKind
74///
75
76pub trait StoreKind: Kind {
77    type Canister: CanisterKind;
78}
79
80/// ------------------------
81/// TYPE TRAITS
82/// ------------------------
83
84///
85/// TypeKind
86/// any data type
87///
88
89pub trait TypeKind:
90    Kind
91    + View
92    + Clone
93    + Default
94    + Serialize
95    + DeserializeOwned
96    + Sanitize
97    + Validate
98    + Visitable
99    + PartialEq
100{
101}
102
103impl<T> TypeKind for T where
104    T: Kind
105        + View
106        + Clone
107        + Default
108        + DeserializeOwned
109        + PartialEq
110        + Serialize
111        + Sanitize
112        + Validate
113        + Visitable
114{
115}
116
117/// ------------------------
118/// OTHER TRAITS
119/// ------------------------
120
121///
122/// FieldValues
123///
124
125pub trait FieldValues {
126    fn get_value(&self, field: &str) -> Option<Value>;
127}
128
129///
130/// FieldValue
131///
132
133pub trait FieldValue {
134    fn to_value(&self) -> Value {
135        Value::Unsupported
136    }
137}
138
139impl FieldValue for &str {
140    fn to_value(&self) -> Value {
141        Value::Text((*self).to_string())
142    }
143}
144
145impl FieldValue for String {
146    fn to_value(&self) -> Value {
147        Value::Text(self.clone())
148    }
149}
150
151impl<T: FieldValue + Clone> FieldValue for &T {
152    fn to_value(&self) -> Value {
153        (*self).clone().to_value()
154    }
155}
156
157impl<T: FieldValue> FieldValue for Option<T> {
158    fn to_value(&self) -> Value {
159        match self {
160            Some(v) => v.to_value(),
161            None => Value::None,
162        }
163    }
164}
165
166impl<T: FieldValue> FieldValue for Vec<T> {
167    fn to_value(&self) -> Value {
168        Value::List(self.iter().map(FieldValue::to_value).collect())
169    }
170}
171
172impl<T: FieldValue> FieldValue for Box<T> {
173    fn to_value(&self) -> Value {
174        (**self).to_value()
175    }
176}
177
178// impl_field_value
179#[macro_export]
180macro_rules! impl_field_value {
181    ( $( $type:ty => $variant:ident ),* $(,)? ) => {
182        $(
183            impl FieldValue for $type {
184                fn to_value(&self) -> Value {
185                    Value::$variant((*self).into())
186                }
187            }
188        )*
189    };
190}
191
192impl_field_value!(
193    i8 => Int,
194    i16 => Int,
195    i32 => Int,
196    i64 => Int,
197    u8 => Uint,
198    u16 => Uint,
199    u32 => Uint,
200    u64 => Uint,
201    bool => Bool,
202);
203
204///
205/// Filterable
206///
207
208pub trait Filterable {
209    type Filter: FilterKind;
210    type ListFilter: FilterKind;
211}
212
213macro_rules! impl_filterable {
214    // Case 1: type => scalar_filter, list_filter
215    ( $( $type:ty => $filter:path, $list_filter:path );* $(;)? ) => {
216        $(
217            impl Filterable for $type {
218                type Filter = $filter;
219                type ListFilter = $list_filter;
220            }
221        )*
222    };
223}
224
225impl_filterable! {
226    bool    => BoolEqualityFilterKind, BoolListFilterKind;
227    i8      => Int64RangeFilterKind, IntListFilterKind;
228    i16     => Int64RangeFilterKind, IntListFilterKind;
229    i32     => Int64RangeFilterKind, IntListFilterKind;
230    i64     => Int64RangeFilterKind, IntListFilterKind;
231
232    u8      => Nat64RangeFilterKind, NatListFilterKind;
233    u16     => Nat64RangeFilterKind, NatListFilterKind;
234    u32     => Nat64RangeFilterKind, NatListFilterKind;
235    u64     => Nat64RangeFilterKind, NatListFilterKind;
236
237    String  => TextFilterKind, TextListFilterKind;
238}
239
240///
241/// FromKey
242/// Convert a stored [`Key`] into a concrete type.
243/// Returns `None` if the key cannot represent this type.
244///
245
246pub trait FromKey: Copy {
247    fn try_from_key(key: Key) -> Option<Self>;
248}
249
250#[macro_export]
251macro_rules! impl_from_key_int {
252    ( $( $ty:ty ),* $(,)? ) => {
253        $(
254            impl FromKey for $ty {
255                fn try_from_key(key: Key) -> Option<Self> {
256                    match key {
257                        Key::Int(v) => Self::try_from(v).ok(),
258                        _ => None,
259                    }
260                }
261            }
262        )*
263    };
264}
265
266#[macro_export]
267macro_rules! impl_from_key_uint {
268    ( $( $ty:ty ),* $(,)? ) => {
269        $(
270            impl FromKey for $ty {
271                fn try_from_key(key: Key) -> Option<Self> {
272                    match key {
273                        Key::Uint(v) => Self::try_from(v).ok(),
274                        _ => None,
275                    }
276                }
277            }
278        )*
279    };
280}
281
282impl_from_key_int!(i8, i16, i32, i64);
283impl_from_key_uint!(u8, u16, u32, u64);
284
285///
286/// Inner
287/// for Newtypes to get the innermost value
288///
289/// DO NOT REMOVE - its been added and removed twice already, NumCast
290/// is a pain to use and won't work for half our types
291///
292
293pub trait Inner<T> {
294    fn inner(&self) -> &T;
295    fn into_inner(self) -> T;
296}
297
298// impl_inner
299#[macro_export]
300macro_rules! impl_inner {
301    ($($type:ty),*) => {
302        $(
303            impl Inner<$type> for $type {
304                fn inner(&self) -> &$type {
305                    &self
306                }
307                fn into_inner(self) -> $type {
308                    self
309                }
310            }
311        )*
312    };
313}
314
315impl_inner!(
316    bool, f32, f64, i8, i16, i32, i64, i128, String, u8, u16, u32, u64, u128
317);
318
319///
320/// Path
321///
322/// any node created via a macro has a Path
323/// ie. design::game::rarity::Rarity
324///
325
326pub trait Path {
327    const PATH: &'static str;
328}
329
330///
331/// Sanitizer
332/// transforms a value into a sanitized version
333///
334
335pub trait Sanitizer<T> {
336    /// Apply in-place sanitization.
337    ///
338    /// - `Ok(())` means success (possibly with issues recorded by the caller)
339    /// - `Err(String)` means a fatal sanitization failure
340    fn sanitize(&self, value: &mut T) -> Result<(), String>;
341}
342
343///
344/// Validator
345/// allows a node to validate different types of primitives
346/// ?Sized so we can operate on str
347///
348
349pub trait Validator<T: ?Sized> {
350    /// Returns `Ok(())` if valid, or a human-readable message if invalid.
351    fn validate(&self, value: &T) -> Result<(), String>;
352}