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/// Marker trait for raw entity key material used at storage boundaries.
73///
74
75pub trait EntityKey: Copy + Debug + Eq + Ord + FieldValue + 'static {}
76impl<T> EntityKey for T where T: Copy + Debug + Eq + Ord + FieldValue + 'static {}
77
78///
79/// EntityStorageKey
80///
81/// Raw storage-key facts about an entity.
82///
83
84pub trait EntityStorageKey {
85 type Key: EntityKey;
86}
87
88///
89/// EntityIdentity
90///
91/// Semantic identity facts about an entity.
92/// `IDENTITY_NAMESPACE` is the stable namespace used for one-way
93/// external identity projection and SHOULD remain unchanged across
94/// schema/type renames when external identity continuity is required.
95///
96
97pub trait EntityIdentity: EntityStorageKey {
98 const ENTITY_NAME: &'static str;
99 const PRIMARY_KEY: &'static str;
100 const IDENTITY_NAMESPACE: &'static str;
101}
102
103///
104/// EntitySchema
105///
106/// Declared schema facts for an entity.
107///
108
109pub trait EntitySchema: EntityIdentity {
110 const MODEL: &'static EntityModel;
111 const FIELDS: &'static [&'static str];
112 const INDEXES: &'static [&'static IndexModel];
113}
114
115// ============================================================================
116// ENTITY RUNTIME COMPOSITION
117// ============================================================================
118//
119// These traits bind schema-defined entities into runtime placement.
120//
121
122///
123/// EntityPlacement
124///
125/// Runtime placement of an entity
126///
127
128pub trait EntityPlacement {
129 type DataStore: DataStoreKind;
130 type Canister: CanisterKind;
131}
132
133///
134/// EntityKind
135///
136/// Fully runtime-bound entity.
137///
138/// This is the *maximum* entity contract and should only be
139/// required by code that actually touches storage or execution.
140///
141
142pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {}
143
144// ============================================================================
145// ENTITY VALUES
146// ============================================================================
147//
148// These traits describe *instances* of entities.
149//
150
151///
152/// EntityValue
153///
154/// A concrete entity value.
155///
156/// This trait is intentionally lighter than `EntityKind`.
157/// It does NOT imply storage placement.
158///
159
160pub trait EntityValue: EntityIdentity + FieldValues + Sized {
161 fn id(&self) -> Id<Self>;
162}
163
164/// Marker for entities with exactly one logical row.
165pub trait SingletonEntity: EntityValue {}
166
167///
168// ============================================================================
169// TYPE SYSTEM CONTRACTS
170// ============================================================================
171//
172// These traits define behavioral expectations for schema-defined types.
173//
174
175///
176/// TypeKind
177///
178/// Any schema-defined data type.
179///
180/// This is a *strong* contract and should only be required
181/// where full lifecycle semantics are needed.
182///
183
184pub trait TypeKind:
185 Kind
186 + View
187 + Clone
188 + Default
189 + Serialize
190 + DeserializeOwned
191 + Sanitize
192 + Validate
193 + Visitable
194 + PartialEq
195{
196}
197
198impl<T> TypeKind for T where
199 T: Kind
200 + View
201 + Clone
202 + Default
203 + DeserializeOwned
204 + PartialEq
205 + Serialize
206 + Sanitize
207 + Validate
208 + Visitable
209{
210}
211
212/// ============================================================================
213/// QUERY VALUE BOUNDARIES
214/// ============================================================================
215
216///
217/// Collection
218///
219/// Explicit iteration contract for list/set wrapper types.
220/// Avoids implicit deref-based access to inner collections.
221///
222
223///
224/// DeterministicCollection
225///
226/// Marker for collection types with stable iteration, serialization, and
227/// equality semantics across process boundaries.
228///
229/// This trait intentionally has no methods; it exists to encode a hard
230/// determinism invariant at collection boundaries.
231///
232
233pub trait DeterministicCollection {}
234
235pub trait Collection {
236 type Item;
237
238 /// Iterator over the collection's items, tied to the borrow of `self`.
239 type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
240 where
241 Self: 'a;
242
243 /// Returns an iterator over the collection's items.
244 fn iter(&self) -> Self::Iter<'_>;
245
246 /// Returns the number of items in the collection.
247 fn len(&self) -> usize;
248
249 /// Returns true if the collection contains no items.
250 fn is_empty(&self) -> bool {
251 self.len() == 0
252 }
253}
254
255///
256/// MapCollection
257///
258/// Explicit iteration contract for map wrapper types.
259/// Avoids implicit deref-based access to inner collections.
260///
261
262pub trait MapCollection {
263 type Key;
264 type Value;
265
266 /// Iterator over the map's key/value pairs, tied to the borrow of `self`.
267 type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
268 where
269 Self: 'a;
270
271 /// Returns an iterator over the map's key/value pairs.
272 fn iter(&self) -> Self::Iter<'_>;
273
274 /// Returns the number of entries in the map.
275 fn len(&self) -> usize;
276
277 /// Returns true if the map contains no entries.
278 fn is_empty(&self) -> bool {
279 self.len() == 0
280 }
281}
282
283pub trait EnumValue {
284 fn to_value_enum(&self) -> ValueEnum;
285}
286
287pub trait FieldValues {
288 fn get_value(&self, field: &str) -> Option<Value>;
289}
290
291///
292/// FieldValue
293///
294/// Conversion boundary for values used in query predicates.
295///
296/// Represents values that can appear on the *right-hand side* of predicates.
297///
298
299pub trait FieldValue {
300 fn to_value(&self) -> Value {
301 Value::Unsupported
302 }
303
304 #[must_use]
305 fn from_value(_value: &Value) -> Option<Self>
306 where
307 Self: Sized,
308 {
309 None
310 }
311}
312
313impl FieldValue for &str {
314 fn to_value(&self) -> Value {
315 Value::Text((*self).to_string())
316 }
317}
318
319impl FieldValue for String {
320 fn to_value(&self) -> Value {
321 Value::Text(self.clone())
322 }
323}
324
325impl<T: FieldValue> FieldValue for Option<T> {
326 fn to_value(&self) -> Value {
327 match self {
328 Some(v) => v.to_value(),
329 None => Value::None,
330 }
331 }
332}
333
334impl<T: FieldValue> FieldValue for Box<T> {
335 fn to_value(&self) -> Value {
336 (**self).to_value()
337 }
338}
339
340impl<T: FieldValue> FieldValue for Vec<Box<T>> {
341 fn to_value(&self) -> Value {
342 Value::List(self.iter().map(FieldValue::to_value).collect())
343 }
344}
345
346// impl_field_value
347#[macro_export]
348macro_rules! impl_field_value {
349 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
350 $(
351 impl FieldValue for $type {
352 fn to_value(&self) -> Value {
353 Value::$variant((*self).into())
354 }
355
356 fn from_value(value: &Value) -> Option<Self> {
357 match value {
358 Value::$variant(v) => (*v).try_into().ok(),
359 _ => None,
360 }
361 }
362 }
363 )*
364 };
365}
366
367impl_field_value!(
368 i8 => Int,
369 i16 => Int,
370 i32 => Int,
371 i64 => Int,
372 u8 => Uint,
373 u16 => Uint,
374 u32 => Uint,
375 u64 => Uint,
376 bool => Bool,
377);
378
379/// ============================================================================
380/// MISC HELPERS
381/// ============================================================================
382
383///
384/// Inner
385///
386/// For newtypes to expose their innermost value.
387///
388pub trait Inner<T> {
389 fn inner(&self) -> &T;
390 fn into_inner(self) -> T;
391}
392
393// impl_inner
394#[macro_export]
395macro_rules! impl_inner {
396 ($($type:ty),*) => {
397 $(
398 impl Inner<$type> for $type {
399 fn inner(&self) -> &$type {
400 &self
401 }
402 fn into_inner(self) -> $type {
403 self
404 }
405 }
406 )*
407 };
408}
409
410impl_inner!(
411 bool, f32, f64, i8, i16, i32, i64, i128, String, u8, u16, u32, u64, u128
412);
413
414/// ============================================================================
415/// SANITIZATION / VALIDATION
416/// ============================================================================
417
418///
419/// Sanitizer
420///
421/// Transforms a value into a sanitized version.
422///
423pub trait Sanitizer<T> {
424 fn sanitize(&self, value: &mut T) -> Result<(), String>;
425}
426
427///
428/// Validator
429///
430/// Allows a node to validate values.
431///
432pub trait Validator<T: ?Sized> {
433 fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
434}