1#[macro_use]
2mod macros;
3mod view;
4mod visitor;
5
6pub use view::*;
7pub use visitor::*;
8
9pub use canic_cdk::structures::storable::Storable;
12pub use num_traits::{FromPrimitive as NumFromPrimitive, NumCast, ToPrimitive as NumToPrimitive};
13pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
14pub use std::{
15 cmp::{Eq, Ordering, PartialEq},
16 convert::{AsRef, From, Into},
17 default::Default,
18 fmt::{Debug, Display},
19 hash::Hash,
20 iter::IntoIterator,
21 ops::{Add, AddAssign, Deref, DerefMut, Mul, MulAssign, Sub, SubAssign},
22 str::FromStr,
23};
24
25use crate::{
26 error::{ErrorClass, ErrorOrigin, InternalError},
27 model::field::EntityFieldKind,
28 prelude::*,
29 types::Unit,
30 value::ValueEnum,
31 visitor::VisitorContext,
32};
33
34pub trait Kind: Path + 'static {}
44
45impl<T> Kind for T where T: Path + 'static {}
46
47pub trait CanisterKind: Kind {}
52
53pub trait DataStoreKind: Kind {
58 type Canister: CanisterKind;
59}
60
61pub trait EntityKind: Kind + TypeKind + FieldValues {
66 type PrimaryKey: Copy + Into<Key>;
67 type DataStore: DataStoreKind;
68 type Canister: CanisterKind; const ENTITY_NAME: &'static str;
71 const PRIMARY_KEY: &'static str;
72 const FIELDS: &'static [&'static str];
73 const INDEXES: &'static [&'static IndexModel];
74 const MODEL: &'static crate::model::entity::EntityModel;
75
76 fn key(&self) -> Key;
77 fn primary_key(&self) -> Self::PrimaryKey;
78 fn set_primary_key(&mut self, key: Self::PrimaryKey);
79}
80
81pub trait IndexStoreKind: Kind {
86 type Canister: CanisterKind;
87}
88
89pub trait UnitKey: Copy + Into<Key> + unit_key::Sealed {}
95
96impl UnitKey for () {}
97impl UnitKey for Unit {}
98
99mod unit_key {
100 use crate::types::Unit;
101
102 pub trait Sealed {}
104
105 impl Sealed for () {}
106 impl Sealed for Unit {}
107}
108
109pub trait TypeKind:
119 Kind
120 + View
121 + Clone
122 + Default
123 + Serialize
124 + DeserializeOwned
125 + Sanitize
126 + Validate
127 + Visitable
128 + PartialEq
129{
130}
131
132impl<T> TypeKind for T where
133 T: Kind
134 + View
135 + Clone
136 + Default
137 + DeserializeOwned
138 + PartialEq
139 + Serialize
140 + Sanitize
141 + Validate
142 + Visitable
143{
144}
145
146pub trait FieldValues {
155 fn get_value(&self, field: &str) -> Option<Value>;
156}
157
158#[derive(Clone, Copy, Debug, Eq, PartialEq)]
167pub struct EntityRef {
168 pub target_path: &'static str,
169 pub key: Key,
170}
171
172pub trait EntityReferences {
182 fn entity_refs(&self) -> Result<Vec<EntityRef>, InternalError>;
184}
185
186impl<E> EntityReferences for E
187where
188 E: EntityKind,
189{
190 fn entity_refs(&self) -> Result<Vec<EntityRef>, InternalError> {
191 let mut refs = Vec::with_capacity(E::MODEL.fields.len());
192
193 for field in E::MODEL.fields {
194 let target_path = match &field.kind {
196 &EntityFieldKind::Ref { target_path, .. } => target_path,
197 &EntityFieldKind::List(inner) | &EntityFieldKind::Set(inner) => {
198 if matches!(inner, &EntityFieldKind::Ref { .. }) {
199 continue;
201 }
202 continue;
203 }
204 &EntityFieldKind::Map { key, value } => {
205 if matches!(key, &EntityFieldKind::Ref { .. })
206 || matches!(value, &EntityFieldKind::Ref { .. })
207 {
208 continue;
210 }
211 continue;
212 }
213 _ => continue,
214 };
215
216 let Some(value) = self.get_value(field.name) else {
218 return Err(InternalError::new(
219 ErrorClass::InvariantViolation,
220 ErrorOrigin::Executor,
221 format!("reference field missing: {} field={}", E::PATH, field.name),
222 ));
223 };
224
225 if matches!(value, Value::None) {
226 continue;
227 }
228
229 if matches!(value, Value::Unsupported) {
230 return Err(InternalError::new(
231 ErrorClass::InvariantViolation,
232 ErrorOrigin::Executor,
233 format!(
234 "reference field value is unsupported: {} field={}",
235 E::PATH,
236 field.name
237 ),
238 ));
239 }
240
241 let Some(key) = value.as_key() else {
243 return Err(InternalError::new(
244 ErrorClass::InvariantViolation,
245 ErrorOrigin::Executor,
246 format!(
247 "reference field value is not a key: {} field={}",
248 E::PATH,
249 field.name
250 ),
251 ));
252 };
253
254 refs.push(EntityRef { target_path, key });
255 }
256
257 Ok(refs)
258 }
259}
260
261pub trait FieldValue {
273 fn to_value(&self) -> Value {
274 Value::Unsupported
275 }
276}
277
278pub trait EnumValue {
284 fn to_value_enum(&self) -> ValueEnum;
286}
287
288impl FieldValue for &str {
289 fn to_value(&self) -> Value {
290 Value::Text((*self).to_string())
291 }
292}
293
294impl FieldValue for String {
295 fn to_value(&self) -> Value {
296 Value::Text(self.clone())
297 }
298}
299
300impl<T> FieldValue for &'_ T
301where
302 T: FieldValue + Copy,
303{
304 fn to_value(&self) -> Value {
305 (*self).to_value()
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 Vec<T> {
319 fn to_value(&self) -> Value {
320 Value::List(self.iter().map(FieldValue::to_value).collect())
321 }
322}
323
324impl<T: FieldValue> FieldValue for Box<T> {
325 fn to_value(&self) -> Value {
326 (**self).to_value()
327 }
328}
329
330#[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 )*
341 };
342}
343
344impl_field_value!(
345 i8 => Int,
346 i16 => Int,
347 i32 => Int,
348 i64 => Int,
349 u8 => Uint,
350 u16 => Uint,
351 u32 => Uint,
352 u64 => Uint,
353 bool => Bool,
354);
355
356pub trait Inner<T> {
365 fn inner(&self) -> &T;
366 fn into_inner(self) -> T;
367}
368
369#[macro_export]
371macro_rules! impl_inner {
372 ($($type:ty),*) => {
373 $(
374 impl Inner<$type> for $type {
375 fn inner(&self) -> &$type {
376 &self
377 }
378 fn into_inner(self) -> $type {
379 self
380 }
381 }
382 )*
383 };
384}
385
386impl_inner!(
387 bool, f32, f64, i8, i16, i32, i64, i128, String, u8, u16, u32, u64, u128
388);
389
390pub trait Path {
398 const PATH: &'static str;
399}
400
401pub trait Sanitizer<T> {
407 fn sanitize(&self, value: &mut T) -> Result<(), String>;
412}
413
414pub trait Validator<T: ?Sized> {
421 fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
422}
423
424#[cfg(test)]
425mod tests;