#[macro_use]
mod macros;
mod numeric_value;
mod visitor;
use crate::{
error::InternalError,
model::field::{FieldKind, FieldModel, FieldStorageDecode},
prelude::*,
types::{EntityTag, Id},
value::{StorageKey, StorageKeyEncodeError, Value, ValueEnum},
visitor::VisitorContext,
};
use std::collections::{BTreeMap, BTreeSet};
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
+ KeyValueCodec
+ StorageKeyCodec
+ StorageKeyDecode
+ 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 KeyValueCodec {
fn to_key_value(&self) -> Value;
#[must_use]
fn from_key_value(value: &Value) -> Option<Self>
where
Self: Sized;
}
pub trait StorageKeyCodec {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError>;
}
pub trait StorageKeyDecode: Sized {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError>;
}
fn storage_key_variant_decode_failed(
type_name: &'static str,
key: StorageKey,
expected: &'static str,
) -> InternalError {
InternalError::store_corruption(format!(
"storage key decode failed for `{type_name}`: expected {expected}, found {key:?}",
))
}
fn storage_key_range_decode_failed(type_name: &'static str, key: StorageKey) -> InternalError {
InternalError::store_corruption(format!(
"storage key decode failed for `{type_name}`: value out of range for {key:?}",
))
}
macro_rules! impl_storage_key_codec_signed {
($($ty:ty),* $(,)?) => {
$(
impl StorageKeyCodec for $ty {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Int(i64::from(*self)))
}
}
)*
};
}
macro_rules! impl_storage_key_codec_unsigned {
($($ty:ty),* $(,)?) => {
$(
impl StorageKeyCodec for $ty {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Uint(u64::from(*self)))
}
}
)*
};
}
impl<T> KeyValueCodec for T
where
T: RuntimeValueDecode + RuntimeValueEncode,
{
fn to_key_value(&self) -> Value {
self.to_value()
}
fn from_key_value(value: &Value) -> Option<Self> {
Self::from_value(value)
}
}
impl_storage_key_codec_signed!(i8, i16, i32, i64);
impl_storage_key_codec_unsigned!(u8, u16, u32, u64);
macro_rules! impl_storage_key_decode_signed {
($($ty:ty),* $(,)?) => {
$(
impl StorageKeyDecode for $ty {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
let StorageKey::Int(value) = key else {
return Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
key,
"StorageKey::Int",
));
};
Self::try_from(value).map_err(|_| {
storage_key_range_decode_failed(::std::any::type_name::<Self>(), key)
})
}
}
)*
};
}
macro_rules! impl_storage_key_decode_unsigned {
($($ty:ty),* $(,)?) => {
$(
impl StorageKeyDecode for $ty {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
let StorageKey::Uint(value) = key else {
return Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
key,
"StorageKey::Uint",
));
};
Self::try_from(value).map_err(|_| {
storage_key_range_decode_failed(::std::any::type_name::<Self>(), key)
})
}
}
)*
};
}
impl_storage_key_decode_signed!(i8, i16, i32, i64);
impl_storage_key_decode_unsigned!(u8, u16, u32, u64);
impl StorageKeyCodec for crate::types::Principal {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Principal(*self))
}
}
impl StorageKeyDecode for crate::types::Principal {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
match key {
StorageKey::Principal(value) => Ok(value),
other => Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
other,
"StorageKey::Principal",
)),
}
}
}
impl StorageKeyCodec for crate::types::Subaccount {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Subaccount(*self))
}
}
impl StorageKeyDecode for crate::types::Subaccount {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
match key {
StorageKey::Subaccount(value) => Ok(value),
other => Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
other,
"StorageKey::Subaccount",
)),
}
}
}
impl StorageKeyCodec for crate::types::Account {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Account(*self))
}
}
impl StorageKeyDecode for crate::types::Account {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
match key {
StorageKey::Account(value) => Ok(value),
other => Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
other,
"StorageKey::Account",
)),
}
}
}
impl StorageKeyCodec for crate::types::Timestamp {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Timestamp(*self))
}
}
impl StorageKeyDecode for crate::types::Timestamp {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
match key {
StorageKey::Timestamp(value) => Ok(value),
other => Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
other,
"StorageKey::Timestamp",
)),
}
}
}
impl StorageKeyCodec for crate::types::Ulid {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Ulid(*self))
}
}
impl StorageKeyDecode for crate::types::Ulid {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
match key {
StorageKey::Ulid(value) => Ok(value),
other => Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
other,
"StorageKey::Ulid",
)),
}
}
}
impl StorageKeyCodec for () {
fn to_storage_key(&self) -> Result<StorageKey, StorageKeyEncodeError> {
Ok(StorageKey::Unit)
}
}
impl StorageKeyDecode for () {
fn from_storage_key(key: StorageKey) -> Result<Self, InternalError> {
match key {
StorageKey::Unit => Ok(()),
other => Err(storage_key_variant_decode_failed(
::std::any::type_name::<Self>(),
other,
"StorageKey::Unit",
)),
}
}
}
pub trait RuntimeValueEncode {
fn to_value(&self) -> Value;
}
pub trait RuntimeValueDecode {
#[must_use]
fn from_value(value: &Value) -> Option<Self>
where
Self: Sized;
}
pub fn runtime_value_to_value<T>(value: &T) -> Value
where
T: ?Sized + RuntimeValueEncode,
{
value.to_value()
}
#[must_use]
pub fn runtime_value_from_value<T>(value: &Value) -> Option<T>
where
T: RuntimeValueDecode,
{
T::from_value(value)
}
pub trait PersistedByKindCodec: Sized {
fn encode_persisted_slot_payload_by_kind(
&self,
kind: FieldKind,
field_name: &'static str,
) -> Result<Vec<u8>, InternalError>;
fn decode_persisted_option_slot_payload_by_kind(
bytes: &[u8],
kind: FieldKind,
field_name: &'static str,
) -> Result<Option<Self>, InternalError>;
}
pub trait PersistedStructuredFieldCodec {
fn encode_persisted_structured_payload(&self) -> Result<Vec<u8>, InternalError>;
fn decode_persisted_structured_payload(bytes: &[u8]) -> Result<Self, InternalError>
where
Self: Sized;
}
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;
const NESTED_FIELDS: &'static [FieldModel] = &[];
}
pub trait PersistedFieldMetaCodec: FieldTypeMeta + Sized {
fn encode_persisted_slot_payload_by_meta(
&self,
field_name: &'static str,
) -> Result<Vec<u8>, InternalError>;
fn decode_persisted_slot_payload_by_meta(
bytes: &[u8],
field_name: &'static str,
) -> Result<Self, InternalError>;
fn encode_persisted_option_slot_payload_by_meta(
value: &Option<Self>,
field_name: &'static str,
) -> Result<Vec<u8>, InternalError>;
fn decode_persisted_option_slot_payload_by_meta(
bytes: &[u8],
field_name: &'static str,
) -> Result<Option<Self>, InternalError>;
}
impl<T> FieldTypeMeta for Option<T>
where
T: FieldTypeMeta,
{
const KIND: FieldKind = T::KIND;
const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
const NESTED_FIELDS: &'static [FieldModel] = T::NESTED_FIELDS;
}
impl<T> FieldTypeMeta for Box<T>
where
T: FieldTypeMeta,
{
const KIND: FieldKind = T::KIND;
const STORAGE_DECODE: FieldStorageDecode = T::STORAGE_DECODE;
const NESTED_FIELDS: &'static [FieldModel] = T::NESTED_FIELDS;
}
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
}
}
impl<T> Collection for Vec<T> {
type Item = T;
type Iter<'a>
= std::slice::Iter<'a, T>
where
Self: 'a;
fn iter(&self) -> Self::Iter<'_> {
self.as_slice().iter()
}
fn len(&self) -> usize {
self.as_slice().len()
}
}
impl<T> Collection for BTreeSet<T> {
type Item = T;
type Iter<'a>
= std::collections::btree_set::Iter<'a, T>
where
Self: 'a;
fn iter(&self) -> Self::Iter<'_> {
self.iter()
}
fn len(&self) -> usize {
self.len()
}
}
impl<K, V> MapCollection for BTreeMap<K, V> {
type Key = K;
type Value = V;
type Iter<'a>
= std::collections::btree_map::Iter<'a, K, V>
where
Self: 'a;
fn iter(&self) -> Self::Iter<'_> {
self.iter()
}
fn len(&self) -> usize {
self.len()
}
}
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 RuntimeValueKind {
Atomic,
Structured {
queryable: bool,
},
}
impl RuntimeValueKind {
#[must_use]
pub const fn is_queryable(self) -> bool {
match self {
Self::Atomic => true,
Self::Structured { queryable } => queryable,
}
}
}
pub trait RuntimeValueMeta {
fn kind() -> RuntimeValueKind
where
Self: Sized;
}
pub fn runtime_value_collection_to_value<C>(collection: &C) -> Value
where
C: Collection,
C::Item: RuntimeValueEncode,
{
Value::List(
collection
.iter()
.map(RuntimeValueEncode::to_value)
.collect(),
)
}
#[must_use]
pub fn runtime_value_vec_from_value<T>(value: &Value) -> Option<Vec<T>>
where
T: RuntimeValueDecode,
{
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 runtime_value_btree_set_from_value<T>(value: &Value) -> Option<BTreeSet<T>>
where
T: Ord + RuntimeValueDecode,
{
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 runtime_value_map_collection_to_value<M>(map: &M, path: &'static str) -> Value
where
M: MapCollection,
M::Key: RuntimeValueEncode,
M::Value: RuntimeValueEncode,
{
let mut entries: Vec<(Value, Value)> = map
.iter()
.map(|(key, value)| {
(
RuntimeValueEncode::to_value(key),
RuntimeValueEncode::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 value-surface canonicalization",
);
break;
}
}
Value::Map(entries)
}
#[must_use]
pub fn runtime_value_btree_map_from_value<K, V>(value: &Value) -> Option<BTreeMap<K, V>>
where
K: Ord + RuntimeValueDecode,
V: RuntimeValueDecode,
{
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 runtime_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 runtime_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 runtime_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 runtime_value_into<T, U>(value: U) -> T
where
U: Into<T>,
{
value.into()
}
impl RuntimeValueMeta for &str {
fn kind() -> RuntimeValueKind {
RuntimeValueKind::Atomic
}
}
impl RuntimeValueEncode for &str {
fn to_value(&self) -> Value {
Value::Text((*self).to_string())
}
}
impl RuntimeValueDecode for &str {
fn from_value(_value: &Value) -> Option<Self> {
None
}
}
impl RuntimeValueMeta for String {
fn kind() -> RuntimeValueKind {
RuntimeValueKind::Atomic
}
}
impl RuntimeValueEncode for String {
fn to_value(&self) -> Value {
Value::Text(self.clone())
}
}
impl RuntimeValueDecode for String {
fn from_value(value: &Value) -> Option<Self> {
match value {
Value::Text(v) => Some(v.clone()),
_ => None,
}
}
}
impl<T: RuntimeValueMeta> RuntimeValueMeta for Option<T> {
fn kind() -> RuntimeValueKind {
T::kind()
}
}
impl<T: RuntimeValueEncode> RuntimeValueEncode for Option<T> {
fn to_value(&self) -> Value {
match self {
Some(v) => v.to_value(),
None => Value::Null,
}
}
}
impl<T: RuntimeValueDecode> RuntimeValueDecode for Option<T> {
fn from_value(value: &Value) -> Option<Self> {
if matches!(value, Value::Null) {
return Some(None);
}
T::from_value(value).map(Some)
}
}
impl<T: RuntimeValueMeta> RuntimeValueMeta for Box<T> {
fn kind() -> RuntimeValueKind {
T::kind()
}
}
impl<T: RuntimeValueEncode> RuntimeValueEncode for Box<T> {
fn to_value(&self) -> Value {
(**self).to_value()
}
}
impl<T: RuntimeValueDecode> RuntimeValueDecode for Box<T> {
fn from_value(value: &Value) -> Option<Self> {
T::from_value(value).map(Self::new)
}
}
impl<T> RuntimeValueMeta for Vec<T> {
fn kind() -> RuntimeValueKind {
RuntimeValueKind::Structured { queryable: true }
}
}
impl<T: RuntimeValueEncode> RuntimeValueEncode for Vec<T> {
fn to_value(&self) -> Value {
runtime_value_collection_to_value(self)
}
}
impl<T: RuntimeValueDecode> RuntimeValueDecode for Vec<T> {
fn from_value(value: &Value) -> Option<Self> {
runtime_value_vec_from_value(value)
}
}
impl<T> RuntimeValueMeta for BTreeSet<T>
where
T: Ord,
{
fn kind() -> RuntimeValueKind {
RuntimeValueKind::Structured { queryable: true }
}
}
impl<T> RuntimeValueEncode for BTreeSet<T>
where
T: Ord + RuntimeValueEncode,
{
fn to_value(&self) -> Value {
runtime_value_collection_to_value(self)
}
}
impl<T> RuntimeValueDecode for BTreeSet<T>
where
T: Ord + RuntimeValueDecode,
{
fn from_value(value: &Value) -> Option<Self> {
runtime_value_btree_set_from_value(value)
}
}
impl<K, V> RuntimeValueMeta for BTreeMap<K, V>
where
K: Ord,
{
fn kind() -> RuntimeValueKind {
RuntimeValueKind::Structured { queryable: true }
}
}
impl<K, V> RuntimeValueEncode for BTreeMap<K, V>
where
K: Ord + RuntimeValueEncode,
V: RuntimeValueEncode,
{
fn to_value(&self) -> Value {
runtime_value_map_collection_to_value(self, std::any::type_name::<Self>())
}
}
impl<K, V> RuntimeValueDecode for BTreeMap<K, V>
where
K: Ord + RuntimeValueDecode,
V: RuntimeValueDecode,
{
fn from_value(value: &Value) -> Option<Self> {
runtime_value_btree_map_from_value(value)
}
}
#[macro_export]
macro_rules! impl_runtime_value {
( $( $type:ty => $variant:ident ),* $(,)? ) => {
$(
impl RuntimeValueMeta for $type {
fn kind() -> RuntimeValueKind {
RuntimeValueKind::Atomic
}
}
impl RuntimeValueEncode for $type {
fn to_value(&self) -> Value {
Value::$variant((*self).into())
}
}
impl RuntimeValueDecode for $type {
fn from_value(value: &Value) -> Option<Self> {
match value {
Value::$variant(v) => (*v).try_into().ok(),
_ => None,
}
}
}
)*
};
}
impl_runtime_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;
}
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);
}