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::*, types::Id, 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///
66/// EntityKey
67/// Marker trait for raw entity key material used at storage boundaries.
68///
69
70pub trait EntityKey: Copy + Debug + Eq + Ord + FieldValue + 'static {}
71impl<T> EntityKey for T where T: Copy + Debug + Eq + Ord + FieldValue + 'static {}
72
73///
74/// EntityStorageKey
75/// Raw storage-key facts about an entity.
76///
77
78pub trait EntityStorageKey {
79    type Key: EntityKey;
80}
81
82///
83/// EntityIdentity
84/// Semantic identity facts about an entity.
85///
86
87pub trait EntityIdentity: EntityStorageKey {
88    const ENTITY_NAME: &'static str;
89    const PRIMARY_KEY: &'static str;
90}
91
92///
93/// EntitySchema
94/// Declared schema facts for an entity.
95///
96
97pub trait EntitySchema: EntityIdentity {
98    const MODEL: &'static EntityModel;
99    const FIELDS: &'static [&'static str];
100    const INDEXES: &'static [&'static IndexModel];
101}
102
103// ============================================================================
104// ENTITY RUNTIME COMPOSITION
105// ============================================================================
106//
107// These traits bind schema-defined entities into runtime placement.
108//
109
110///
111/// EntityPlacement
112/// Runtime placement of an entity
113///
114
115pub trait EntityPlacement {
116    type DataStore: DataStoreKind;
117    type Canister: CanisterKind;
118}
119
120///
121/// EntityKind
122/// Fully runtime-bound entity.
123///
124/// This is the *maximum* entity contract and should only be
125/// required by code that actually touches storage or execution.
126///
127
128pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {}
129
130// ============================================================================
131// ENTITY VALUES
132// ============================================================================
133//
134// These traits describe *instances* of entities.
135//
136
137/// A concrete entity value.
138///
139/// This trait is intentionally lighter than `EntityKind`.
140/// It does NOT imply storage placement.
141pub trait EntityValue: EntityIdentity + FieldValues + Sized {
142    fn id(&self) -> Id<Self>;
143}
144
145/// Marker for entities with exactly one logical row.
146pub trait SingletonEntity: EntityValue {}
147
148///
149// ============================================================================
150// TYPE SYSTEM CONTRACTS
151// ============================================================================
152//
153// These traits define behavioral expectations for schema-defined types.
154//
155
156///
157/// TypeKind
158///
159/// Any schema-defined data type.
160///
161/// This is a *strong* contract and should only be required
162/// where full lifecycle semantics are needed.
163///
164
165pub trait TypeKind:
166    Kind
167    + View
168    + Clone
169    + Default
170    + Serialize
171    + DeserializeOwned
172    + Sanitize
173    + Validate
174    + Visitable
175    + PartialEq
176{
177}
178
179impl<T> TypeKind for T where
180    T: Kind
181        + View
182        + Clone
183        + Default
184        + DeserializeOwned
185        + PartialEq
186        + Serialize
187        + Sanitize
188        + Validate
189        + Visitable
190{
191}
192
193/// ============================================================================
194/// QUERY VALUE BOUNDARIES
195/// ============================================================================
196
197///
198/// Collection
199///
200/// Explicit iteration contract for list/set wrapper types.
201/// Avoids implicit deref-based access to inner collections.
202///
203
204pub trait Collection {
205    type Item;
206
207    /// Iterator over the collection's items, tied to the borrow of `self`.
208    type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
209    where
210        Self: 'a;
211
212    /// Returns an iterator over the collection's items.
213    fn iter(&self) -> Self::Iter<'_>;
214
215    /// Returns the number of items in the collection.
216    fn len(&self) -> usize;
217
218    /// Returns true if the collection contains no items.
219    fn is_empty(&self) -> bool {
220        self.len() == 0
221    }
222}
223
224///
225/// MapCollection
226///
227/// Explicit iteration contract for map wrapper types.
228/// Avoids implicit deref-based access to inner collections.
229///
230
231pub trait MapCollection {
232    type Key;
233    type Value;
234
235    /// Iterator over the map's key/value pairs, tied to the borrow of `self`.
236    type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
237    where
238        Self: 'a;
239
240    /// Returns an iterator over the map's key/value pairs.
241    fn iter(&self) -> Self::Iter<'_>;
242
243    /// Returns the number of entries in the map.
244    fn len(&self) -> usize;
245
246    /// Returns true if the map contains no entries.
247    fn is_empty(&self) -> bool {
248        self.len() == 0
249    }
250}
251
252pub trait EnumValue {
253    fn to_value_enum(&self) -> ValueEnum;
254}
255
256pub trait FieldValues {
257    fn get_value(&self, field: &str) -> Option<Value>;
258}
259
260///
261/// FieldValue
262///
263/// Conversion boundary for values used in query predicates.
264///
265/// Represents values that can appear on the *right-hand side* of predicates.
266///
267
268pub trait FieldValue {
269    fn to_value(&self) -> Value {
270        Value::Unsupported
271    }
272
273    #[must_use]
274    fn from_value(_value: &Value) -> Option<Self>
275    where
276        Self: Sized,
277    {
278        None
279    }
280}
281
282impl FieldValue for &str {
283    fn to_value(&self) -> Value {
284        Value::Text((*self).to_string())
285    }
286}
287
288impl FieldValue for String {
289    fn to_value(&self) -> Value {
290        Value::Text(self.clone())
291    }
292}
293
294impl<T: FieldValue> FieldValue for Option<T> {
295    fn to_value(&self) -> Value {
296        match self {
297            Some(v) => v.to_value(),
298            None => Value::None,
299        }
300    }
301}
302
303impl<T: FieldValue> FieldValue for Box<T> {
304    fn to_value(&self) -> Value {
305        (**self).to_value()
306    }
307}
308
309impl<T: FieldValue> FieldValue for Vec<Box<T>> {
310    fn to_value(&self) -> Value {
311        Value::List(self.iter().map(FieldValue::to_value).collect())
312    }
313}
314
315// impl_field_value
316#[macro_export]
317macro_rules! impl_field_value {
318    ( $( $type:ty => $variant:ident ),* $(,)? ) => {
319        $(
320            impl FieldValue for $type {
321                fn to_value(&self) -> Value {
322                    Value::$variant((*self).into())
323                }
324
325                fn from_value(value: &Value) -> Option<Self> {
326                    match value {
327                        Value::$variant(v) => (*v).try_into().ok(),
328                        _ => None,
329                    }
330                }
331            }
332        )*
333    };
334}
335
336impl_field_value!(
337    i8 => Int,
338    i16 => Int,
339    i32 => Int,
340    i64 => Int,
341    u8 => Uint,
342    u16 => Uint,
343    u32 => Uint,
344    u64 => Uint,
345    bool => Bool,
346);
347
348/// ============================================================================
349/// MISC HELPERS
350/// ============================================================================
351
352///
353/// Inner
354///
355/// For newtypes to expose their innermost value.
356///
357pub trait Inner<T> {
358    fn inner(&self) -> &T;
359    fn into_inner(self) -> T;
360}
361
362// impl_inner
363#[macro_export]
364macro_rules! impl_inner {
365    ($($type:ty),*) => {
366        $(
367            impl Inner<$type> for $type {
368                fn inner(&self) -> &$type {
369                    &self
370                }
371                fn into_inner(self) -> $type {
372                    self
373                }
374            }
375        )*
376    };
377}
378
379impl_inner!(
380    bool, f32, f64, i8, i16, i32, i64, i128, String, u8, u16, u32, u64, u128
381);
382
383/// ============================================================================
384/// SANITIZATION / VALIDATION
385/// ============================================================================
386
387///
388/// Sanitizer
389///
390/// Transforms a value into a sanitized version.
391///
392pub trait Sanitizer<T> {
393    fn sanitize(&self, value: &mut T) -> Result<(), String>;
394}
395
396///
397/// Validator
398///
399/// Allows a node to validate values.
400///
401pub trait Validator<T: ?Sized> {
402    fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
403}