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