Skip to main content

icydb_core/traits/
mod.rs

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