#[macro_use]
mod macros;
mod atomic;
mod numeric_value;
mod visitor;
use crate::{
model::field::{FieldKind, FieldStorageDecode},
prelude::*,
types::{EntityTag, Id},
value::ValueEnum,
visitor::VisitorContext,
};
use std::collections::{BTreeMap, BTreeSet};
pub use atomic::*;
pub use numeric_value::*;
pub use visitor::*;
pub use canic_cdk::structures::storable::Storable;
pub use serde::{Deserialize, Serialize, de::DeserializeOwned};
pub use std::{
cmp::{Eq, Ordering, PartialEq},
convert::From,
default::Default,
fmt::Debug,
hash::Hash,
ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
};
pub trait Path {
const PATH: &'static str;
}
pub trait Kind: Path + 'static {}
impl<T> Kind for T where T: Path + 'static {}
pub trait CanisterKind: Kind {
const COMMIT_MEMORY_ID: u8;
}
pub trait StoreKind: Kind {
type Canister: CanisterKind;
}
pub trait EntityKey {
type Key: Copy + Debug + Eq + Ord + FieldValue + EntityKeyBytes + 'static;
}
pub trait EntityKeyBytes {
const BYTE_LEN: usize;
fn write_bytes(&self, out: &mut [u8]);
}
macro_rules! impl_entity_key_bytes_numeric {
($($ty:ty),* $(,)?) => {
$(
impl EntityKeyBytes for $ty {
const BYTE_LEN: usize = ::core::mem::size_of::<Self>();
fn write_bytes(&self, out: &mut [u8]) {
assert_eq!(out.len(), Self::BYTE_LEN);
out.copy_from_slice(&self.to_be_bytes());
}
}
)*
};
}
impl_entity_key_bytes_numeric!(i8, i16, i32, i64, u8, u16, u32, u64);
impl EntityKeyBytes for () {
const BYTE_LEN: usize = 0;
fn write_bytes(&self, out: &mut [u8]) {
assert_eq!(out.len(), Self::BYTE_LEN);
}
}
pub trait EntitySchema: EntityKey {
const NAME: &'static str;
const MODEL: &'static EntityModel;
}
pub trait EntityPlacement {
type Store: StoreKind;
type Canister: CanisterKind;
}
pub trait EntityKind: EntitySchema + EntityPlacement + Kind + TypeKind {
const ENTITY_TAG: EntityTag;
}
pub trait EntityValue: EntityKey + FieldProjection + Sized {
fn id(&self) -> Id<Self>;
}
pub struct EntityCreateMaterialization<E> {
entity: E,
authored_slots: Vec<usize>,
}
impl<E> EntityCreateMaterialization<E> {
#[must_use]
pub const fn new(entity: E, authored_slots: Vec<usize>) -> Self {
Self {
entity,
authored_slots,
}
}
#[must_use]
pub fn into_entity(self) -> E {
self.entity
}
#[must_use]
pub const fn authored_slots(&self) -> &[usize] {
self.authored_slots.as_slice()
}
}
pub trait EntityCreateInput: Sized {
type Entity: EntityValue + Default;
fn materialize_create(self) -> EntityCreateMaterialization<Self::Entity>;
}
pub trait EntityCreateType: EntityValue {
type Create: EntityCreateInput<Entity = Self>;
}
pub trait SingletonEntity: EntityValue {}
pub trait TypeKind:
Kind + Clone + Default + DeserializeOwned + Sanitize + Validate + Visitable + PartialEq
{
}
impl<T> TypeKind for T where
T: Kind + Clone + Default + DeserializeOwned + PartialEq + Sanitize + Validate + Visitable
{
}
pub trait FieldTypeMeta {
const KIND: FieldKind;
const STORAGE_DECODE: FieldStorageDecode;
}
impl<T> FieldTypeMeta for Option<T>
where
T: FieldTypeMeta,
{
const KIND: FieldKind = T::KIND;
const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
}
impl<T> FieldTypeMeta for Box<T>
where
T: FieldTypeMeta,
{
const KIND: FieldKind = T::KIND;
const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
}
impl<T> FieldTypeMeta for Vec<T>
where
T: FieldTypeMeta,
{
const KIND: FieldKind = FieldKind::List(&T::KIND);
const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
}
impl<T> FieldTypeMeta for BTreeSet<T>
where
T: FieldTypeMeta,
{
const KIND: FieldKind = FieldKind::Set(&T::KIND);
const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
}
impl<K, V> FieldTypeMeta for BTreeMap<K, V>
where
K: FieldTypeMeta,
V: FieldTypeMeta,
{
const KIND: FieldKind = FieldKind::Map {
key: &K::KIND,
value: &V::KIND,
};
const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
}
pub trait Collection {
type Item;
type Iter<'a>: Iterator<Item = &'a Self::Item> + 'a
where
Self: 'a;
fn iter(&self) -> Self::Iter<'_>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub trait MapCollection {
type Key;
type Value;
type Iter<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)> + 'a
where
Self: 'a;
fn iter(&self) -> Self::Iter<'_>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub trait EnumValue {
fn to_value_enum(&self) -> ValueEnum;
}
pub trait FieldProjection {
fn get_value_by_index(&self, index: usize) -> Option<Value>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FieldValueKind {
Atomic,
Structured {
queryable: bool,
},
}
impl FieldValueKind {
#[must_use]
pub const fn is_queryable(self) -> bool {
match self {
Self::Atomic => true,
Self::Structured { queryable } => queryable,
}
}
}
pub trait FieldValue {
fn kind() -> FieldValueKind
where
Self: Sized;
fn to_value(&self) -> Value;
#[must_use]
fn from_value(value: &Value) -> Option<Self>
where
Self: Sized;
}
pub fn field_value_collection_to_value<C>(collection: &C) -> Value
where
C: Collection,
C::Item: FieldValue,
{
Value::List(collection.iter().map(FieldValue::to_value).collect())
}
#[must_use]
pub fn field_value_vec_from_value<T>(value: &Value) -> Option<Vec<T>>
where
T: FieldValue,
{
let Value::List(values) = value else {
return None;
};
let mut out = Vec::with_capacity(values.len());
for value in values {
out.push(T::from_value(value)?);
}
Some(out)
}
#[must_use]
pub fn field_value_btree_set_from_value<T>(value: &Value) -> Option<BTreeSet<T>>
where
T: FieldValue + Ord,
{
let Value::List(values) = value else {
return None;
};
let mut out = BTreeSet::new();
for value in values {
let item = T::from_value(value)?;
if !out.insert(item) {
return None;
}
}
Some(out)
}
pub fn field_value_map_collection_to_value<M>(map: &M, path: &'static str) -> Value
where
M: MapCollection,
M::Key: FieldValue,
M::Value: FieldValue,
{
let mut entries: Vec<(Value, Value)> = map
.iter()
.map(|(key, value)| (FieldValue::to_value(key), FieldValue::to_value(value)))
.collect();
if let Err(err) = Value::validate_map_entries(entries.as_slice()) {
debug_assert!(false, "invalid map field value for {path}: {err}");
return Value::Map(entries);
}
Value::sort_map_entries_in_place(entries.as_mut_slice());
for i in 1..entries.len() {
let (left_key, _) = &entries[i - 1];
let (right_key, _) = &entries[i];
if Value::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
debug_assert!(
false,
"duplicate map key in {path} after FieldValue::to_value canonicalization",
);
break;
}
}
Value::Map(entries)
}
#[must_use]
pub fn field_value_btree_map_from_value<K, V>(value: &Value) -> Option<BTreeMap<K, V>>
where
K: FieldValue + Ord,
V: FieldValue,
{
let Value::Map(entries) = value else {
return None;
};
let normalized = Value::normalize_map_entries(entries.clone()).ok()?;
if normalized.as_slice() != entries.as_slice() {
return None;
}
let mut map = BTreeMap::new();
for (entry_key, entry_value) in normalized {
let key = K::from_value(&entry_key)?;
let value = V::from_value(&entry_value)?;
map.insert(key, value);
}
Some(map)
}
#[must_use]
pub fn field_value_from_vec_into<T, I>(entries: Vec<I>) -> Vec<T>
where
I: Into<T>,
{
entries.into_iter().map(Into::into).collect()
}
#[must_use]
pub fn field_value_from_vec_into_btree_set<T, I>(entries: Vec<I>) -> BTreeSet<T>
where
I: Into<T>,
T: Ord,
{
entries.into_iter().map(Into::into).collect()
}
#[must_use]
pub fn field_value_from_vec_into_btree_map<K, V, IK, IV>(entries: Vec<(IK, IV)>) -> BTreeMap<K, V>
where
IK: Into<K>,
IV: Into<V>,
K: Ord,
{
entries
.into_iter()
.map(|(key, value)| (key.into(), value.into()))
.collect()
}
#[must_use]
pub fn field_value_into<T, U>(value: U) -> T
where
U: Into<T>,
{
value.into()
}
impl FieldValue for &str {
fn kind() -> FieldValueKind {
FieldValueKind::Atomic
}
fn to_value(&self) -> Value {
Value::Text((*self).to_string())
}
fn from_value(_value: &Value) -> Option<Self> {
None
}
}
impl FieldValue for String {
fn kind() -> FieldValueKind {
FieldValueKind::Atomic
}
fn to_value(&self) -> Value {
Value::Text(self.clone())
}
fn from_value(value: &Value) -> Option<Self> {
match value {
Value::Text(v) => Some(v.clone()),
_ => None,
}
}
}
impl<T: FieldValue> FieldValue for Option<T> {
fn kind() -> FieldValueKind {
T::kind()
}
fn to_value(&self) -> Value {
match self {
Some(v) => v.to_value(),
None => Value::Null,
}
}
fn from_value(value: &Value) -> Option<Self> {
if matches!(value, Value::Null) {
return Some(None);
}
T::from_value(value).map(Some)
}
}
impl<T: FieldValue> FieldValue for Box<T> {
fn kind() -> FieldValueKind {
T::kind()
}
fn to_value(&self) -> Value {
(**self).to_value()
}
fn from_value(value: &Value) -> Option<Self> {
T::from_value(value).map(Self::new)
}
}
impl<T: FieldValue> FieldValue for Vec<T> {
fn kind() -> FieldValueKind {
FieldValueKind::Structured { queryable: true }
}
fn to_value(&self) -> Value {
Value::List(self.iter().map(FieldValue::to_value).collect())
}
fn from_value(value: &Value) -> Option<Self> {
field_value_vec_from_value(value)
}
}
impl<T> FieldValue for BTreeSet<T>
where
T: FieldValue + Ord,
{
fn kind() -> FieldValueKind {
FieldValueKind::Structured { queryable: true }
}
fn to_value(&self) -> Value {
Value::List(self.iter().map(FieldValue::to_value).collect())
}
fn from_value(value: &Value) -> Option<Self> {
field_value_btree_set_from_value(value)
}
}
impl<K, V> FieldValue for BTreeMap<K, V>
where
K: FieldValue + Ord,
V: FieldValue,
{
fn kind() -> FieldValueKind {
FieldValueKind::Structured { queryable: true }
}
fn to_value(&self) -> Value {
let mut entries: Vec<(Value, Value)> = self
.iter()
.map(|(key, value)| (FieldValue::to_value(key), FieldValue::to_value(value)))
.collect();
if let Err(err) = Value::validate_map_entries(entries.as_slice()) {
debug_assert!(
false,
"invalid map field value for {}: {err}",
std::any::type_name::<Self>()
);
return Value::Map(entries);
}
Value::sort_map_entries_in_place(entries.as_mut_slice());
Value::Map(entries)
}
fn from_value(value: &Value) -> Option<Self> {
field_value_btree_map_from_value(value)
}
}
#[macro_export]
macro_rules! impl_field_value {
( $( $type:ty => $variant:ident ),* $(,)? ) => {
$(
impl FieldValue for $type {
fn kind() -> FieldValueKind {
FieldValueKind::Atomic
}
fn to_value(&self) -> Value {
Value::$variant((*self).into())
}
fn from_value(value: &Value) -> Option<Self> {
match value {
Value::$variant(v) => (*v).try_into().ok(),
_ => None,
}
}
}
)*
};
}
impl_field_value!(
i8 => Int,
i16 => Int,
i32 => Int,
i64 => Int,
u8 => Uint,
u16 => Uint,
u32 => Uint,
u64 => Uint,
bool => Bool,
);
pub trait Inner<T> {
fn inner(&self) -> &T;
fn into_inner(self) -> T;
}
impl<T> Inner<T> for T
where
T: Atomic,
{
fn inner(&self) -> &T {
self
}
fn into_inner(self) -> T {
self
}
}
pub trait Repr {
type Inner;
fn repr(&self) -> Self::Inner;
fn from_repr(inner: Self::Inner) -> Self;
}
pub trait Sanitizer<T> {
fn sanitize(&self, value: &mut T) -> Result<(), String>;
fn sanitize_with_context(
&self,
value: &mut T,
ctx: &mut dyn VisitorContext,
) -> Result<(), String> {
let _ = ctx;
self.sanitize(value)
}
}
pub trait Validator<T: ?Sized> {
fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
}