mod canonical;
mod coercion;
mod compare;
mod hash;
mod input;
pub mod map;
pub mod ops;
mod output;
mod rank;
pub mod semantics;
mod storage_key;
mod storage_key_runtime;
mod tag;
mod wire;
#[cfg(test)]
mod tests;
use crate::{
model::field::{FieldKind, FieldStorageDecode},
prelude::*,
traits::{EnumValue, FieldTypeMeta, RuntimeValueDecode, RuntimeValueEncode, RuntimeValueMeta},
types::*,
};
use candid::CandidType;
use serde::Deserialize;
use std::{cmp::Ordering, fmt};
pub(crate) use canonical::canonicalize_value_set;
pub use coercion::{CoercionFamily, CoercionFamilyExt};
#[cfg(test)]
pub(crate) use hash::with_test_hash_override;
pub(crate) use hash::{ValueHashWriter, hash_single_list_identity_canonical_value, hash_value};
pub use input::{InputValue, InputValueEnum};
pub use map::{MapValueError, SchemaInvariantError};
pub use output::{OutputValue, OutputValueEnum};
pub use storage_key::{StorageKey, StorageKeyDecodeError, StorageKeyEncodeError};
pub(crate) use storage_key_runtime::{
storage_key_as_runtime_value, storage_key_from_runtime_value,
};
pub use tag::ValueTag;
pub(crate) const VALUE_WIRE_TYPE_NAME: &str = "Value";
pub(crate) const VALUE_WIRE_VARIANT_LABELS: &[&str] = &[
"Account",
"Blob",
"Bool",
"Date",
"Decimal",
"Duration",
"Enum",
"Float32",
"Float64",
"Int",
"Int128",
"IntBig",
"List",
"Map",
"Null",
"Principal",
"Subaccount",
"Text",
"Timestamp",
"Uint",
"Uint128",
"UintBig",
"Ulid",
"Unit",
];
#[derive(Clone, Copy)]
pub(crate) enum ValueWireVariant {
Account,
Blob,
Bool,
Date,
Decimal,
Duration,
Enum,
Float32,
Float64,
Int,
Int128,
IntBig,
List,
Map,
Null,
Principal,
Subaccount,
Text,
Timestamp,
Uint,
Uint128,
UintBig,
Ulid,
Unit,
}
impl ValueWireVariant {
pub(crate) fn from_label(label: &str) -> Option<Self> {
match label {
"Account" => Some(Self::Account),
"Blob" => Some(Self::Blob),
"Bool" => Some(Self::Bool),
"Date" => Some(Self::Date),
"Decimal" => Some(Self::Decimal),
"Duration" => Some(Self::Duration),
"Enum" => Some(Self::Enum),
"Float32" => Some(Self::Float32),
"Float64" => Some(Self::Float64),
"Int" => Some(Self::Int),
"Int128" => Some(Self::Int128),
"IntBig" => Some(Self::IntBig),
"List" => Some(Self::List),
"Map" => Some(Self::Map),
"Null" => Some(Self::Null),
"Principal" => Some(Self::Principal),
"Subaccount" => Some(Self::Subaccount),
"Text" => Some(Self::Text),
"Timestamp" => Some(Self::Timestamp),
"Uint" => Some(Self::Uint),
"Uint128" => Some(Self::Uint128),
"UintBig" => Some(Self::UintBig),
"Ulid" => Some(Self::Ulid),
"Unit" => Some(Self::Unit),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TextMode {
Cs, Ci, }
#[derive(CandidType, Clone, Eq, PartialEq)]
pub enum Value {
Account(Account),
Blob(Vec<u8>),
Bool(bool),
Date(Date),
Decimal(Decimal),
Duration(Duration),
Enum(ValueEnum),
Float32(Float32),
Float64(Float64),
Int(i64),
Int128(Int128),
IntBig(Int),
List(Vec<Self>),
Map(Vec<(Self, Self)>),
Null,
Principal(Principal),
Subaccount(Subaccount),
Text(String),
Timestamp(Timestamp),
Uint(u64),
Uint128(Nat128),
UintBig(Nat),
Ulid(Ulid),
Unit,
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Account(value) => f.debug_tuple("Account").field(value).finish(),
Self::Blob(value) => write!(f, "Blob({} bytes)", value.len()),
Self::Bool(value) => f.debug_tuple("Bool").field(value).finish(),
Self::Date(value) => f.debug_tuple("Date").field(value).finish(),
Self::Decimal(value) => f.debug_tuple("Decimal").field(value).finish(),
Self::Duration(value) => f.debug_tuple("Duration").field(value).finish(),
Self::Enum(value) => f.debug_tuple("Enum").field(value).finish(),
Self::Float32(value) => f.debug_tuple("Float32").field(value).finish(),
Self::Float64(value) => f.debug_tuple("Float64").field(value).finish(),
Self::Int(value) => f.debug_tuple("Int").field(value).finish(),
Self::Int128(value) => f.debug_tuple("Int128").field(value).finish(),
Self::IntBig(value) => f.debug_tuple("IntBig").field(value).finish(),
Self::List(value) => f.debug_tuple("List").field(value).finish(),
Self::Map(value) => f.debug_tuple("Map").field(value).finish(),
Self::Null => f.write_str("Null"),
Self::Principal(value) => f.debug_tuple("Principal").field(value).finish(),
Self::Subaccount(value) => f.debug_tuple("Subaccount").field(value).finish(),
Self::Text(value) => f.debug_tuple("Text").field(value).finish(),
Self::Timestamp(value) => f.debug_tuple("Timestamp").field(value).finish(),
Self::Uint(value) => f.debug_tuple("Uint").field(value).finish(),
Self::Uint128(value) => f.debug_tuple("Uint128").field(value).finish(),
Self::UintBig(value) => f.debug_tuple("UintBig").field(value).finish(),
Self::Ulid(value) => f.debug_tuple("Ulid").field(value).finish(),
Self::Unit => f.write_str("Unit"),
}
}
}
impl FieldTypeMeta for Value {
const KIND: FieldKind = FieldKind::Structured { queryable: false };
const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
}
impl Value {
pub const __KIND: FieldKind = FieldKind::Structured { queryable: false };
pub const __STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
}
impl Value {
pub fn from_slice<T>(items: &[T]) -> Self
where
T: Into<Self> + Clone,
{
Self::List(items.iter().cloned().map(Into::into).collect())
}
pub fn from_list<T>(items: Vec<T>) -> Self
where
T: Into<Self>,
{
Self::List(items.into_iter().map(Into::into).collect())
}
pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
let normalized = map::normalize_map_entries(entries)?;
Ok(Self::Map(normalized))
}
pub fn from_enum<E: EnumValue>(value: E) -> Self {
Self::Enum(value.to_value_enum())
}
#[must_use]
pub fn enum_strict<E: Path>(variant: &str) -> Self {
Self::Enum(ValueEnum::strict::<E>(variant))
}
#[must_use]
pub const fn is_text(&self) -> bool {
matches!(self, Self::Text(_))
}
#[must_use]
pub const fn is_unit(&self) -> bool {
matches!(self, Self::Unit)
}
#[must_use]
pub const fn is_scalar(&self) -> bool {
match self {
Self::List(_) | Self::Map(_) | Self::Unit => false,
_ => true,
}
}
#[must_use]
pub(crate) const fn canonical_tag(&self) -> ValueTag {
tag::canonical_tag(self)
}
#[must_use]
pub(crate) const fn canonical_rank(&self) -> u8 {
rank::canonical_rank(self)
}
#[must_use]
pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
compare::canonical_cmp(left, right)
}
#[must_use]
pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
compare::canonical_cmp_key(left, right)
}
#[must_use]
pub const fn as_storage_key(&self) -> Option<StorageKey> {
match self {
Self::Account(value) => Some(StorageKey::Account(*value)),
Self::Int(value) => Some(StorageKey::Int(*value)),
Self::Principal(value) => Some(StorageKey::Principal(*value)),
Self::Subaccount(value) => Some(StorageKey::Subaccount(*value)),
Self::Timestamp(value) => Some(StorageKey::Timestamp(*value)),
Self::Uint(value) => Some(StorageKey::Uint(*value)),
Self::Ulid(value) => Some(StorageKey::Ulid(*value)),
Self::Unit => Some(StorageKey::Unit),
_ => None,
}
}
#[must_use]
pub const fn as_text(&self) -> Option<&str> {
if let Self::Text(s) = self {
Some(s.as_str())
} else {
None
}
}
#[must_use]
pub const fn as_list(&self) -> Option<&[Self]> {
if let Self::List(xs) = self {
Some(xs.as_slice())
} else {
None
}
}
#[must_use]
pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
if let Self::Map(entries) = self {
Some(entries.as_slice())
} else {
None
}
}
}
impl RuntimeValueMeta for Value {
fn kind() -> crate::traits::RuntimeValueKind {
crate::traits::RuntimeValueKind::Atomic
}
}
impl RuntimeValueEncode for Value {
fn to_value(&self) -> Value {
self.clone()
}
}
impl RuntimeValueDecode for Value {
fn from_value(value: &Value) -> Option<Self> {
Some(value.clone())
}
}
#[macro_export]
macro_rules! impl_from_for {
( $( $type:ty => $variant:ident ),* $(,)? ) => {
$(
impl From<$type> for Value {
fn from(v: $type) -> Self {
Self::$variant(v.into())
}
}
)*
};
}
impl_from_for! {
Account => Account,
Date => Date,
Decimal => Decimal,
Duration => Duration,
bool => Bool,
i8 => Int,
i16 => Int,
i32 => Int,
i64 => Int,
i128 => Int128,
Int => IntBig,
Principal => Principal,
Subaccount => Subaccount,
&str => Text,
String => Text,
Timestamp => Timestamp,
u8 => Uint,
u16 => Uint,
u32 => Uint,
u64 => Uint,
u128 => Uint128,
Nat => UintBig,
Ulid => Ulid,
}
impl From<Vec<Self>> for Value {
fn from(vec: Vec<Self>) -> Self {
Self::List(vec)
}
}
impl TryFrom<Vec<(Self, Self)>> for Value {
type Error = SchemaInvariantError;
fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
Self::from_map(entries).map_err(Self::Error::from)
}
}
impl From<()> for Value {
fn from((): ()) -> Self {
Self::Unit
}
}
#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd)]
pub struct ValueEnum {
variant: String,
path: Option<String>,
payload: Option<Box<Value>>,
}
impl ValueEnum {
#[must_use]
pub fn new(variant: &str, path: Option<&str>) -> Self {
Self {
variant: variant.to_string(),
path: path.map(ToString::to_string),
payload: None,
}
}
#[must_use]
pub fn strict<E: Path>(variant: &str) -> Self {
Self::new(variant, Some(E::PATH))
}
#[must_use]
pub fn from_enum<E: EnumValue>(value: E) -> Self {
value.to_value_enum()
}
#[must_use]
pub fn loose(variant: &str) -> Self {
Self::new(variant, None)
}
#[must_use]
pub fn with_payload(mut self, payload: Value) -> Self {
self.payload = Some(Box::new(payload));
self
}
#[must_use]
pub fn variant(&self) -> &str {
&self.variant
}
#[must_use]
pub fn path(&self) -> Option<&str> {
self.path.as_deref()
}
#[must_use]
pub fn payload(&self) -> Option<&Value> {
self.payload.as_deref()
}
pub(crate) fn set_path(&mut self, path: Option<String>) {
self.path = path;
}
}