icydb_core/traits/mod.rs
1mod create;
2#[macro_use]
3mod macros;
4mod update;
5mod view;
6mod visitor;
7
8pub use create::*;
9pub use update::*;
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
508// impl_inner
509#[macro_export]
510macro_rules! impl_inner {
511 ($($type:ty),*) => {
512 $(
513 impl Inner<$type> for $type {
514 fn inner(&self) -> &$type {
515 &self
516 }
517 fn into_inner(self) -> $type {
518 self
519 }
520 }
521 )*
522 };
523}
524
525impl_inner!(
526 bool, f32, f64, i8, i16, i32, i64, i128, String, u8, u16, u32, u64, u128
527);
528
529/// ============================================================================
530/// SANITIZATION / VALIDATION
531/// ============================================================================
532
533///
534/// Sanitizer
535///
536/// Transforms a value into a sanitized version.
537///
538pub trait Sanitizer<T> {
539 fn sanitize(&self, value: &mut T) -> Result<(), String>;
540}
541
542///
543/// Validator
544///
545/// Allows a node to validate values.
546///
547pub trait Validator<T: ?Sized> {
548 fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
549}