1mod canonical;
8mod coercion;
9mod compare;
10mod hash;
11mod input;
12pub mod map;
13pub mod ops;
14mod output;
15mod rank;
16pub mod semantics;
17mod storage_key;
18mod storage_key_runtime;
19mod tag;
20mod wire;
21
22#[cfg(test)]
23mod tests;
24
25use crate::{
26 model::field::{FieldKind, FieldStorageDecode},
27 prelude::*,
28 traits::{EnumValue, FieldTypeMeta, RuntimeValueDecode, RuntimeValueEncode, RuntimeValueMeta},
29 types::*,
30};
31use candid::CandidType;
32use serde::Deserialize;
33use std::{cmp::Ordering, fmt};
34
35pub(crate) use canonical::canonicalize_value_set;
37pub use coercion::{CoercionFamily, CoercionFamilyExt};
38#[cfg(test)]
39pub(crate) use hash::with_test_hash_override;
40pub(crate) use hash::{ValueHashWriter, hash_single_list_identity_canonical_value, hash_value};
41pub use input::{InputValue, InputValueEnum};
42pub use map::{MapValueError, SchemaInvariantError};
43pub use output::{OutputValue, OutputValueEnum};
44pub use storage_key::{StorageKey, StorageKeyDecodeError, StorageKeyEncodeError};
45pub(crate) use storage_key_runtime::{
46 storage_key_as_runtime_value, storage_key_from_runtime_value,
47};
48pub use tag::ValueTag;
49
50pub(crate) const VALUE_WIRE_TYPE_NAME: &str = "Value";
55pub(crate) const VALUE_WIRE_VARIANT_LABELS: &[&str] = &[
56 "Account",
57 "Blob",
58 "Bool",
59 "Date",
60 "Decimal",
61 "Duration",
62 "Enum",
63 "Float32",
64 "Float64",
65 "Int",
66 "Int128",
67 "IntBig",
68 "List",
69 "Map",
70 "Null",
71 "Principal",
72 "Subaccount",
73 "Text",
74 "Timestamp",
75 "Uint",
76 "Uint128",
77 "UintBig",
78 "Ulid",
79 "Unit",
80];
81
82#[derive(Clone, Copy)]
84pub(crate) enum ValueWireVariant {
85 Account,
86 Blob,
87 Bool,
88 Date,
89 Decimal,
90 Duration,
91 Enum,
92 Float32,
93 Float64,
94 Int,
95 Int128,
96 IntBig,
97 List,
98 Map,
99 Null,
100 Principal,
101 Subaccount,
102 Text,
103 Timestamp,
104 Uint,
105 Uint128,
106 UintBig,
107 Ulid,
108 Unit,
109}
110
111impl ValueWireVariant {
112 pub(crate) fn from_label(label: &str) -> Option<Self> {
114 match label {
115 "Account" => Some(Self::Account),
116 "Blob" => Some(Self::Blob),
117 "Bool" => Some(Self::Bool),
118 "Date" => Some(Self::Date),
119 "Decimal" => Some(Self::Decimal),
120 "Duration" => Some(Self::Duration),
121 "Enum" => Some(Self::Enum),
122 "Float32" => Some(Self::Float32),
123 "Float64" => Some(Self::Float64),
124 "Int" => Some(Self::Int),
125 "Int128" => Some(Self::Int128),
126 "IntBig" => Some(Self::IntBig),
127 "List" => Some(Self::List),
128 "Map" => Some(Self::Map),
129 "Null" => Some(Self::Null),
130 "Principal" => Some(Self::Principal),
131 "Subaccount" => Some(Self::Subaccount),
132 "Text" => Some(Self::Text),
133 "Timestamp" => Some(Self::Timestamp),
134 "Uint" => Some(Self::Uint),
135 "Uint128" => Some(Self::Uint128),
136 "UintBig" => Some(Self::UintBig),
137 "Ulid" => Some(Self::Ulid),
138 "Unit" => Some(Self::Unit),
139 _ => None,
140 }
141 }
142}
143
144#[derive(Clone, Copy, Debug, Eq, PartialEq)]
149pub enum TextMode {
150 Cs, Ci, }
153
154#[derive(CandidType, Clone, Eq, PartialEq)]
163pub enum Value {
164 Account(Account),
165 Blob(Vec<u8>),
166 Bool(bool),
167 Date(Date),
168 Decimal(Decimal),
169 Duration(Duration),
170 Enum(ValueEnum),
171 Float32(Float32),
172 Float64(Float64),
173 Int(i64),
174 Int128(Int128),
175 IntBig(Int),
176 List(Vec<Self>),
180 Map(Vec<(Self, Self)>),
187 Null,
188 Principal(Principal),
189 Subaccount(Subaccount),
190 Text(String),
191 Timestamp(Timestamp),
192 Uint(u64),
193 Uint128(Nat128),
194 UintBig(Nat),
195 Ulid(Ulid),
196 Unit,
197}
198
199impl fmt::Debug for Value {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 match self {
202 Self::Account(value) => f.debug_tuple("Account").field(value).finish(),
203 Self::Blob(value) => write!(f, "Blob({} bytes)", value.len()),
204 Self::Bool(value) => f.debug_tuple("Bool").field(value).finish(),
205 Self::Date(value) => f.debug_tuple("Date").field(value).finish(),
206 Self::Decimal(value) => f.debug_tuple("Decimal").field(value).finish(),
207 Self::Duration(value) => f.debug_tuple("Duration").field(value).finish(),
208 Self::Enum(value) => f.debug_tuple("Enum").field(value).finish(),
209 Self::Float32(value) => f.debug_tuple("Float32").field(value).finish(),
210 Self::Float64(value) => f.debug_tuple("Float64").field(value).finish(),
211 Self::Int(value) => f.debug_tuple("Int").field(value).finish(),
212 Self::Int128(value) => f.debug_tuple("Int128").field(value).finish(),
213 Self::IntBig(value) => f.debug_tuple("IntBig").field(value).finish(),
214 Self::List(value) => f.debug_tuple("List").field(value).finish(),
215 Self::Map(value) => f.debug_tuple("Map").field(value).finish(),
216 Self::Null => f.write_str("Null"),
217 Self::Principal(value) => f.debug_tuple("Principal").field(value).finish(),
218 Self::Subaccount(value) => f.debug_tuple("Subaccount").field(value).finish(),
219 Self::Text(value) => f.debug_tuple("Text").field(value).finish(),
220 Self::Timestamp(value) => f.debug_tuple("Timestamp").field(value).finish(),
221 Self::Uint(value) => f.debug_tuple("Uint").field(value).finish(),
222 Self::Uint128(value) => f.debug_tuple("Uint128").field(value).finish(),
223 Self::UintBig(value) => f.debug_tuple("UintBig").field(value).finish(),
224 Self::Ulid(value) => f.debug_tuple("Ulid").field(value).finish(),
225 Self::Unit => f.write_str("Unit"),
226 }
227 }
228}
229
230impl FieldTypeMeta for Value {
231 const KIND: FieldKind = FieldKind::Structured { queryable: false };
232 const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
233}
234
235impl Value {
236 pub const __KIND: FieldKind = FieldKind::Structured { queryable: false };
237 pub const __STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
238}
239
240impl Value {
241 pub fn from_slice<T>(items: &[T]) -> Self
250 where
251 T: Into<Self> + Clone,
252 {
253 Self::List(items.iter().cloned().map(Into::into).collect())
254 }
255
256 pub fn from_list<T>(items: Vec<T>) -> Self
260 where
261 T: Into<Self>,
262 {
263 Self::List(items.into_iter().map(Into::into).collect())
264 }
265
266 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
274 let normalized = map::normalize_map_entries(entries)?;
275 Ok(Self::Map(normalized))
276 }
277
278 pub fn from_enum<E: EnumValue>(value: E) -> Self {
280 Self::Enum(value.to_value_enum())
281 }
282
283 #[must_use]
285 pub fn enum_strict<E: Path>(variant: &str) -> Self {
286 Self::Enum(ValueEnum::strict::<E>(variant))
287 }
288
289 #[must_use]
295 pub const fn is_text(&self) -> bool {
296 matches!(self, Self::Text(_))
297 }
298
299 #[must_use]
301 pub const fn is_unit(&self) -> bool {
302 matches!(self, Self::Unit)
303 }
304
305 #[must_use]
306 pub const fn is_scalar(&self) -> bool {
307 match self {
308 Self::List(_) | Self::Map(_) | Self::Unit => false,
310 _ => true,
311 }
312 }
313
314 #[must_use]
316 pub(crate) const fn canonical_tag(&self) -> ValueTag {
317 tag::canonical_tag(self)
318 }
319
320 #[must_use]
322 pub(crate) const fn canonical_rank(&self) -> u8 {
323 rank::canonical_rank(self)
324 }
325
326 #[must_use]
328 pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
329 compare::canonical_cmp(left, right)
330 }
331
332 #[must_use]
334 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
335 compare::canonical_cmp_key(left, right)
336 }
337
338 #[must_use]
347 pub const fn as_storage_key(&self) -> Option<StorageKey> {
348 match self {
349 Self::Account(value) => Some(StorageKey::Account(*value)),
350 Self::Int(value) => Some(StorageKey::Int(*value)),
351 Self::Principal(value) => Some(StorageKey::Principal(*value)),
352 Self::Subaccount(value) => Some(StorageKey::Subaccount(*value)),
353 Self::Timestamp(value) => Some(StorageKey::Timestamp(*value)),
354 Self::Uint(value) => Some(StorageKey::Uint(*value)),
355 Self::Ulid(value) => Some(StorageKey::Ulid(*value)),
356 Self::Unit => Some(StorageKey::Unit),
357 _ => None,
358 }
359 }
360
361 #[must_use]
362 pub const fn as_text(&self) -> Option<&str> {
363 if let Self::Text(s) = self {
364 Some(s.as_str())
365 } else {
366 None
367 }
368 }
369
370 #[must_use]
371 pub const fn as_list(&self) -> Option<&[Self]> {
372 if let Self::List(xs) = self {
373 Some(xs.as_slice())
374 } else {
375 None
376 }
377 }
378
379 #[must_use]
380 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
381 if let Self::Map(entries) = self {
382 Some(entries.as_slice())
383 } else {
384 None
385 }
386 }
387}
388
389impl RuntimeValueMeta for Value {
390 fn kind() -> crate::traits::RuntimeValueKind {
391 crate::traits::RuntimeValueKind::Atomic
392 }
393}
394
395impl RuntimeValueEncode for Value {
396 fn to_value(&self) -> Value {
397 self.clone()
398 }
399}
400
401impl RuntimeValueDecode for Value {
402 fn from_value(value: &Value) -> Option<Self> {
403 Some(value.clone())
404 }
405}
406
407#[macro_export]
408macro_rules! impl_from_for {
409 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
410 $(
411 impl From<$type> for Value {
412 fn from(v: $type) -> Self {
413 Self::$variant(v.into())
414 }
415 }
416 )*
417 };
418}
419
420impl_from_for! {
421 Account => Account,
422 Date => Date,
423 Decimal => Decimal,
424 Duration => Duration,
425 bool => Bool,
426 i8 => Int,
427 i16 => Int,
428 i32 => Int,
429 i64 => Int,
430 i128 => Int128,
431 Int => IntBig,
432 Principal => Principal,
433 Subaccount => Subaccount,
434 &str => Text,
435 String => Text,
436 Timestamp => Timestamp,
437 u8 => Uint,
438 u16 => Uint,
439 u32 => Uint,
440 u64 => Uint,
441 u128 => Uint128,
442 Nat => UintBig,
443 Ulid => Ulid,
444}
445
446impl From<Vec<Self>> for Value {
447 fn from(vec: Vec<Self>) -> Self {
448 Self::List(vec)
449 }
450}
451
452impl TryFrom<Vec<(Self, Self)>> for Value {
453 type Error = SchemaInvariantError;
454
455 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
456 Self::from_map(entries).map_err(Self::Error::from)
457 }
458}
459
460impl From<()> for Value {
461 fn from((): ()) -> Self {
462 Self::Unit
463 }
464}
465
466#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd)]
472pub struct ValueEnum {
473 variant: String,
474 path: Option<String>,
475 payload: Option<Box<Value>>,
476}
477
478impl ValueEnum {
479 #[must_use]
481 pub fn new(variant: &str, path: Option<&str>) -> Self {
482 Self {
483 variant: variant.to_string(),
484 path: path.map(ToString::to_string),
485 payload: None,
486 }
487 }
488
489 #[must_use]
491 pub fn strict<E: Path>(variant: &str) -> Self {
492 Self::new(variant, Some(E::PATH))
493 }
494
495 #[must_use]
497 pub fn from_enum<E: EnumValue>(value: E) -> Self {
498 value.to_value_enum()
499 }
500
501 #[must_use]
504 pub fn loose(variant: &str) -> Self {
505 Self::new(variant, None)
506 }
507
508 #[must_use]
510 pub fn with_payload(mut self, payload: Value) -> Self {
511 self.payload = Some(Box::new(payload));
512 self
513 }
514
515 #[must_use]
516 pub fn variant(&self) -> &str {
517 &self.variant
518 }
519
520 #[must_use]
521 pub fn path(&self) -> Option<&str> {
522 self.path.as_deref()
523 }
524
525 #[must_use]
526 pub fn payload(&self) -> Option<&Value> {
527 self.payload.as_deref()
528 }
529
530 pub(crate) fn set_path(&mut self, path: Option<String>) {
531 self.path = path;
532 }
533}