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