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