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