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