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}