Skip to main content

icydb_core/traits/
mod.rs

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