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