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