1mod canonical;
8mod coercion;
9mod compare;
10mod hash;
11mod input;
12mod output;
13mod rank;
14mod storage_key;
15mod storage_key_runtime;
16mod tag;
17mod wire;
18
19#[cfg(test)]
20mod tests;
21
22use crate::{
23 model::field::{FieldKind, FieldStorageDecode},
24 prelude::*,
25 traits::{
26 EnumValue, FieldTypeMeta, NumericValue, Repr, RuntimeValueDecode, RuntimeValueEncode,
27 RuntimeValueMeta,
28 },
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 output::{OutputValue, OutputValueEnum};
43pub use storage_key::{StorageKey, StorageKeyDecodeError, StorageKeyEncodeError};
44pub(crate) use storage_key_runtime::{
45 storage_key_as_runtime_value, storage_key_from_runtime_value,
46};
47pub use tag::ValueTag;
48
49const F64_SAFE_I64: i64 = 1i64 << 53;
54const F64_SAFE_U64: u64 = 1u64 << 53;
55const F64_SAFE_I128: i128 = 1i128 << 53;
56const F64_SAFE_U128: u128 = 1u128 << 53;
57pub(crate) const VALUE_WIRE_TYPE_NAME: &str = "Value";
58pub(crate) const VALUE_WIRE_VARIANT_LABELS: &[&str] = &[
59 "Account",
60 "Blob",
61 "Bool",
62 "Date",
63 "Decimal",
64 "Duration",
65 "Enum",
66 "Float32",
67 "Float64",
68 "Int",
69 "Int128",
70 "IntBig",
71 "List",
72 "Map",
73 "Null",
74 "Principal",
75 "Subaccount",
76 "Text",
77 "Timestamp",
78 "Uint",
79 "Uint128",
80 "UintBig",
81 "Ulid",
82 "Unit",
83];
84
85enum NumericRepr {
90 Decimal(Decimal),
91 F64(f64),
92 None,
93}
94
95#[derive(Clone, Copy)]
97pub(crate) enum ValueWireVariant {
98 Account,
99 Blob,
100 Bool,
101 Date,
102 Decimal,
103 Duration,
104 Enum,
105 Float32,
106 Float64,
107 Int,
108 Int128,
109 IntBig,
110 List,
111 Map,
112 Null,
113 Principal,
114 Subaccount,
115 Text,
116 Timestamp,
117 Uint,
118 Uint128,
119 UintBig,
120 Ulid,
121 Unit,
122}
123
124impl ValueWireVariant {
125 pub(crate) fn from_label(label: &str) -> Option<Self> {
127 match label {
128 "Account" => Some(Self::Account),
129 "Blob" => Some(Self::Blob),
130 "Bool" => Some(Self::Bool),
131 "Date" => Some(Self::Date),
132 "Decimal" => Some(Self::Decimal),
133 "Duration" => Some(Self::Duration),
134 "Enum" => Some(Self::Enum),
135 "Float32" => Some(Self::Float32),
136 "Float64" => Some(Self::Float64),
137 "Int" => Some(Self::Int),
138 "Int128" => Some(Self::Int128),
139 "IntBig" => Some(Self::IntBig),
140 "List" => Some(Self::List),
141 "Map" => Some(Self::Map),
142 "Null" => Some(Self::Null),
143 "Principal" => Some(Self::Principal),
144 "Subaccount" => Some(Self::Subaccount),
145 "Text" => Some(Self::Text),
146 "Timestamp" => Some(Self::Timestamp),
147 "Uint" => Some(Self::Uint),
148 "Uint128" => Some(Self::Uint128),
149 "UintBig" => Some(Self::UintBig),
150 "Ulid" => Some(Self::Ulid),
151 "Unit" => Some(Self::Unit),
152 _ => None,
153 }
154 }
155}
156
157#[derive(Clone, Copy, Debug, Eq, PartialEq)]
162pub enum TextMode {
163 Cs, Ci, }
166
167#[derive(Clone, Debug, Eq, PartialEq)]
174pub enum MapValueError {
175 EmptyKey {
176 index: usize,
177 },
178 NonScalarKey {
179 index: usize,
180 key: Value,
181 },
182 NonScalarValue {
183 index: usize,
184 value: Value,
185 },
186 DuplicateKey {
187 left_index: usize,
188 right_index: usize,
189 },
190}
191
192impl std::fmt::Display for MapValueError {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 match self {
195 Self::EmptyKey { index } => write!(f, "map key at index {index} must be non-null"),
196 Self::NonScalarKey { index, key } => {
197 write!(f, "map key at index {index} is not scalar: {key:?}")
198 }
199 Self::NonScalarValue { index, value } => {
200 write!(
201 f,
202 "map value at index {index} is not scalar/ref-like: {value:?}"
203 )
204 }
205 Self::DuplicateKey {
206 left_index,
207 right_index,
208 } => write!(
209 f,
210 "map contains duplicate keys at normalized positions {left_index} and {right_index}"
211 ),
212 }
213 }
214}
215
216impl std::error::Error for MapValueError {}
217
218#[derive(Clone, Debug, Eq, PartialEq)]
225pub enum SchemaInvariantError {
226 InvalidMapValue(MapValueError),
227}
228
229impl std::fmt::Display for SchemaInvariantError {
230 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231 match self {
232 Self::InvalidMapValue(err) => write!(f, "{err}"),
233 }
234 }
235}
236
237impl std::error::Error for SchemaInvariantError {}
238
239impl From<MapValueError> for SchemaInvariantError {
240 fn from(value: MapValueError) -> Self {
241 Self::InvalidMapValue(value)
242 }
243}
244
245#[derive(CandidType, Clone, Eq, PartialEq)]
254pub enum Value {
255 Account(Account),
256 Blob(Vec<u8>),
257 Bool(bool),
258 Date(Date),
259 Decimal(Decimal),
260 Duration(Duration),
261 Enum(ValueEnum),
262 Float32(Float32),
263 Float64(Float64),
264 Int(i64),
265 Int128(Int128),
266 IntBig(Int),
267 List(Vec<Self>),
271 Map(Vec<(Self, Self)>),
278 Null,
279 Principal(Principal),
280 Subaccount(Subaccount),
281 Text(String),
282 Timestamp(Timestamp),
283 Uint(u64),
284 Uint128(Nat128),
285 UintBig(Nat),
286 Ulid(Ulid),
287 Unit,
288}
289
290impl fmt::Debug for Value {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 match self {
293 Self::Account(value) => f.debug_tuple("Account").field(value).finish(),
294 Self::Blob(value) => write!(f, "Blob({} bytes)", value.len()),
295 Self::Bool(value) => f.debug_tuple("Bool").field(value).finish(),
296 Self::Date(value) => f.debug_tuple("Date").field(value).finish(),
297 Self::Decimal(value) => f.debug_tuple("Decimal").field(value).finish(),
298 Self::Duration(value) => f.debug_tuple("Duration").field(value).finish(),
299 Self::Enum(value) => f.debug_tuple("Enum").field(value).finish(),
300 Self::Float32(value) => f.debug_tuple("Float32").field(value).finish(),
301 Self::Float64(value) => f.debug_tuple("Float64").field(value).finish(),
302 Self::Int(value) => f.debug_tuple("Int").field(value).finish(),
303 Self::Int128(value) => f.debug_tuple("Int128").field(value).finish(),
304 Self::IntBig(value) => f.debug_tuple("IntBig").field(value).finish(),
305 Self::List(value) => f.debug_tuple("List").field(value).finish(),
306 Self::Map(value) => f.debug_tuple("Map").field(value).finish(),
307 Self::Null => f.write_str("Null"),
308 Self::Principal(value) => f.debug_tuple("Principal").field(value).finish(),
309 Self::Subaccount(value) => f.debug_tuple("Subaccount").field(value).finish(),
310 Self::Text(value) => f.debug_tuple("Text").field(value).finish(),
311 Self::Timestamp(value) => f.debug_tuple("Timestamp").field(value).finish(),
312 Self::Uint(value) => f.debug_tuple("Uint").field(value).finish(),
313 Self::Uint128(value) => f.debug_tuple("Uint128").field(value).finish(),
314 Self::UintBig(value) => f.debug_tuple("UintBig").field(value).finish(),
315 Self::Ulid(value) => f.debug_tuple("Ulid").field(value).finish(),
316 Self::Unit => f.write_str("Unit"),
317 }
318 }
319}
320
321impl FieldTypeMeta for Value {
322 const KIND: FieldKind = FieldKind::Structured { queryable: false };
323 const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
324}
325
326impl Value {
327 pub const __KIND: FieldKind = FieldKind::Structured { queryable: false };
328 pub const __STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
329}
330
331macro_rules! value_is_numeric_from_registry {
333 ( @args $value:expr; @entries $( ($scalar:ident, $coercion_family:expr, $value_pat:pat, is_numeric_value = $is_numeric:expr, supports_numeric_coercion = $supports_numeric_coercion:expr, supports_arithmetic = $supports_arithmetic:expr, supports_equality = $supports_equality:expr, supports_ordering = $supports_ordering:expr, is_keyable = $is_keyable:expr, is_storage_key_encodable = $is_storage_key_encodable:expr) ),* $(,)? ) => {
334 match $value {
335 $( $value_pat => $is_numeric, )*
336 _ => false,
337 }
338 };
339}
340
341macro_rules! value_supports_numeric_coercion_from_registry {
342 ( @args $value:expr; @entries $( ($scalar:ident, $coercion_family:expr, $value_pat:pat, is_numeric_value = $is_numeric:expr, supports_numeric_coercion = $supports_numeric_coercion:expr, supports_arithmetic = $supports_arithmetic:expr, supports_equality = $supports_equality:expr, supports_ordering = $supports_ordering:expr, is_keyable = $is_keyable:expr, is_storage_key_encodable = $is_storage_key_encodable:expr) ),* $(,)? ) => {
343 match $value {
344 $( $value_pat => $supports_numeric_coercion, )*
345 _ => false,
346 }
347 };
348}
349
350macro_rules! value_storage_key_case {
351 ( $value:expr, Unit, true ) => {
352 if let Value::Unit = $value {
353 Some(StorageKey::Unit)
354 } else {
355 None
356 }
357 };
358 ( $value:expr, $scalar:ident, true ) => {
359 if let Value::$scalar(v) = $value {
360 Some(StorageKey::$scalar(*v))
361 } else {
362 None
363 }
364 };
365 ( $value:expr, $scalar:ident, false ) => {
366 None
367 };
368}
369
370macro_rules! value_storage_key_from_registry {
371 ( @args $value:expr; @entries $( ($scalar:ident, $coercion_family:expr, $value_pat:pat, is_numeric_value = $is_numeric:expr, supports_numeric_coercion = $supports_numeric_coercion:expr, supports_arithmetic = $supports_arithmetic:expr, supports_equality = $supports_equality:expr, supports_ordering = $supports_ordering:expr, is_keyable = $is_keyable:tt, is_storage_key_encodable = $is_storage_key_encodable:tt) ),* $(,)? ) => {
372 {
373 let mut key = None;
374 $(
375 match key {
376 Some(_) => {}
377 None => {
378 key = value_storage_key_case!($value, $scalar, $is_storage_key_encodable);
379 }
380 }
381 )*
382 key
383 }
384 };
385}
386
387macro_rules! value_coercion_family_from_registry {
388 ( @args $value:expr; @entries $( ($scalar:ident, $coercion_family:expr, $value_pat:pat, is_numeric_value = $is_numeric:expr, supports_numeric_coercion = $supports_numeric_coercion:expr, supports_arithmetic = $supports_arithmetic:expr, supports_equality = $supports_equality:expr, supports_ordering = $supports_ordering:expr, is_keyable = $is_keyable:expr, is_storage_key_encodable = $is_storage_key_encodable:expr) ),* $(,)? ) => {
389 match $value {
390 $( $value_pat => $coercion_family, )*
391 Value::List(_) => CoercionFamily::Collection,
392 Value::Map(_) => CoercionFamily::Collection,
393 Value::Null => CoercionFamily::Null,
394 }
395 };
396}
397
398impl Value {
399 pub fn from_slice<T>(items: &[T]) -> Self
408 where
409 T: Into<Self> + Clone,
410 {
411 Self::List(items.iter().cloned().map(Into::into).collect())
412 }
413
414 pub fn from_list<T>(items: Vec<T>) -> Self
418 where
419 T: Into<Self>,
420 {
421 Self::List(items.into_iter().map(Into::into).collect())
422 }
423
424 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
432 let normalized = Self::normalize_map_entries(entries)?;
433 Ok(Self::Map(normalized))
434 }
435
436 pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
438 for (index, (key, _value)) in entries.iter().enumerate() {
439 if matches!(key, Self::Null) {
440 return Err(MapValueError::EmptyKey { index });
441 }
442 if !key.is_scalar() {
443 return Err(MapValueError::NonScalarKey {
444 index,
445 key: key.clone(),
446 });
447 }
448 }
449
450 Ok(())
451 }
452
453 pub(crate) fn compare_map_entry_keys(left: &(Self, Self), right: &(Self, Self)) -> Ordering {
455 Self::canonical_cmp_key(&left.0, &right.0)
456 }
457
458 pub(crate) fn sort_map_entries_in_place(entries: &mut [(Self, Self)]) {
460 entries.sort_by(Self::compare_map_entry_keys);
461 }
462
463 pub(crate) fn map_entries_are_strictly_canonical(entries: &[(Self, Self)]) -> bool {
466 entries.windows(2).all(|pair| {
467 let [left, right] = pair else {
468 return true;
469 };
470
471 Self::compare_map_entry_keys(left, right) == Ordering::Less
472 })
473 }
474
475 pub fn normalize_map_entries(
477 mut entries: Vec<(Self, Self)>,
478 ) -> Result<Vec<(Self, Self)>, MapValueError> {
479 Self::validate_map_entries(&entries)?;
480 Self::sort_map_entries_in_place(entries.as_mut_slice());
481
482 for i in 1..entries.len() {
483 let (left_key, _) = &entries[i - 1];
484 let (right_key, _) = &entries[i];
485 if Self::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
486 return Err(MapValueError::DuplicateKey {
487 left_index: i - 1,
488 right_index: i,
489 });
490 }
491 }
492
493 Ok(entries)
494 }
495
496 pub fn from_enum<E: EnumValue>(value: E) -> Self {
498 Self::Enum(value.to_value_enum())
499 }
500
501 #[must_use]
503 pub fn enum_strict<E: Path>(variant: &str) -> Self {
504 Self::Enum(ValueEnum::strict::<E>(variant))
505 }
506
507 #[must_use]
514 pub const fn is_numeric(&self) -> bool {
515 scalar_registry!(value_is_numeric_from_registry, self)
516 }
517
518 #[must_use]
520 pub const fn supports_numeric_coercion(&self) -> bool {
521 scalar_registry!(value_supports_numeric_coercion_from_registry, self)
522 }
523
524 #[must_use]
526 pub const fn is_text(&self) -> bool {
527 matches!(self, Self::Text(_))
528 }
529
530 #[must_use]
532 pub const fn is_unit(&self) -> bool {
533 matches!(self, Self::Unit)
534 }
535
536 #[must_use]
537 pub const fn is_scalar(&self) -> bool {
538 match self {
539 Self::List(_) | Self::Map(_) | Self::Unit => false,
541 _ => true,
542 }
543 }
544
545 #[must_use]
547 pub(crate) const fn canonical_tag(&self) -> ValueTag {
548 tag::canonical_tag(self)
549 }
550
551 #[must_use]
553 pub(crate) const fn canonical_rank(&self) -> u8 {
554 rank::canonical_rank(self)
555 }
556
557 #[must_use]
559 pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
560 compare::canonical_cmp(left, right)
561 }
562
563 #[must_use]
565 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
566 compare::canonical_cmp_key(left, right)
567 }
568
569 #[must_use]
574 pub(crate) fn canonical_cmp_map_entry(
575 left_key: &Self,
576 left_value: &Self,
577 right_key: &Self,
578 right_value: &Self,
579 ) -> Ordering {
580 Self::canonical_cmp_key(left_key, right_key)
581 .then_with(|| Self::canonical_cmp(left_value, right_value))
582 }
583
584 #[must_use]
587 pub(crate) fn ordered_map_entries(entries: &[(Self, Self)]) -> Vec<&(Self, Self)> {
588 let mut ordered = entries.iter().collect::<Vec<_>>();
589 ordered.sort_by(|left, right| {
590 Self::canonical_cmp_map_entry(&left.0, &left.1, &right.0, &right.1)
591 });
592
593 ordered
594 }
595
596 #[must_use]
600 pub(crate) fn strict_order_cmp(left: &Self, right: &Self) -> Option<Ordering> {
601 compare::strict_order_cmp(left, right)
602 }
603
604 fn numeric_repr(&self) -> NumericRepr {
605 if !self.supports_numeric_coercion() {
607 return NumericRepr::None;
608 }
609
610 if let Some(d) = self.to_decimal() {
611 return NumericRepr::Decimal(d);
612 }
613 if let Some(f) = self.to_f64_lossless() {
614 return NumericRepr::F64(f);
615 }
616 NumericRepr::None
617 }
618
619 #[must_use]
628 pub const fn as_storage_key(&self) -> Option<StorageKey> {
629 scalar_registry!(value_storage_key_from_registry, self)
630 }
631
632 #[must_use]
633 pub const fn as_text(&self) -> Option<&str> {
634 if let Self::Text(s) = self {
635 Some(s.as_str())
636 } else {
637 None
638 }
639 }
640
641 #[must_use]
642 pub const fn as_list(&self) -> Option<&[Self]> {
643 if let Self::List(xs) = self {
644 Some(xs.as_slice())
645 } else {
646 None
647 }
648 }
649
650 #[must_use]
651 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
652 if let Self::Map(entries) = self {
653 Some(entries.as_slice())
654 } else {
655 None
656 }
657 }
658
659 fn to_decimal(&self) -> Option<Decimal> {
660 match self {
661 Self::Decimal(d) => d.try_to_decimal(),
662 Self::Duration(d) => d.try_to_decimal(),
663 Self::Float64(f) => f.try_to_decimal(),
664 Self::Float32(f) => f.try_to_decimal(),
665 Self::Int(i) => i.try_to_decimal(),
666 Self::Int128(i) => i.try_to_decimal(),
667 Self::IntBig(i) => i.try_to_decimal(),
668 Self::Timestamp(t) => t.try_to_decimal(),
669 Self::Uint(u) => u.try_to_decimal(),
670 Self::Uint128(u) => u.try_to_decimal(),
671 Self::UintBig(u) => u.try_to_decimal(),
672
673 _ => None,
674 }
675 }
676
677 pub(crate) fn to_numeric_decimal(&self) -> Option<Decimal> {
679 self.to_decimal()
680 }
681
682 #[expect(clippy::cast_precision_loss)]
684 fn to_f64_lossless(&self) -> Option<f64> {
685 match self {
686 Self::Duration(d) if d.repr() <= F64_SAFE_U64 => Some(d.repr() as f64),
687 Self::Float64(f) => Some(f.get()),
688 Self::Float32(f) => Some(f64::from(f.get())),
689 Self::Int(i) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(i) => Some(*i as f64),
690 Self::Int128(i) if (-F64_SAFE_I128..=F64_SAFE_I128).contains(&i.get()) => {
691 Some(i.get() as f64)
692 }
693 Self::IntBig(i) => i.to_i128().and_then(|v| {
694 (-F64_SAFE_I128..=F64_SAFE_I128)
695 .contains(&v)
696 .then_some(v as f64)
697 }),
698 Self::Timestamp(t) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(&t.repr()) => {
699 Some(t.repr() as f64)
700 }
701 Self::Uint(u) if *u <= F64_SAFE_U64 => Some(*u as f64),
702 Self::Uint128(u) if u.get() <= F64_SAFE_U128 => Some(u.get() as f64),
703 Self::UintBig(u) => u
704 .to_u128()
705 .and_then(|v| (v <= F64_SAFE_U128).then_some(v as f64)),
706
707 _ => None,
708 }
709 }
710
711 #[must_use]
717 pub fn cmp_numeric(&self, other: &Self) -> Option<Ordering> {
718 if !self.supports_numeric_coercion() || !other.supports_numeric_coercion() {
719 return None;
720 }
721
722 match (self.numeric_repr(), other.numeric_repr()) {
723 (NumericRepr::Decimal(a), NumericRepr::Decimal(b)) => a.partial_cmp(&b),
724 (NumericRepr::F64(a), NumericRepr::F64(b)) => a.partial_cmp(&b),
725 _ => None,
726 }
727 }
728
729 fn fold_ci(s: &str) -> std::borrow::Cow<'_, str> {
734 if s.is_ascii() {
735 return std::borrow::Cow::Owned(s.to_ascii_lowercase());
736 }
737 std::borrow::Cow::Owned(s.to_lowercase())
740 }
741
742 fn text_with_mode(s: &'_ str, mode: TextMode) -> std::borrow::Cow<'_, str> {
743 match mode {
744 TextMode::Cs => std::borrow::Cow::Borrowed(s),
745 TextMode::Ci => Self::fold_ci(s),
746 }
747 }
748
749 fn text_op(
750 &self,
751 other: &Self,
752 mode: TextMode,
753 f: impl Fn(&str, &str) -> bool,
754 ) -> Option<bool> {
755 let (a, b) = (self.as_text()?, other.as_text()?);
756 let a = Self::text_with_mode(a, mode);
757 let b = Self::text_with_mode(b, mode);
758 Some(f(&a, &b))
759 }
760
761 fn ci_key(&self) -> Option<String> {
762 match self {
763 Self::Text(s) => Some(Self::fold_ci(s).into_owned()),
764 Self::Ulid(u) => Some(u.to_string().to_ascii_lowercase()),
765 Self::Principal(p) => Some(p.to_string().to_ascii_lowercase()),
766 Self::Account(a) => Some(a.to_string().to_ascii_lowercase()),
767 _ => None,
768 }
769 }
770
771 fn eq_ci(a: &Self, b: &Self) -> bool {
772 if let (Some(ak), Some(bk)) = (a.ci_key(), b.ci_key()) {
773 return ak == bk;
774 }
775
776 a == b
777 }
778
779 fn normalize_list_ref(v: &Self) -> Vec<&Self> {
780 match v {
781 Self::List(vs) => vs.iter().collect(),
782 v => vec![v],
783 }
784 }
785
786 fn contains_by<F>(&self, needle: &Self, eq: F) -> Option<bool>
787 where
788 F: Fn(&Self, &Self) -> bool,
789 {
790 self.as_list()
791 .map(|items| items.iter().any(|v| eq(v, needle)))
792 }
793
794 #[expect(clippy::unnecessary_wraps)]
795 fn contains_any_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
796 where
797 F: Fn(&Self, &Self) -> bool,
798 {
799 let needles = Self::normalize_list_ref(needles);
800 match self {
801 Self::List(items) => Some(needles.iter().any(|n| items.iter().any(|v| eq(v, n)))),
802 scalar => Some(needles.iter().any(|n| eq(scalar, n))),
803 }
804 }
805
806 #[expect(clippy::unnecessary_wraps)]
807 fn contains_all_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
808 where
809 F: Fn(&Self, &Self) -> bool,
810 {
811 let needles = Self::normalize_list_ref(needles);
812 match self {
813 Self::List(items) => Some(needles.iter().all(|n| items.iter().any(|v| eq(v, n)))),
814 scalar => Some(needles.len() == 1 && eq(scalar, needles[0])),
815 }
816 }
817
818 fn in_list_by<F>(&self, haystack: &Self, eq: F) -> Option<bool>
819 where
820 F: Fn(&Self, &Self) -> bool,
821 {
822 if let Self::List(items) = haystack {
823 Some(items.iter().any(|h| eq(h, self)))
824 } else {
825 None
826 }
827 }
828
829 #[must_use]
831 pub fn text_eq(&self, other: &Self, mode: TextMode) -> Option<bool> {
832 self.text_op(other, mode, |a, b| a == b)
833 }
834
835 #[must_use]
837 pub fn text_contains(&self, needle: &Self, mode: TextMode) -> Option<bool> {
838 self.text_op(needle, mode, |a, b| a.contains(b))
839 }
840
841 #[must_use]
843 pub fn text_starts_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
844 self.text_op(needle, mode, |a, b| a.starts_with(b))
845 }
846
847 #[must_use]
849 pub fn text_ends_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
850 self.text_op(needle, mode, |a, b| a.ends_with(b))
851 }
852
853 #[must_use]
858 pub const fn is_empty(&self) -> Option<bool> {
859 match self {
860 Self::List(xs) => Some(xs.is_empty()),
861 Self::Map(entries) => Some(entries.is_empty()),
862 Self::Text(s) => Some(s.is_empty()),
863 Self::Blob(b) => Some(b.is_empty()),
864
865 Self::Null => Some(true),
867
868 _ => None,
869 }
870 }
871
872 #[must_use]
874 pub fn is_not_empty(&self) -> Option<bool> {
875 self.is_empty().map(|b| !b)
876 }
877
878 #[must_use]
884 pub fn contains(&self, needle: &Self) -> Option<bool> {
885 self.contains_by(needle, |a, b| a == b)
886 }
887
888 #[must_use]
890 pub fn contains_any(&self, needles: &Self) -> Option<bool> {
891 self.contains_any_by(needles, |a, b| a == b)
892 }
893
894 #[must_use]
896 pub fn contains_all(&self, needles: &Self) -> Option<bool> {
897 self.contains_all_by(needles, |a, b| a == b)
898 }
899
900 #[must_use]
902 pub fn in_list(&self, haystack: &Self) -> Option<bool> {
903 self.in_list_by(haystack, |a, b| a == b)
904 }
905
906 #[must_use]
908 pub fn contains_ci(&self, needle: &Self) -> Option<bool> {
909 match self {
910 Self::List(_) => self.contains_by(needle, Self::eq_ci),
911 _ => Some(Self::eq_ci(self, needle)),
912 }
913 }
914
915 #[must_use]
917 pub fn contains_any_ci(&self, needles: &Self) -> Option<bool> {
918 self.contains_any_by(needles, Self::eq_ci)
919 }
920
921 #[must_use]
923 pub fn contains_all_ci(&self, needles: &Self) -> Option<bool> {
924 self.contains_all_by(needles, Self::eq_ci)
925 }
926
927 #[must_use]
929 pub fn in_list_ci(&self, haystack: &Self) -> Option<bool> {
930 self.in_list_by(haystack, Self::eq_ci)
931 }
932}
933
934impl RuntimeValueMeta for Value {
935 fn kind() -> crate::traits::RuntimeValueKind {
936 crate::traits::RuntimeValueKind::Atomic
937 }
938}
939
940impl RuntimeValueEncode for Value {
941 fn to_value(&self) -> Value {
942 self.clone()
943 }
944}
945
946impl RuntimeValueDecode for Value {
947 fn from_value(value: &Value) -> Option<Self> {
948 Some(value.clone())
949 }
950}
951
952#[macro_export]
953macro_rules! impl_from_for {
954 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
955 $(
956 impl From<$type> for Value {
957 fn from(v: $type) -> Self {
958 Self::$variant(v.into())
959 }
960 }
961 )*
962 };
963}
964
965impl_from_for! {
966 Account => Account,
967 Date => Date,
968 Decimal => Decimal,
969 Duration => Duration,
970 bool => Bool,
971 i8 => Int,
972 i16 => Int,
973 i32 => Int,
974 i64 => Int,
975 i128 => Int128,
976 Int => IntBig,
977 Principal => Principal,
978 Subaccount => Subaccount,
979 &str => Text,
980 String => Text,
981 Timestamp => Timestamp,
982 u8 => Uint,
983 u16 => Uint,
984 u32 => Uint,
985 u64 => Uint,
986 u128 => Uint128,
987 Nat => UintBig,
988 Ulid => Ulid,
989}
990
991impl CoercionFamilyExt for Value {
992 fn coercion_family(&self) -> CoercionFamily {
998 scalar_registry!(value_coercion_family_from_registry, self)
999 }
1000}
1001
1002impl From<Vec<Self>> for Value {
1003 fn from(vec: Vec<Self>) -> Self {
1004 Self::List(vec)
1005 }
1006}
1007
1008impl TryFrom<Vec<(Self, Self)>> for Value {
1009 type Error = SchemaInvariantError;
1010
1011 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
1012 Self::from_map(entries).map_err(Self::Error::from)
1013 }
1014}
1015
1016impl From<()> for Value {
1017 fn from((): ()) -> Self {
1018 Self::Unit
1019 }
1020}
1021
1022impl PartialOrd for Value {
1028 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1029 match (self, other) {
1030 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
1031 (Self::Date(a), Self::Date(b)) => a.partial_cmp(b),
1032 (Self::Decimal(a), Self::Decimal(b)) => a.partial_cmp(b),
1033 (Self::Duration(a), Self::Duration(b)) => a.partial_cmp(b),
1034 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b),
1035 (Self::Float32(a), Self::Float32(b)) => a.partial_cmp(b),
1036 (Self::Float64(a), Self::Float64(b)) => a.partial_cmp(b),
1037 (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
1038 (Self::Int128(a), Self::Int128(b)) => a.partial_cmp(b),
1039 (Self::IntBig(a), Self::IntBig(b)) => a.partial_cmp(b),
1040 (Self::Principal(a), Self::Principal(b)) => a.partial_cmp(b),
1041 (Self::Subaccount(a), Self::Subaccount(b)) => a.partial_cmp(b),
1042 (Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
1043 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
1044 (Self::Uint(a), Self::Uint(b)) => a.partial_cmp(b),
1045 (Self::Uint128(a), Self::Uint128(b)) => a.partial_cmp(b),
1046 (Self::UintBig(a), Self::UintBig(b)) => a.partial_cmp(b),
1047 (Self::Ulid(a), Self::Ulid(b)) => a.partial_cmp(b),
1048 (Self::Map(a), Self::Map(b)) => {
1049 for ((left_key, left_value), (right_key, right_value)) in a.iter().zip(b.iter()) {
1050 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
1051 if key_cmp != Ordering::Equal {
1052 return Some(key_cmp);
1053 }
1054
1055 match left_value.partial_cmp(right_value) {
1056 Some(Ordering::Equal) => {}
1057 non_eq => return non_eq,
1058 }
1059 }
1060 a.len().partial_cmp(&b.len())
1061 }
1062
1063 _ => None,
1065 }
1066 }
1067}
1068
1069#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd)]
1075pub struct ValueEnum {
1076 variant: String,
1077 path: Option<String>,
1078 payload: Option<Box<Value>>,
1079}
1080
1081impl ValueEnum {
1082 #[must_use]
1084 pub fn new(variant: &str, path: Option<&str>) -> Self {
1085 Self {
1086 variant: variant.to_string(),
1087 path: path.map(ToString::to_string),
1088 payload: None,
1089 }
1090 }
1091
1092 #[must_use]
1094 pub fn strict<E: Path>(variant: &str) -> Self {
1095 Self::new(variant, Some(E::PATH))
1096 }
1097
1098 #[must_use]
1100 pub fn from_enum<E: EnumValue>(value: E) -> Self {
1101 value.to_value_enum()
1102 }
1103
1104 #[must_use]
1107 pub fn loose(variant: &str) -> Self {
1108 Self::new(variant, None)
1109 }
1110
1111 #[must_use]
1113 pub fn with_payload(mut self, payload: Value) -> Self {
1114 self.payload = Some(Box::new(payload));
1115 self
1116 }
1117
1118 #[must_use]
1119 pub fn variant(&self) -> &str {
1120 &self.variant
1121 }
1122
1123 #[must_use]
1124 pub fn path(&self) -> Option<&str> {
1125 self.path.as_deref()
1126 }
1127
1128 #[must_use]
1129 pub fn payload(&self) -> Option<&Value> {
1130 self.payload.as_deref()
1131 }
1132
1133 pub(crate) fn set_path(&mut self, path: Option<String>) {
1134 self.path = path;
1135 }
1136}