Skip to main content

icydb_core/traits/
mod.rs

1#[macro_use]
2mod macros;
3mod view;
4mod visitor;
5
6pub use view::*;
7pub use visitor::*;
8
9// -----------------------------------------------------------------------------
10// Standard re-exports for `traits::X` ergonomics
11// -----------------------------------------------------------------------------
12
13pub use canic_cdk::structures::storable::Storable;
14pub use num_traits::{FromPrimitive as NumFromPrimitive, NumCast, ToPrimitive as NumToPrimitive};
15pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
16pub use std::{
17    cmp::{Eq, Ordering, PartialEq},
18    convert::From,
19    default::Default,
20    fmt::Debug,
21    hash::Hash,
22    ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
23};
24
25use crate::{prelude::*, value::ValueEnum, visitor::VisitorContext};
26
27// ============================================================================
28// FOUNDATIONAL KINDS
29// ============================================================================
30//
31// These traits define *where* something lives in the system,
32// not what data it contains.
33//
34
35/// Fully-qualified schema path.
36pub trait Path {
37    const PATH: &'static str;
38}
39
40/// Marker for all schema/runtime nodes.
41pub trait Kind: Path + 'static {}
42impl<T> Kind for T where T: Path + 'static {}
43
44/// Marker for canister namespaces.
45pub trait CanisterKind: Kind {}
46
47/// Marker for data stores bound to a canister.
48pub trait DataStoreKind: Kind {
49    type Canister: CanisterKind;
50}
51
52/// Marker for index stores bound to a canister.
53pub trait IndexStoreKind: Kind {
54    type Canister: CanisterKind;
55}
56
57// ============================================================================
58// ENTITY IDENTITY & SCHEMA
59// ============================================================================
60//
61// These traits describe *what an entity is*, not how it is stored
62// or manipulated at runtime.
63//
64
65/// Marker trait for entity identity types.
66///
67/// Identity types:
68/// - Are cheap to copy
69/// - Are totally ordered
70/// - Can be converted to/from query Values
71///
72/// They are NOT required to be persistable.
73pub trait EntityKey: Copy + Debug + Eq + Ord + FieldValue + 'static {}
74impl<T> EntityKey for T where T: Copy + Debug + Eq + Ord + FieldValue + 'static {}
75
76///
77/// EntityIdentity
78/// Identity-only facts about an entity.
79///
80
81pub trait EntityIdentity {
82    type Id: EntityKey;
83
84    const ENTITY_NAME: &'static str;
85    const PRIMARY_KEY: &'static str;
86}
87
88///
89/// EntitySchema
90/// Declared schema facts for an entity.
91///
92
93pub trait EntitySchema: EntityIdentity {
94    const MODEL: &'static EntityModel;
95    const FIELDS: &'static [&'static str];
96    const INDEXES: &'static [&'static IndexModel];
97}
98
99// ============================================================================
100// ENTITY RUNTIME COMPOSITION
101// ============================================================================
102//
103// These traits bind schema-defined entities into runtime placement.
104//
105
106/// Runtime placement of an entity.
107pub trait EntityPlacement {
108    type DataStore: DataStoreKind;
109    type Canister: CanisterKind;
110}
111
112/// Fully runtime-bound entity.
113///
114/// This is the *maximum* entity contract and should only be
115/// required by code that actually touches storage or execution.
116pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {}
117
118// ============================================================================
119// ENTITY VALUES
120// ============================================================================
121//
122// These traits describe *instances* of entities.
123//
124
125/// A concrete entity value.
126///
127/// This trait is intentionally lighter than `EntityKind`.
128/// It does NOT imply storage placement.
129pub trait EntityValue: EntityIdentity + FieldValues {
130    fn id(&self) -> Self::Id;
131    fn set_id(&mut self, id: Self::Id);
132}
133
134/// Marker for entities with exactly one logical row.
135pub trait SingletonEntity: EntityValue {}
136
137///
138// ============================================================================
139// TYPE SYSTEM CONTRACTS
140// ============================================================================
141//
142// These traits define behavioral expectations for schema-defined types.
143//
144
145/// Any schema-defined data type.
146///
147/// This is a *strong* contract and should only be required
148/// where full lifecycle semantics are needed.
149pub trait TypeKind:
150    Kind
151    + View
152    + Clone
153    + Default
154    + Serialize
155    + DeserializeOwned
156    + Sanitize
157    + Validate
158    + Visitable
159    + PartialEq
160{
161}
162
163impl<T> TypeKind for T where
164    T: Kind
165        + View
166        + Clone
167        + Default
168        + DeserializeOwned
169        + PartialEq
170        + Serialize
171        + Sanitize
172        + Validate
173        + Visitable
174{
175}
176
177/// ============================================================================
178/// QUERY VALUE BOUNDARIES
179/// ============================================================================
180
181/// Explicit iteration contract for list/set wrapper types.
182/// Avoids implicit deref-based access to inner collections.
183pub trait Collection {
184    type Item;
185
186    /// Iterator over the collection's items, tied to the borrow of `self`.
187    type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
188    where
189        Self: 'a;
190
191    /// Returns an iterator over the collection's items.
192    fn iter(&self) -> Self::Iter<'_>;
193
194    /// Returns the number of items in the collection.
195    fn len(&self) -> usize;
196
197    /// Returns true if the collection contains no items.
198    fn is_empty(&self) -> bool {
199        self.len() == 0
200    }
201}
202
203/// Explicit iteration contract for map wrapper types.
204/// Avoids implicit deref-based access to inner collections.
205pub trait MapCollection {
206    type Key;
207    type Value;
208
209    /// Iterator over the map's key/value pairs, tied to the borrow of `self`.
210    type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
211    where
212        Self: 'a;
213
214    /// Returns an iterator over the map's key/value pairs.
215    fn iter(&self) -> Self::Iter<'_>;
216
217    /// Returns the number of entries in the map.
218    fn len(&self) -> usize;
219
220    /// Returns true if the map contains no entries.
221    fn is_empty(&self) -> bool {
222        self.len() == 0
223    }
224}
225
226pub trait EnumValue {
227    fn to_value_enum(&self) -> ValueEnum;
228}
229
230pub trait FieldValues {
231    fn get_value(&self, field: &str) -> Option<Value>;
232}
233
234///
235/// FieldValue
236///
237/// Conversion boundary for values used in query predicates.
238///
239/// Represents values that can appear on the *right-hand side* of predicates.
240///
241
242pub trait FieldValue {
243    fn to_value(&self) -> Value {
244        Value::Unsupported
245    }
246
247    #[must_use]
248    fn from_value(_value: &Value) -> Option<Self>
249    where
250        Self: Sized,
251    {
252        None
253    }
254}
255
256impl FieldValue for &str {
257    fn to_value(&self) -> Value {
258        Value::Text((*self).to_string())
259    }
260}
261
262impl FieldValue for String {
263    fn to_value(&self) -> Value {
264        Value::Text(self.clone())
265    }
266}
267
268impl<T: FieldValue> FieldValue for Option<T> {
269    fn to_value(&self) -> Value {
270        match self {
271            Some(v) => v.to_value(),
272            None => Value::None,
273        }
274    }
275}
276
277impl<T: FieldValue> FieldValue for Box<T> {
278    fn to_value(&self) -> Value {
279        (**self).to_value()
280    }
281}
282
283impl<T: FieldValue> FieldValue for Vec<Box<T>> {
284    fn to_value(&self) -> Value {
285        Value::List(self.iter().map(FieldValue::to_value).collect())
286    }
287}
288
289// impl_field_value
290#[macro_export]
291macro_rules! impl_field_value {
292    ( $( $type:ty => $variant:ident ),* $(,)? ) => {
293        $(
294            impl FieldValue for $type {
295                fn to_value(&self) -> Value {
296                    Value::$variant((*self).into())
297                }
298
299                fn from_value(value: &Value) -> Option<Self> {
300                    match value {
301                        Value::$variant(v) => (*v).try_into().ok(),
302                        _ => None,
303                    }
304                }
305            }
306        )*
307    };
308}
309
310impl_field_value!(
311    i8 => Int,
312    i16 => Int,
313    i32 => Int,
314    i64 => Int,
315    u8 => Uint,
316    u16 => Uint,
317    u32 => Uint,
318    u64 => Uint,
319    bool => Bool,
320);
321
322/// ============================================================================
323/// MISC HELPERS
324/// ============================================================================
325
326///
327/// Inner
328///
329/// For newtypes to expose their innermost value.
330///
331pub trait Inner<T> {
332    fn inner(&self) -> &T;
333    fn into_inner(self) -> T;
334}
335
336// impl_inner
337#[macro_export]
338macro_rules! impl_inner {
339    ($($type:ty),*) => {
340        $(
341            impl Inner<$type> for $type {
342                fn inner(&self) -> &$type {
343                    &self
344                }
345                fn into_inner(self) -> $type {
346                    self
347                }
348            }
349        )*
350    };
351}
352
353impl_inner!(
354    bool, f32, f64, i8, i16, i32, i64, i128, String, u8, u16, u32, u64, u128
355);
356
357/// ============================================================================
358/// SANITIZATION / VALIDATION
359/// ============================================================================
360
361///
362/// Sanitizer
363///
364/// Transforms a value into a sanitized version.
365///
366pub trait Sanitizer<T> {
367    fn sanitize(&self, value: &mut T) -> Result<(), String>;
368}
369
370///
371/// Validator
372///
373/// Allows a node to validate values.
374///
375pub trait Validator<T: ?Sized> {
376    fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
377}