Skip to main content

icydb_core/traits/
mod.rs

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