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