Skip to main content

icydb_core/traits/
mod.rs

1//! Module: traits
2//!
3//! Responsibility: core trait surface shared across values, entities, and visitors.
4//! Does not own: executor/runtime policy or public facade DTO behavior.
5//! Boundary: reusable domain contracts consumed throughout `icydb-core`.
6
7#[macro_use]
8mod macros;
9mod atomic;
10mod numeric_value;
11mod visitor;
12
13use crate::{
14    model::field::{FieldKind, FieldStorageDecode},
15    prelude::*,
16    types::{EntityTag, Id},
17    value::ValueEnum,
18    visitor::VisitorContext,
19};
20use std::collections::{BTreeMap, BTreeSet};
21
22pub use atomic::*;
23pub use numeric_value::*;
24pub use visitor::*;
25
26// -----------------------------------------------------------------------------
27// Standard re-exports for `traits::X` ergonomics
28// -----------------------------------------------------------------------------
29
30pub use canic_cdk::structures::storable::Storable;
31pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
32pub use std::{
33    cmp::{Eq, Ordering, PartialEq},
34    convert::From,
35    default::Default,
36    fmt::Debug,
37    hash::Hash,
38    ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
39};
40
41// ============================================================================
42// FOUNDATIONAL KINDS
43// ============================================================================
44//
45// These traits define *where* something lives in the system,
46// not what data it contains.
47//
48
49///
50/// Path
51/// Fully-qualified schema path.
52///
53
54pub trait Path {
55    const PATH: &'static str;
56}
57
58///
59/// Kind
60/// Marker for all schema/runtime nodes.
61///
62
63pub trait Kind: Path + 'static {}
64impl<T> Kind for T where T: Path + 'static {}
65
66///
67/// CanisterKind
68/// Marker for canister namespaces
69///
70
71pub trait CanisterKind: Kind {
72    /// Stable memory slot used for commit marker storage.
73    const COMMIT_MEMORY_ID: u8;
74}
75
76///
77/// StoreKind
78/// Marker for data stores bound to a canister
79///
80
81pub trait StoreKind: Kind {
82    type Canister: CanisterKind;
83}
84
85// ============================================================================
86// ENTITY IDENTITY & SCHEMA
87// ============================================================================
88//
89// These traits describe *what an entity is*, not how it is stored
90// or manipulated at runtime.
91//
92
93///
94/// EntityKey
95///
96/// Associates an entity with the primitive type used as its primary key.
97///
98/// ## Semantics
99/// - Implemented for entity types
100/// - `Self::Key` is the *storage representation* of the primary key
101/// - Keys are plain values (Ulid, u64, Principal, …)
102/// - Typed identity is provided by `Id<Self>`, not by the key itself
103/// - Keys are public identifiers and are never authority-bearing capabilities
104///
105
106pub trait EntityKey {
107    type Key: Copy + Debug + Eq + Ord + FieldValue + EntityKeyBytes + 'static;
108}
109
110///
111/// EntityKeyBytes
112///
113
114pub trait EntityKeyBytes {
115    /// Exact number of bytes produced.
116    const BYTE_LEN: usize;
117
118    /// Write bytes into the provided buffer.
119    fn write_bytes(&self, out: &mut [u8]);
120}
121
122macro_rules! impl_entity_key_bytes_numeric {
123    ($($ty:ty),* $(,)?) => {
124        $(
125            impl EntityKeyBytes for $ty {
126                const BYTE_LEN: usize = ::core::mem::size_of::<Self>();
127
128                fn write_bytes(&self, out: &mut [u8]) {
129                    assert_eq!(out.len(), Self::BYTE_LEN);
130                    out.copy_from_slice(&self.to_be_bytes());
131                }
132            }
133        )*
134    };
135}
136
137impl_entity_key_bytes_numeric!(i8, i16, i32, i64, u8, u16, u32, u64);
138
139impl EntityKeyBytes for () {
140    const BYTE_LEN: usize = 0;
141
142    fn write_bytes(&self, out: &mut [u8]) {
143        assert_eq!(out.len(), Self::BYTE_LEN);
144    }
145}
146
147///
148/// EntitySchema
149///
150/// Declared runtime schema facts for an entity.
151///
152/// `NAME` seeds self-referential model construction for relation metadata.
153/// `MODEL` remains the authoritative runtime authority for field, primary-key,
154/// and index metadata consumed by planning and execution.
155///
156
157pub trait EntitySchema: EntityKey {
158    const NAME: &'static str;
159    const MODEL: &'static EntityModel;
160}
161
162// ============================================================================
163// ENTITY RUNTIME COMPOSITION
164// ============================================================================
165//
166// These traits bind schema-defined entities into runtime placement.
167//
168
169///
170/// EntityPlacement
171///
172/// Runtime placement of an entity
173///
174
175pub trait EntityPlacement {
176    type Store: StoreKind;
177    type Canister: CanisterKind;
178}
179
180///
181/// EntityKind
182///
183/// Fully runtime-bound entity.
184///
185/// This is the *maximum* entity contract and should only be
186/// required by code that actually touches storage or execution.
187///
188
189pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {
190    const ENTITY_TAG: EntityTag;
191}
192
193// ============================================================================
194// ENTITY VALUES
195// ============================================================================
196//
197// These traits describe *instances* of entities.
198//
199
200///
201/// EntityValue
202///
203/// A concrete entity value that can present a typed identity at boundaries.
204///
205/// Implementors store primitive key material internally.
206/// `id()` constructs a typed `Id<Self>` view on demand.
207/// The returned `Id<Self>` is a public identifier, not proof of authority.
208///
209
210pub trait EntityValue: EntityKey + FieldProjection + Sized {
211    fn id(&self) -> Id<Self>;
212}
213
214/// Marker for entities with exactly one logical row.
215pub trait SingletonEntity: EntityValue {}
216
217///
218// ============================================================================
219// TYPE SYSTEM CONTRACTS
220// ============================================================================
221//
222// These traits define behavioral expectations for schema-defined types.
223//
224
225///
226/// TypeKind
227///
228/// Any schema-defined data type.
229///
230/// This is a *strong* contract and should only be required
231/// where full lifecycle semantics are needed.
232///
233
234pub trait TypeKind:
235    Kind + Clone + Default + Serialize + DeserializeOwned + Sanitize + Validate + Visitable + PartialEq
236{
237}
238
239impl<T> TypeKind for T where
240    T: Kind
241        + Clone
242        + Default
243        + DeserializeOwned
244        + PartialEq
245        + Serialize
246        + Sanitize
247        + Validate
248        + Visitable
249{
250}
251
252///
253/// FieldTypeMeta
254///
255/// Static runtime field metadata for one schema-facing value type.
256/// This is the single authority for generated field kind and storage-decode
257/// metadata, so callers do not need per-type inherent constants.
258///
259
260pub trait FieldTypeMeta {
261    /// Semantic field kind used for runtime planning and validation.
262    const KIND: FieldKind;
263
264    /// Persisted decode contract used by row and payload decoding.
265    const STORAGE_DECODE: FieldStorageDecode;
266}
267
268/// ============================================================================
269/// QUERY VALUE BOUNDARIES
270/// ============================================================================
271
272///
273/// Collection
274///
275/// Explicit iteration contract for list/set wrapper types.
276/// Keeps generic collection code on one stable boundary even when concrete
277/// wrapper types opt into direct container ergonomics.
278///
279
280pub trait Collection {
281    type Item;
282
283    /// Iterator over the collection's items, tied to the borrow of `self`.
284    type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
285    where
286        Self: 'a;
287
288    /// Returns an iterator over the collection's items.
289    fn iter(&self) -> Self::Iter<'_>;
290
291    /// Returns the number of items in the collection.
292    fn len(&self) -> usize;
293
294    /// Returns true if the collection contains no items.
295    fn is_empty(&self) -> bool {
296        self.len() == 0
297    }
298}
299
300///
301/// MapCollection
302///
303/// Explicit iteration contract for map wrapper types.
304/// Keeps generic map code on one stable boundary even when concrete wrapper
305/// types opt into direct container ergonomics.
306///
307
308pub trait MapCollection {
309    type Key;
310    type Value;
311
312    /// Iterator over the map's key/value pairs, tied to the borrow of `self`.
313    type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
314    where
315        Self: 'a;
316
317    /// Returns an iterator over the map's key/value pairs.
318    fn iter(&self) -> Self::Iter<'_>;
319
320    /// Returns the number of entries in the map.
321    fn len(&self) -> usize;
322
323    /// Returns true if the map contains no entries.
324    fn is_empty(&self) -> bool {
325        self.len() == 0
326    }
327}
328
329pub trait EnumValue {
330    fn to_value_enum(&self) -> ValueEnum;
331}
332
333pub trait FieldProjection {
334    /// Resolve one field value by stable field slot index.
335    fn get_value_by_index(&self, index: usize) -> Option<Value>;
336}
337
338///
339/// FieldValueKind
340///
341/// Schema affordance classification for query planning and validation.
342/// Describes whether a field is planner-addressable and predicate-queryable.
343///
344
345#[derive(Clone, Copy, Debug, Eq, PartialEq)]
346pub enum FieldValueKind {
347    /// Planner-addressable atomic value.
348    Atomic,
349
350    /// Structured value with known internal fields that the planner
351    /// does not reason about as an addressable query target.
352    Structured {
353        /// Whether predicates may be expressed against this field.
354        queryable: bool,
355    },
356}
357
358impl FieldValueKind {
359    #[must_use]
360    pub const fn is_queryable(self) -> bool {
361        match self {
362            Self::Atomic => true,
363            Self::Structured { queryable } => queryable,
364        }
365    }
366}
367
368///
369/// FieldValue
370///
371/// Conversion boundary for values used in query predicates.
372///
373/// Represents values that can appear on the *right-hand side* of predicates.
374///
375
376pub trait FieldValue {
377    fn kind() -> FieldValueKind
378    where
379        Self: Sized;
380
381    fn to_value(&self) -> Value;
382
383    #[must_use]
384    fn from_value(value: &Value) -> Option<Self>
385    where
386        Self: Sized;
387}
388
389///
390/// field_value_collection_to_value
391///
392/// Shared collection-to-`Value::List` lowering for generated wrapper types.
393/// This keeps list and set `FieldValue` impls from re-emitting the same item
394/// iteration body for every generated schema type.
395///
396
397pub fn field_value_collection_to_value<C>(collection: &C) -> Value
398where
399    C: Collection,
400    C::Item: FieldValue,
401{
402    Value::List(collection.iter().map(FieldValue::to_value).collect())
403}
404
405///
406/// field_value_vec_from_value
407///
408/// Shared `Value::List` decode for generated list wrapper types.
409/// This preserves typed `FieldValue` decoding while avoiding one repeated loop
410/// body per generated list schema type.
411///
412
413#[must_use]
414pub fn field_value_vec_from_value<T>(value: &Value) -> Option<Vec<T>>
415where
416    T: FieldValue,
417{
418    let Value::List(values) = value else {
419        return None;
420    };
421
422    let mut out = Vec::with_capacity(values.len());
423    for value in values {
424        out.push(T::from_value(value)?);
425    }
426
427    Some(out)
428}
429
430///
431/// field_value_btree_set_from_value
432///
433/// Shared `Value::List` decode for generated set wrapper types.
434/// This preserves duplicate rejection while avoiding one repeated loop body
435/// per generated set schema type.
436///
437
438#[must_use]
439pub fn field_value_btree_set_from_value<T>(value: &Value) -> Option<BTreeSet<T>>
440where
441    T: FieldValue + Ord,
442{
443    let Value::List(values) = value else {
444        return None;
445    };
446
447    let mut out = BTreeSet::new();
448    for value in values {
449        let item = T::from_value(value)?;
450        if !out.insert(item) {
451            return None;
452        }
453    }
454
455    Some(out)
456}
457
458///
459/// field_value_map_collection_to_value
460///
461/// Shared map-to-`Value::Map` lowering for generated map wrapper types.
462/// This keeps canonicalization and duplicate-key checks in one runtime helper
463/// instead of re-emitting the same map conversion body per generated schema
464/// type.
465///
466
467pub fn field_value_map_collection_to_value<M>(map: &M, path: &'static str) -> Value
468where
469    M: MapCollection,
470    M::Key: FieldValue,
471    M::Value: FieldValue,
472{
473    let mut entries: Vec<(Value, Value)> = map
474        .iter()
475        .map(|(key, value)| (FieldValue::to_value(key), FieldValue::to_value(value)))
476        .collect();
477
478    if let Err(err) = Value::validate_map_entries(entries.as_slice()) {
479        debug_assert!(false, "invalid map field value for {path}: {err}");
480        return Value::Map(entries);
481    }
482
483    Value::sort_map_entries_in_place(entries.as_mut_slice());
484
485    for i in 1..entries.len() {
486        let (left_key, _) = &entries[i - 1];
487        let (right_key, _) = &entries[i];
488        if Value::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
489            debug_assert!(
490                false,
491                "duplicate map key in {path} after FieldValue::to_value canonicalization",
492            );
493            break;
494        }
495    }
496
497    Value::Map(entries)
498}
499
500///
501/// field_value_btree_map_from_value
502///
503/// Shared `Value::Map` decode for generated map wrapper types.
504/// This keeps canonical-entry normalization in one runtime helper instead of
505/// re-emitting the same decode body per generated schema type.
506///
507
508#[must_use]
509pub fn field_value_btree_map_from_value<K, V>(value: &Value) -> Option<BTreeMap<K, V>>
510where
511    K: FieldValue + Ord,
512    V: FieldValue,
513{
514    let Value::Map(entries) = value else {
515        return None;
516    };
517
518    let normalized = Value::normalize_map_entries(entries.clone()).ok()?;
519    if normalized.as_slice() != entries.as_slice() {
520        return None;
521    }
522
523    let mut map = BTreeMap::new();
524    for (entry_key, entry_value) in normalized {
525        let key = K::from_value(&entry_key)?;
526        let value = V::from_value(&entry_value)?;
527        map.insert(key, value);
528    }
529
530    Some(map)
531}
532
533///
534/// field_value_from_vec_into
535///
536/// Shared `Vec<I> -> Vec<T>` conversion for generated wrapper `From<Vec<I>>`
537/// impls. This keeps list wrappers from re-emitting the same `into_iter` /
538/// `map(Into::into)` collection body for every generated schema type.
539///
540
541#[must_use]
542pub fn field_value_from_vec_into<T, I>(entries: Vec<I>) -> Vec<T>
543where
544    I: Into<T>,
545{
546    entries.into_iter().map(Into::into).collect()
547}
548
549///
550/// field_value_from_vec_into_btree_set
551///
552/// Shared `Vec<I> -> BTreeSet<T>` conversion for generated set wrapper
553/// `From<Vec<I>>` impls. This keeps set wrappers from re-emitting the same
554/// collection conversion body for every generated schema type.
555///
556
557#[must_use]
558pub fn field_value_from_vec_into_btree_set<T, I>(entries: Vec<I>) -> BTreeSet<T>
559where
560    I: Into<T>,
561    T: Ord,
562{
563    entries.into_iter().map(Into::into).collect()
564}
565
566///
567/// field_value_from_vec_into_btree_map
568///
569/// Shared `Vec<(IK, IV)> -> BTreeMap<K, V>` conversion for generated map
570/// wrapper `From<Vec<(IK, IV)>>` impls. This keeps map wrappers from
571/// re-emitting the same pair-conversion body for every generated schema type.
572///
573
574#[must_use]
575pub fn field_value_from_vec_into_btree_map<K, V, IK, IV>(entries: Vec<(IK, IV)>) -> BTreeMap<K, V>
576where
577    IK: Into<K>,
578    IV: Into<V>,
579    K: Ord,
580{
581    entries
582        .into_iter()
583        .map(|(key, value)| (key.into(), value.into()))
584        .collect()
585}
586
587///
588/// field_value_into
589///
590/// Shared `Into<T>` lowering for generated newtype `From<U>` impls.
591/// This keeps newtype wrappers from re-emitting the same single-field
592/// conversion body for every generated schema type.
593///
594
595#[must_use]
596pub fn field_value_into<T, U>(value: U) -> T
597where
598    U: Into<T>,
599{
600    value.into()
601}
602
603impl FieldValue for &str {
604    fn kind() -> FieldValueKind {
605        FieldValueKind::Atomic
606    }
607
608    fn to_value(&self) -> Value {
609        Value::Text((*self).to_string())
610    }
611
612    fn from_value(_value: &Value) -> Option<Self> {
613        None
614    }
615}
616
617impl FieldValue for String {
618    fn kind() -> FieldValueKind {
619        FieldValueKind::Atomic
620    }
621
622    fn to_value(&self) -> Value {
623        Value::Text(self.clone())
624    }
625
626    fn from_value(value: &Value) -> Option<Self> {
627        match value {
628            Value::Text(v) => Some(v.clone()),
629            _ => None,
630        }
631    }
632}
633
634impl<T: FieldValue> FieldValue for Option<T> {
635    fn kind() -> FieldValueKind {
636        T::kind()
637    }
638
639    fn to_value(&self) -> Value {
640        match self {
641            Some(v) => v.to_value(),
642            None => Value::Null,
643        }
644    }
645
646    fn from_value(value: &Value) -> Option<Self> {
647        if matches!(value, Value::Null) {
648            return Some(None);
649        }
650
651        T::from_value(value).map(Some)
652    }
653}
654
655impl<T: FieldValue> FieldValue for Box<T> {
656    fn kind() -> FieldValueKind {
657        T::kind()
658    }
659
660    fn to_value(&self) -> Value {
661        (**self).to_value()
662    }
663
664    fn from_value(value: &Value) -> Option<Self> {
665        T::from_value(value).map(Self::new)
666    }
667}
668
669impl<T: FieldValue> FieldValue for Vec<Box<T>> {
670    fn kind() -> FieldValueKind {
671        FieldValueKind::Structured { queryable: true }
672    }
673
674    fn to_value(&self) -> Value {
675        Value::List(self.iter().map(FieldValue::to_value).collect())
676    }
677
678    fn from_value(value: &Value) -> Option<Self> {
679        let Value::List(items) = value else {
680            return None;
681        };
682
683        let mut out = Self::with_capacity(items.len());
684        for item in items {
685            out.push(Box::new(T::from_value(item)?));
686        }
687
688        Some(out)
689    }
690}
691
692// impl_field_value
693#[macro_export]
694macro_rules! impl_field_value {
695    ( $( $type:ty => $variant:ident ),* $(,)? ) => {
696        $(
697            impl FieldValue for $type {
698                fn kind() -> FieldValueKind {
699                    FieldValueKind::Atomic
700                }
701
702                fn to_value(&self) -> Value {
703                    Value::$variant((*self).into())
704                }
705
706                fn from_value(value: &Value) -> Option<Self> {
707                    match value {
708                        Value::$variant(v) => (*v).try_into().ok(),
709                        _ => None,
710                    }
711                }
712            }
713        )*
714    };
715}
716
717impl_field_value!(
718    i8 => Int,
719    i16 => Int,
720    i32 => Int,
721    i64 => Int,
722    u8 => Uint,
723    u16 => Uint,
724    u32 => Uint,
725    u64 => Uint,
726    bool => Bool,
727);
728
729/// ============================================================================
730/// MISC HELPERS
731/// ============================================================================
732
733///
734/// Inner
735///
736/// For newtypes to expose their innermost value.
737///
738
739pub trait Inner<T> {
740    fn inner(&self) -> &T;
741    fn into_inner(self) -> T;
742}
743
744impl<T> Inner<T> for T
745where
746    T: Atomic,
747{
748    fn inner(&self) -> &T {
749        self
750    }
751
752    fn into_inner(self) -> T {
753        self
754    }
755}
756
757///
758/// Repr
759///
760/// Internal representation boundary for scalar wrapper types.
761///
762
763pub trait Repr {
764    type Inner;
765
766    fn repr(&self) -> Self::Inner;
767    fn from_repr(inner: Self::Inner) -> Self;
768}
769
770/// ============================================================================
771/// SANITIZATION / VALIDATION
772/// ============================================================================
773
774///
775/// Sanitizer
776///
777/// Transforms a value into a sanitized version.
778///
779
780pub trait Sanitizer<T> {
781    fn sanitize(&self, value: &mut T) -> Result<(), String>;
782}
783
784///
785/// Validator
786///
787/// Allows a node to validate values.
788///
789
790pub trait Validator<T: ?Sized> {
791    fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
792}