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