Skip to main content

icydb_core/traits/
mod.rs

1#[macro_use]
2mod macros;
3mod atomic;
4mod view;
5mod visitor;
6
7use crate::{prelude::*, types::Id, value::ValueEnum, visitor::VisitorContext};
8
9pub use atomic::*;
10pub use view::*;
11pub use visitor::*;
12
13// -----------------------------------------------------------------------------
14// Standard re-exports for `traits::X` ergonomics
15// -----------------------------------------------------------------------------
16
17pub use canic_cdk::structures::storable::Storable;
18pub use num_traits::{FromPrimitive as NumFromPrimitive, NumCast, ToPrimitive as NumToPrimitive};
19pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
20pub use std::{
21    cmp::{Eq, Ordering, PartialEq},
22    convert::From,
23    default::Default,
24    fmt::Debug,
25    hash::Hash,
26    ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
27};
28
29// ============================================================================
30// FOUNDATIONAL KINDS
31// ============================================================================
32//
33// These traits define *where* something lives in the system,
34// not what data it contains.
35//
36
37///
38/// Path
39/// Fully-qualified schema path.
40///
41
42pub trait Path {
43    const PATH: &'static str;
44}
45
46///
47/// Kind
48/// Marker for all schema/runtime nodes.
49///
50
51pub trait Kind: Path + 'static {}
52impl<T> Kind for T where T: Path + 'static {}
53
54///
55/// CanisterKind
56/// Marker for canister namespaces
57///
58
59pub trait CanisterKind: Kind {
60    /// Stable memory slot used for commit marker storage.
61    const COMMIT_MEMORY_ID: u8;
62}
63
64///
65/// StoreKind
66/// Marker for data stores bound to a canister
67///
68
69pub trait StoreKind: Kind {
70    type Canister: CanisterKind;
71}
72
73// ============================================================================
74// ENTITY IDENTITY & SCHEMA
75// ============================================================================
76//
77// These traits describe *what an entity is*, not how it is stored
78// or manipulated at runtime.
79//
80
81///
82/// EntityKey
83///
84/// Associates an entity with the primitive type used as its primary key.
85///
86/// ## Semantics
87/// - Implemented for entity types
88/// - `Self::Key` is the *storage representation* of the primary key
89/// - Keys are plain values (Ulid, u64, Principal, …)
90/// - Typed identity is provided by `Id<Self>`, not by the key itself
91/// - Keys are public identifiers and are never authority-bearing capabilities
92///
93
94pub trait EntityKey {
95    type Key: Copy + Debug + Eq + Ord + FieldValue + EntityKeyBytes + 'static;
96}
97
98///
99/// EntityKeyBytes
100///
101
102pub trait EntityKeyBytes {
103    /// Exact number of bytes produced.
104    const BYTE_LEN: usize;
105
106    /// Write bytes into the provided buffer.
107    fn write_bytes(&self, out: &mut [u8]);
108}
109
110macro_rules! impl_entity_key_bytes_numeric {
111    ($($ty:ty),* $(,)?) => {
112        $(
113            impl EntityKeyBytes for $ty {
114                const BYTE_LEN: usize = ::core::mem::size_of::<Self>();
115
116                fn write_bytes(&self, out: &mut [u8]) {
117                    assert_eq!(out.len(), Self::BYTE_LEN);
118                    out.copy_from_slice(&self.to_be_bytes());
119                }
120            }
121        )*
122    };
123}
124
125impl_entity_key_bytes_numeric!(i8, i16, i32, i64, u8, u16, u32, u64);
126
127impl EntityKeyBytes for () {
128    const BYTE_LEN: usize = 0;
129
130    fn write_bytes(&self, out: &mut [u8]) {
131        assert_eq!(out.len(), Self::BYTE_LEN);
132    }
133}
134
135///
136/// EntityIdentity
137///
138/// Semantic primary-key metadata about an entity.
139///
140/// These constants name identity metadata only. They do not imply trust, ownership,
141/// authorization, or existence.
142///
143
144pub trait EntityIdentity: EntityKey {
145    const ENTITY_NAME: &'static str;
146    const PRIMARY_KEY: &'static str;
147}
148
149///
150/// EntitySchema
151///
152/// Declared schema facts for an entity.
153///
154
155pub trait EntitySchema: EntityIdentity {
156    const MODEL: &'static EntityModel;
157    const FIELDS: &'static [&'static str];
158    const INDEXES: &'static [&'static IndexModel];
159}
160
161// ============================================================================
162// ENTITY RUNTIME COMPOSITION
163// ============================================================================
164//
165// These traits bind schema-defined entities into runtime placement.
166//
167
168///
169/// EntityPlacement
170///
171/// Runtime placement of an entity
172///
173
174pub trait EntityPlacement {
175    type Store: StoreKind;
176    type Canister: CanisterKind;
177}
178
179///
180/// EntityKind
181///
182/// Fully runtime-bound entity.
183///
184/// This is the *maximum* entity contract and should only be
185/// required by code that actually touches storage or execution.
186///
187
188pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {}
189
190// ============================================================================
191// ENTITY VALUES
192// ============================================================================
193//
194// These traits describe *instances* of entities.
195//
196
197///
198/// EntityValue
199///
200/// A concrete entity value that can present a typed identity at boundaries.
201///
202/// Implementors store primitive key material internally.
203/// `id()` constructs a typed `Id<Self>` view on demand.
204/// The returned `Id<Self>` is a public identifier, not proof of authority.
205///
206
207pub trait EntityValue: EntityIdentity + FieldProjection + Sized {
208    fn id(&self) -> Id<Self>;
209}
210
211/// Marker for entities with exactly one logical row.
212pub trait SingletonEntity: EntityValue {}
213
214///
215// ============================================================================
216// TYPE SYSTEM CONTRACTS
217// ============================================================================
218//
219// These traits define behavioral expectations for schema-defined types.
220//
221
222///
223/// TypeKind
224///
225/// Any schema-defined data type.
226///
227/// This is a *strong* contract and should only be required
228/// where full lifecycle semantics are needed.
229///
230
231pub trait TypeKind:
232    Kind
233    + AsView
234    + Clone
235    + Default
236    + Serialize
237    + DeserializeOwned
238    + Sanitize
239    + Validate
240    + Visitable
241    + PartialEq
242{
243}
244
245impl<T> TypeKind for T where
246    T: Kind
247        + AsView
248        + Clone
249        + Default
250        + DeserializeOwned
251        + PartialEq
252        + Serialize
253        + Sanitize
254        + Validate
255        + Visitable
256{
257}
258
259/// ============================================================================
260/// QUERY VALUE BOUNDARIES
261/// ============================================================================
262
263///
264/// Collection
265///
266/// Explicit iteration contract for list/set wrapper types.
267/// Avoids implicit deref-based access to inner collections.
268///
269
270pub trait Collection {
271    type Item;
272
273    /// Iterator over the collection's items, tied to the borrow of `self`.
274    type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
275    where
276        Self: 'a;
277
278    /// Returns an iterator over the collection's items.
279    fn iter(&self) -> Self::Iter<'_>;
280
281    /// Returns the number of items in the collection.
282    fn len(&self) -> usize;
283
284    /// Returns true if the collection contains no items.
285    fn is_empty(&self) -> bool {
286        self.len() == 0
287    }
288}
289
290///
291/// MapCollection
292///
293/// Explicit iteration contract for map wrapper types.
294/// Avoids implicit deref-based access to inner collections.
295///
296
297pub trait MapCollection {
298    type Key;
299    type Value;
300
301    /// Iterator over the map's key/value pairs, tied to the borrow of `self`.
302    type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
303    where
304        Self: 'a;
305
306    /// Returns an iterator over the map's key/value pairs.
307    fn iter(&self) -> Self::Iter<'_>;
308
309    /// Returns the number of entries in the map.
310    fn len(&self) -> usize;
311
312    /// Returns true if the map contains no entries.
313    fn is_empty(&self) -> bool {
314        self.len() == 0
315    }
316}
317
318pub trait EnumValue {
319    fn to_value_enum(&self) -> ValueEnum;
320}
321
322pub trait FieldProjection {
323    /// Resolve one field value by stable field slot index.
324    fn get_value_by_index(&self, index: usize) -> Option<Value>;
325}
326
327///
328/// FieldValueKind
329///
330/// Schema affordance classification for query planning and validation.
331/// Describes whether a field is planner-addressable and predicate-queryable.
332///
333
334#[derive(Clone, Copy, Debug, Eq, PartialEq)]
335pub enum FieldValueKind {
336    /// Planner-addressable atomic value.
337    Atomic,
338
339    /// Structured value with known internal fields that the planner
340    /// does not reason about as an addressable query target.
341    Structured {
342        /// Whether predicates may be expressed against this field.
343        queryable: bool,
344    },
345}
346
347impl FieldValueKind {
348    #[must_use]
349    pub const fn is_queryable(self) -> bool {
350        match self {
351            Self::Atomic => true,
352            Self::Structured { queryable } => queryable,
353        }
354    }
355}
356
357///
358/// FieldValue
359///
360/// Conversion boundary for values used in query predicates.
361///
362/// Represents values that can appear on the *right-hand side* of predicates.
363///
364
365pub trait FieldValue {
366    fn kind() -> FieldValueKind
367    where
368        Self: Sized;
369
370    fn to_value(&self) -> Value;
371
372    #[must_use]
373    fn from_value(value: &Value) -> Option<Self>
374    where
375        Self: Sized;
376}
377
378impl FieldValue for &str {
379    fn kind() -> FieldValueKind {
380        FieldValueKind::Atomic
381    }
382
383    fn to_value(&self) -> Value {
384        Value::Text((*self).to_string())
385    }
386
387    fn from_value(_value: &Value) -> Option<Self> {
388        None
389    }
390}
391
392impl FieldValue for String {
393    fn kind() -> FieldValueKind {
394        FieldValueKind::Atomic
395    }
396
397    fn to_value(&self) -> Value {
398        Value::Text(self.clone())
399    }
400
401    fn from_value(value: &Value) -> Option<Self> {
402        match value {
403            Value::Text(v) => Some(v.clone()),
404            _ => None,
405        }
406    }
407}
408
409impl<T: FieldValue> FieldValue for Option<T> {
410    fn kind() -> FieldValueKind {
411        T::kind()
412    }
413
414    fn to_value(&self) -> Value {
415        match self {
416            Some(v) => v.to_value(),
417            None => Value::Null,
418        }
419    }
420
421    fn from_value(value: &Value) -> Option<Self> {
422        if matches!(value, Value::Null) {
423            return Some(None);
424        }
425
426        T::from_value(value).map(Some)
427    }
428}
429
430impl<T: FieldValue> FieldValue for Box<T> {
431    fn kind() -> FieldValueKind {
432        T::kind()
433    }
434
435    fn to_value(&self) -> Value {
436        (**self).to_value()
437    }
438
439    fn from_value(value: &Value) -> Option<Self> {
440        T::from_value(value).map(Self::new)
441    }
442}
443
444impl<T: FieldValue> FieldValue for Vec<Box<T>> {
445    fn kind() -> FieldValueKind {
446        FieldValueKind::Structured { queryable: true }
447    }
448
449    fn to_value(&self) -> Value {
450        Value::List(self.iter().map(FieldValue::to_value).collect())
451    }
452
453    fn from_value(value: &Value) -> Option<Self> {
454        let Value::List(items) = value else {
455            return None;
456        };
457
458        let mut out = Self::with_capacity(items.len());
459        for item in items {
460            out.push(Box::new(T::from_value(item)?));
461        }
462
463        Some(out)
464    }
465}
466
467// impl_field_value
468#[macro_export]
469macro_rules! impl_field_value {
470    ( $( $type:ty => $variant:ident ),* $(,)? ) => {
471        $(
472            impl FieldValue for $type {
473                fn kind() -> FieldValueKind {
474                    FieldValueKind::Atomic
475                }
476
477                fn to_value(&self) -> Value {
478                    Value::$variant((*self).into())
479                }
480
481                fn from_value(value: &Value) -> Option<Self> {
482                    match value {
483                        Value::$variant(v) => (*v).try_into().ok(),
484                        _ => None,
485                    }
486                }
487            }
488        )*
489    };
490}
491
492impl_field_value!(
493    i8 => Int,
494    i16 => Int,
495    i32 => Int,
496    i64 => Int,
497    u8 => Uint,
498    u16 => Uint,
499    u32 => Uint,
500    u64 => Uint,
501    bool => Bool,
502);
503
504/// ============================================================================
505/// MISC HELPERS
506/// ============================================================================
507
508///
509/// Inner
510///
511/// For newtypes to expose their innermost value.
512///
513
514pub trait Inner<T> {
515    fn inner(&self) -> &T;
516    fn into_inner(self) -> T;
517}
518
519impl<T> Inner<T> for T
520where
521    T: Atomic,
522{
523    fn inner(&self) -> &T {
524        self
525    }
526
527    fn into_inner(self) -> T {
528        self
529    }
530}
531
532/// ============================================================================
533/// SANITIZATION / VALIDATION
534/// ============================================================================
535
536///
537/// Sanitizer
538///
539/// Transforms a value into a sanitized version.
540///
541
542pub trait Sanitizer<T> {
543    fn sanitize(&self, value: &mut T) -> Result<(), String>;
544}
545
546///
547/// Validator
548///
549/// Allows a node to validate values.
550///
551
552pub trait Validator<T: ?Sized> {
553    fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
554}