1mod coercion;
8mod compare;
9mod hash;
10mod input;
11mod output;
12mod rank;
13mod storage_key;
14mod tag;
15mod wire;
16
17#[cfg(test)]
18mod tests;
19
20use crate::{
21 model::field::{FieldKind, FieldStorageDecode},
22 prelude::*,
23 traits::{EnumValue, FieldTypeMeta, NumericValue, Repr, ValueCodec, ValueSurfaceMeta},
24 types::*,
25};
26use candid::CandidType;
27use serde::Deserialize;
28use std::{cmp::Ordering, fmt};
29
30pub use coercion::{CoercionFamily, CoercionFamilyExt};
32#[cfg(test)]
33pub(crate) use hash::with_test_hash_override;
34pub(crate) use hash::{ValueHashWriter, hash_single_list_identity_canonical_value, hash_value};
35pub use input::{InputValue, InputValueEnum};
36pub use output::{OutputValue, OutputValueEnum};
37pub use storage_key::{StorageKey, StorageKeyDecodeError, StorageKeyEncodeError};
38pub use tag::ValueTag;
39
40const F64_SAFE_I64: i64 = 1i64 << 53;
45const F64_SAFE_U64: u64 = 1u64 << 53;
46const F64_SAFE_I128: i128 = 1i128 << 53;
47const F64_SAFE_U128: u128 = 1u128 << 53;
48pub(crate) const VALUE_WIRE_TYPE_NAME: &str = "Value";
49pub(crate) const VALUE_WIRE_VARIANT_LABELS: &[&str] = &[
50 "Account",
51 "Blob",
52 "Bool",
53 "Date",
54 "Decimal",
55 "Duration",
56 "Enum",
57 "Float32",
58 "Float64",
59 "Int",
60 "Int128",
61 "IntBig",
62 "List",
63 "Map",
64 "Null",
65 "Principal",
66 "Subaccount",
67 "Text",
68 "Timestamp",
69 "Uint",
70 "Uint128",
71 "UintBig",
72 "Ulid",
73 "Unit",
74];
75
76enum NumericRepr {
81 Decimal(Decimal),
82 F64(f64),
83 None,
84}
85
86#[derive(Clone, Copy)]
88pub(crate) enum ValueWireVariant {
89 Account,
90 Blob,
91 Bool,
92 Date,
93 Decimal,
94 Duration,
95 Enum,
96 Float32,
97 Float64,
98 Int,
99 Int128,
100 IntBig,
101 List,
102 Map,
103 Null,
104 Principal,
105 Subaccount,
106 Text,
107 Timestamp,
108 Uint,
109 Uint128,
110 UintBig,
111 Ulid,
112 Unit,
113}
114
115impl ValueWireVariant {
116 pub(crate) fn from_label(label: &str) -> Option<Self> {
118 match label {
119 "Account" => Some(Self::Account),
120 "Blob" => Some(Self::Blob),
121 "Bool" => Some(Self::Bool),
122 "Date" => Some(Self::Date),
123 "Decimal" => Some(Self::Decimal),
124 "Duration" => Some(Self::Duration),
125 "Enum" => Some(Self::Enum),
126 "Float32" => Some(Self::Float32),
127 "Float64" => Some(Self::Float64),
128 "Int" => Some(Self::Int),
129 "Int128" => Some(Self::Int128),
130 "IntBig" => Some(Self::IntBig),
131 "List" => Some(Self::List),
132 "Map" => Some(Self::Map),
133 "Null" => Some(Self::Null),
134 "Principal" => Some(Self::Principal),
135 "Subaccount" => Some(Self::Subaccount),
136 "Text" => Some(Self::Text),
137 "Timestamp" => Some(Self::Timestamp),
138 "Uint" => Some(Self::Uint),
139 "Uint128" => Some(Self::Uint128),
140 "UintBig" => Some(Self::UintBig),
141 "Ulid" => Some(Self::Ulid),
142 "Unit" => Some(Self::Unit),
143 _ => None,
144 }
145 }
146}
147
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
153pub enum TextMode {
154 Cs, Ci, }
157
158#[derive(Clone, Debug, Eq, PartialEq)]
165pub enum MapValueError {
166 EmptyKey {
167 index: usize,
168 },
169 NonScalarKey {
170 index: usize,
171 key: Value,
172 },
173 NonScalarValue {
174 index: usize,
175 value: Value,
176 },
177 DuplicateKey {
178 left_index: usize,
179 right_index: usize,
180 },
181}
182
183impl std::fmt::Display for MapValueError {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 match self {
186 Self::EmptyKey { index } => write!(f, "map key at index {index} must be non-null"),
187 Self::NonScalarKey { index, key } => {
188 write!(f, "map key at index {index} is not scalar: {key:?}")
189 }
190 Self::NonScalarValue { index, value } => {
191 write!(
192 f,
193 "map value at index {index} is not scalar/ref-like: {value:?}"
194 )
195 }
196 Self::DuplicateKey {
197 left_index,
198 right_index,
199 } => write!(
200 f,
201 "map contains duplicate keys at normalized positions {left_index} and {right_index}"
202 ),
203 }
204 }
205}
206
207impl std::error::Error for MapValueError {}
208
209#[derive(Clone, Debug, Eq, PartialEq)]
216pub enum SchemaInvariantError {
217 InvalidMapValue(MapValueError),
218}
219
220impl std::fmt::Display for SchemaInvariantError {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 match self {
223 Self::InvalidMapValue(err) => write!(f, "{err}"),
224 }
225 }
226}
227
228impl std::error::Error for SchemaInvariantError {}
229
230impl From<MapValueError> for SchemaInvariantError {
231 fn from(value: MapValueError) -> Self {
232 Self::InvalidMapValue(value)
233 }
234}
235
236#[derive(CandidType, Clone, Eq, PartialEq)]
245pub enum Value {
246 Account(Account),
247 Blob(Vec<u8>),
248 Bool(bool),
249 Date(Date),
250 Decimal(Decimal),
251 Duration(Duration),
252 Enum(ValueEnum),
253 Float32(Float32),
254 Float64(Float64),
255 Int(i64),
256 Int128(Int128),
257 IntBig(Int),
258 List(Vec<Self>),
262 Map(Vec<(Self, Self)>),
269 Null,
270 Principal(Principal),
271 Subaccount(Subaccount),
272 Text(String),
273 Timestamp(Timestamp),
274 Uint(u64),
275 Uint128(Nat128),
276 UintBig(Nat),
277 Ulid(Ulid),
278 Unit,
279}
280
281impl fmt::Debug for Value {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 match self {
284 Self::Account(value) => f.debug_tuple("Account").field(value).finish(),
285 Self::Blob(value) => write!(f, "Blob({} bytes)", value.len()),
286 Self::Bool(value) => f.debug_tuple("Bool").field(value).finish(),
287 Self::Date(value) => f.debug_tuple("Date").field(value).finish(),
288 Self::Decimal(value) => f.debug_tuple("Decimal").field(value).finish(),
289 Self::Duration(value) => f.debug_tuple("Duration").field(value).finish(),
290 Self::Enum(value) => f.debug_tuple("Enum").field(value).finish(),
291 Self::Float32(value) => f.debug_tuple("Float32").field(value).finish(),
292 Self::Float64(value) => f.debug_tuple("Float64").field(value).finish(),
293 Self::Int(value) => f.debug_tuple("Int").field(value).finish(),
294 Self::Int128(value) => f.debug_tuple("Int128").field(value).finish(),
295 Self::IntBig(value) => f.debug_tuple("IntBig").field(value).finish(),
296 Self::List(value) => f.debug_tuple("List").field(value).finish(),
297 Self::Map(value) => f.debug_tuple("Map").field(value).finish(),
298 Self::Null => f.write_str("Null"),
299 Self::Principal(value) => f.debug_tuple("Principal").field(value).finish(),
300 Self::Subaccount(value) => f.debug_tuple("Subaccount").field(value).finish(),
301 Self::Text(value) => f.debug_tuple("Text").field(value).finish(),
302 Self::Timestamp(value) => f.debug_tuple("Timestamp").field(value).finish(),
303 Self::Uint(value) => f.debug_tuple("Uint").field(value).finish(),
304 Self::Uint128(value) => f.debug_tuple("Uint128").field(value).finish(),
305 Self::UintBig(value) => f.debug_tuple("UintBig").field(value).finish(),
306 Self::Ulid(value) => f.debug_tuple("Ulid").field(value).finish(),
307 Self::Unit => f.write_str("Unit"),
308 }
309 }
310}
311
312impl FieldTypeMeta for Value {
313 const KIND: FieldKind = FieldKind::Structured { queryable: false };
314 const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
315}
316
317impl Value {
318 pub const __KIND: FieldKind = FieldKind::Structured { queryable: false };
319 pub const __STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
320}
321
322macro_rules! value_is_numeric_from_registry {
324 ( @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) ),* $(,)? ) => {
325 match $value {
326 $( $value_pat => $is_numeric, )*
327 _ => false,
328 }
329 };
330}
331
332macro_rules! value_supports_numeric_coercion_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 => $supports_numeric_coercion, )*
336 _ => false,
337 }
338 };
339}
340
341macro_rules! value_storage_key_case {
342 ( $value:expr, Unit, true ) => {
343 if let Value::Unit = $value {
344 Some(StorageKey::Unit)
345 } else {
346 None
347 }
348 };
349 ( $value:expr, $scalar:ident, true ) => {
350 if let Value::$scalar(v) = $value {
351 Some(StorageKey::$scalar(*v))
352 } else {
353 None
354 }
355 };
356 ( $value:expr, $scalar:ident, false ) => {
357 None
358 };
359}
360
361macro_rules! value_storage_key_from_registry {
362 ( @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) ),* $(,)? ) => {
363 {
364 let mut key = None;
365 $(
366 match key {
367 Some(_) => {}
368 None => {
369 key = value_storage_key_case!($value, $scalar, $is_storage_key_encodable);
370 }
371 }
372 )*
373 key
374 }
375 };
376}
377
378macro_rules! value_coercion_family_from_registry {
379 ( @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) ),* $(,)? ) => {
380 match $value {
381 $( $value_pat => $coercion_family, )*
382 Value::List(_) => CoercionFamily::Collection,
383 Value::Map(_) => CoercionFamily::Collection,
384 Value::Null => CoercionFamily::Null,
385 }
386 };
387}
388
389impl Value {
390 pub fn from_slice<T>(items: &[T]) -> Self
399 where
400 T: Into<Self> + Clone,
401 {
402 Self::List(items.iter().cloned().map(Into::into).collect())
403 }
404
405 pub fn from_list<T>(items: Vec<T>) -> Self
409 where
410 T: Into<Self>,
411 {
412 Self::List(items.into_iter().map(Into::into).collect())
413 }
414
415 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
423 let normalized = Self::normalize_map_entries(entries)?;
424 Ok(Self::Map(normalized))
425 }
426
427 pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
429 for (index, (key, _value)) in entries.iter().enumerate() {
430 if matches!(key, Self::Null) {
431 return Err(MapValueError::EmptyKey { index });
432 }
433 if !key.is_scalar() {
434 return Err(MapValueError::NonScalarKey {
435 index,
436 key: key.clone(),
437 });
438 }
439 }
440
441 Ok(())
442 }
443
444 pub(crate) fn compare_map_entry_keys(left: &(Self, Self), right: &(Self, Self)) -> Ordering {
446 Self::canonical_cmp_key(&left.0, &right.0)
447 }
448
449 pub(crate) fn sort_map_entries_in_place(entries: &mut [(Self, Self)]) {
451 entries.sort_by(Self::compare_map_entry_keys);
452 }
453
454 pub(crate) fn map_entries_are_strictly_canonical(entries: &[(Self, Self)]) -> bool {
457 entries.windows(2).all(|pair| {
458 let [left, right] = pair else {
459 return true;
460 };
461
462 Self::compare_map_entry_keys(left, right) == Ordering::Less
463 })
464 }
465
466 pub fn normalize_map_entries(
468 mut entries: Vec<(Self, Self)>,
469 ) -> Result<Vec<(Self, Self)>, MapValueError> {
470 Self::validate_map_entries(&entries)?;
471 Self::sort_map_entries_in_place(entries.as_mut_slice());
472
473 for i in 1..entries.len() {
474 let (left_key, _) = &entries[i - 1];
475 let (right_key, _) = &entries[i];
476 if Self::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
477 return Err(MapValueError::DuplicateKey {
478 left_index: i - 1,
479 right_index: i,
480 });
481 }
482 }
483
484 Ok(entries)
485 }
486
487 pub fn from_enum<E: EnumValue>(value: E) -> Self {
489 Self::Enum(value.to_value_enum())
490 }
491
492 #[must_use]
494 pub fn enum_strict<E: Path>(variant: &str) -> Self {
495 Self::Enum(ValueEnum::strict::<E>(variant))
496 }
497
498 #[must_use]
505 pub const fn is_numeric(&self) -> bool {
506 scalar_registry!(value_is_numeric_from_registry, self)
507 }
508
509 #[must_use]
511 pub const fn supports_numeric_coercion(&self) -> bool {
512 scalar_registry!(value_supports_numeric_coercion_from_registry, self)
513 }
514
515 #[must_use]
517 pub const fn is_text(&self) -> bool {
518 matches!(self, Self::Text(_))
519 }
520
521 #[must_use]
523 pub const fn is_unit(&self) -> bool {
524 matches!(self, Self::Unit)
525 }
526
527 #[must_use]
528 pub const fn is_scalar(&self) -> bool {
529 match self {
530 Self::List(_) | Self::Map(_) | Self::Unit => false,
532 _ => true,
533 }
534 }
535
536 #[must_use]
538 pub(crate) const fn canonical_tag(&self) -> ValueTag {
539 tag::canonical_tag(self)
540 }
541
542 #[must_use]
544 pub(crate) const fn canonical_rank(&self) -> u8 {
545 rank::canonical_rank(self)
546 }
547
548 #[must_use]
550 pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
551 compare::canonical_cmp(left, right)
552 }
553
554 #[must_use]
556 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
557 compare::canonical_cmp_key(left, right)
558 }
559
560 #[must_use]
565 pub(crate) fn canonical_cmp_map_entry(
566 left_key: &Self,
567 left_value: &Self,
568 right_key: &Self,
569 right_value: &Self,
570 ) -> Ordering {
571 Self::canonical_cmp_key(left_key, right_key)
572 .then_with(|| Self::canonical_cmp(left_value, right_value))
573 }
574
575 #[must_use]
578 pub(crate) fn ordered_map_entries(entries: &[(Self, Self)]) -> Vec<&(Self, Self)> {
579 let mut ordered = entries.iter().collect::<Vec<_>>();
580 ordered.sort_by(|left, right| {
581 Self::canonical_cmp_map_entry(&left.0, &left.1, &right.0, &right.1)
582 });
583
584 ordered
585 }
586
587 #[must_use]
591 pub(crate) fn strict_order_cmp(left: &Self, right: &Self) -> Option<Ordering> {
592 compare::strict_order_cmp(left, right)
593 }
594
595 fn numeric_repr(&self) -> NumericRepr {
596 if !self.supports_numeric_coercion() {
598 return NumericRepr::None;
599 }
600
601 if let Some(d) = self.to_decimal() {
602 return NumericRepr::Decimal(d);
603 }
604 if let Some(f) = self.to_f64_lossless() {
605 return NumericRepr::F64(f);
606 }
607 NumericRepr::None
608 }
609
610 #[must_use]
619 pub const fn as_storage_key(&self) -> Option<StorageKey> {
620 scalar_registry!(value_storage_key_from_registry, self)
621 }
622
623 #[must_use]
624 pub const fn as_text(&self) -> Option<&str> {
625 if let Self::Text(s) = self {
626 Some(s.as_str())
627 } else {
628 None
629 }
630 }
631
632 #[must_use]
633 pub const fn as_list(&self) -> Option<&[Self]> {
634 if let Self::List(xs) = self {
635 Some(xs.as_slice())
636 } else {
637 None
638 }
639 }
640
641 #[must_use]
642 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
643 if let Self::Map(entries) = self {
644 Some(entries.as_slice())
645 } else {
646 None
647 }
648 }
649
650 fn to_decimal(&self) -> Option<Decimal> {
651 match self {
652 Self::Decimal(d) => d.try_to_decimal(),
653 Self::Duration(d) => d.try_to_decimal(),
654 Self::Float64(f) => f.try_to_decimal(),
655 Self::Float32(f) => f.try_to_decimal(),
656 Self::Int(i) => i.try_to_decimal(),
657 Self::Int128(i) => i.try_to_decimal(),
658 Self::IntBig(i) => i.try_to_decimal(),
659 Self::Timestamp(t) => t.try_to_decimal(),
660 Self::Uint(u) => u.try_to_decimal(),
661 Self::Uint128(u) => u.try_to_decimal(),
662 Self::UintBig(u) => u.try_to_decimal(),
663
664 _ => None,
665 }
666 }
667
668 pub(crate) fn to_numeric_decimal(&self) -> Option<Decimal> {
670 self.to_decimal()
671 }
672
673 #[expect(clippy::cast_precision_loss)]
675 fn to_f64_lossless(&self) -> Option<f64> {
676 match self {
677 Self::Duration(d) if d.repr() <= F64_SAFE_U64 => Some(d.repr() as f64),
678 Self::Float64(f) => Some(f.get()),
679 Self::Float32(f) => Some(f64::from(f.get())),
680 Self::Int(i) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(i) => Some(*i as f64),
681 Self::Int128(i) if (-F64_SAFE_I128..=F64_SAFE_I128).contains(&i.get()) => {
682 Some(i.get() as f64)
683 }
684 Self::IntBig(i) => i.to_i128().and_then(|v| {
685 (-F64_SAFE_I128..=F64_SAFE_I128)
686 .contains(&v)
687 .then_some(v as f64)
688 }),
689 Self::Timestamp(t) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(&t.repr()) => {
690 Some(t.repr() as f64)
691 }
692 Self::Uint(u) if *u <= F64_SAFE_U64 => Some(*u as f64),
693 Self::Uint128(u) if u.get() <= F64_SAFE_U128 => Some(u.get() as f64),
694 Self::UintBig(u) => u
695 .to_u128()
696 .and_then(|v| (v <= F64_SAFE_U128).then_some(v as f64)),
697
698 _ => None,
699 }
700 }
701
702 #[must_use]
704 pub fn cmp_numeric(&self, other: &Self) -> Option<Ordering> {
705 if !self.supports_numeric_coercion() || !other.supports_numeric_coercion() {
706 return None;
707 }
708
709 match (self.numeric_repr(), other.numeric_repr()) {
710 (NumericRepr::Decimal(a), NumericRepr::Decimal(b)) => a.partial_cmp(&b),
711 (NumericRepr::F64(a), NumericRepr::F64(b)) => a.partial_cmp(&b),
712 _ => None,
713 }
714 }
715
716 fn fold_ci(s: &str) -> std::borrow::Cow<'_, str> {
721 if s.is_ascii() {
722 return std::borrow::Cow::Owned(s.to_ascii_lowercase());
723 }
724 std::borrow::Cow::Owned(s.to_lowercase())
727 }
728
729 fn text_with_mode(s: &'_ str, mode: TextMode) -> std::borrow::Cow<'_, str> {
730 match mode {
731 TextMode::Cs => std::borrow::Cow::Borrowed(s),
732 TextMode::Ci => Self::fold_ci(s),
733 }
734 }
735
736 fn text_op(
737 &self,
738 other: &Self,
739 mode: TextMode,
740 f: impl Fn(&str, &str) -> bool,
741 ) -> Option<bool> {
742 let (a, b) = (self.as_text()?, other.as_text()?);
743 let a = Self::text_with_mode(a, mode);
744 let b = Self::text_with_mode(b, mode);
745 Some(f(&a, &b))
746 }
747
748 fn ci_key(&self) -> Option<String> {
749 match self {
750 Self::Text(s) => Some(Self::fold_ci(s).into_owned()),
751 Self::Ulid(u) => Some(u.to_string().to_ascii_lowercase()),
752 Self::Principal(p) => Some(p.to_string().to_ascii_lowercase()),
753 Self::Account(a) => Some(a.to_string().to_ascii_lowercase()),
754 _ => None,
755 }
756 }
757
758 fn eq_ci(a: &Self, b: &Self) -> bool {
759 if let (Some(ak), Some(bk)) = (a.ci_key(), b.ci_key()) {
760 return ak == bk;
761 }
762
763 a == b
764 }
765
766 fn normalize_list_ref(v: &Self) -> Vec<&Self> {
767 match v {
768 Self::List(vs) => vs.iter().collect(),
769 v => vec![v],
770 }
771 }
772
773 fn contains_by<F>(&self, needle: &Self, eq: F) -> Option<bool>
774 where
775 F: Fn(&Self, &Self) -> bool,
776 {
777 self.as_list()
778 .map(|items| items.iter().any(|v| eq(v, needle)))
779 }
780
781 #[expect(clippy::unnecessary_wraps)]
782 fn contains_any_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
783 where
784 F: Fn(&Self, &Self) -> bool,
785 {
786 let needles = Self::normalize_list_ref(needles);
787 match self {
788 Self::List(items) => Some(needles.iter().any(|n| items.iter().any(|v| eq(v, n)))),
789 scalar => Some(needles.iter().any(|n| eq(scalar, n))),
790 }
791 }
792
793 #[expect(clippy::unnecessary_wraps)]
794 fn contains_all_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
795 where
796 F: Fn(&Self, &Self) -> bool,
797 {
798 let needles = Self::normalize_list_ref(needles);
799 match self {
800 Self::List(items) => Some(needles.iter().all(|n| items.iter().any(|v| eq(v, n)))),
801 scalar => Some(needles.len() == 1 && eq(scalar, needles[0])),
802 }
803 }
804
805 fn in_list_by<F>(&self, haystack: &Self, eq: F) -> Option<bool>
806 where
807 F: Fn(&Self, &Self) -> bool,
808 {
809 if let Self::List(items) = haystack {
810 Some(items.iter().any(|h| eq(h, self)))
811 } else {
812 None
813 }
814 }
815
816 #[must_use]
818 pub fn text_eq(&self, other: &Self, mode: TextMode) -> Option<bool> {
819 self.text_op(other, mode, |a, b| a == b)
820 }
821
822 #[must_use]
824 pub fn text_contains(&self, needle: &Self, mode: TextMode) -> Option<bool> {
825 self.text_op(needle, mode, |a, b| a.contains(b))
826 }
827
828 #[must_use]
830 pub fn text_starts_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
831 self.text_op(needle, mode, |a, b| a.starts_with(b))
832 }
833
834 #[must_use]
836 pub fn text_ends_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
837 self.text_op(needle, mode, |a, b| a.ends_with(b))
838 }
839
840 #[must_use]
845 pub const fn is_empty(&self) -> Option<bool> {
846 match self {
847 Self::List(xs) => Some(xs.is_empty()),
848 Self::Map(entries) => Some(entries.is_empty()),
849 Self::Text(s) => Some(s.is_empty()),
850 Self::Blob(b) => Some(b.is_empty()),
851
852 Self::Null => Some(true),
854
855 _ => None,
856 }
857 }
858
859 #[must_use]
861 pub fn is_not_empty(&self) -> Option<bool> {
862 self.is_empty().map(|b| !b)
863 }
864
865 #[must_use]
871 pub fn contains(&self, needle: &Self) -> Option<bool> {
872 self.contains_by(needle, |a, b| a == b)
873 }
874
875 #[must_use]
877 pub fn contains_any(&self, needles: &Self) -> Option<bool> {
878 self.contains_any_by(needles, |a, b| a == b)
879 }
880
881 #[must_use]
883 pub fn contains_all(&self, needles: &Self) -> Option<bool> {
884 self.contains_all_by(needles, |a, b| a == b)
885 }
886
887 #[must_use]
889 pub fn in_list(&self, haystack: &Self) -> Option<bool> {
890 self.in_list_by(haystack, |a, b| a == b)
891 }
892
893 #[must_use]
895 pub fn contains_ci(&self, needle: &Self) -> Option<bool> {
896 match self {
897 Self::List(_) => self.contains_by(needle, Self::eq_ci),
898 _ => Some(Self::eq_ci(self, needle)),
899 }
900 }
901
902 #[must_use]
904 pub fn contains_any_ci(&self, needles: &Self) -> Option<bool> {
905 self.contains_any_by(needles, Self::eq_ci)
906 }
907
908 #[must_use]
910 pub fn contains_all_ci(&self, needles: &Self) -> Option<bool> {
911 self.contains_all_by(needles, Self::eq_ci)
912 }
913
914 #[must_use]
916 pub fn in_list_ci(&self, haystack: &Self) -> Option<bool> {
917 self.in_list_by(haystack, Self::eq_ci)
918 }
919}
920
921impl ValueSurfaceMeta for Value {
922 fn kind() -> crate::traits::ValueSurfaceKind {
923 crate::traits::ValueSurfaceKind::Atomic
924 }
925}
926
927impl ValueCodec for Value {
928 fn to_value(&self) -> Value {
929 self.clone()
930 }
931
932 fn from_value(value: &Value) -> Option<Self> {
933 Some(value.clone())
934 }
935}
936
937#[macro_export]
938macro_rules! impl_from_for {
939 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
940 $(
941 impl From<$type> for Value {
942 fn from(v: $type) -> Self {
943 Self::$variant(v.into())
944 }
945 }
946 )*
947 };
948}
949
950impl_from_for! {
951 Account => Account,
952 Date => Date,
953 Decimal => Decimal,
954 Duration => Duration,
955 bool => Bool,
956 i8 => Int,
957 i16 => Int,
958 i32 => Int,
959 i64 => Int,
960 i128 => Int128,
961 Int => IntBig,
962 Principal => Principal,
963 Subaccount => Subaccount,
964 &str => Text,
965 String => Text,
966 Timestamp => Timestamp,
967 u8 => Uint,
968 u16 => Uint,
969 u32 => Uint,
970 u64 => Uint,
971 u128 => Uint128,
972 Nat => UintBig,
973 Ulid => Ulid,
974}
975
976impl CoercionFamilyExt for Value {
977 fn coercion_family(&self) -> CoercionFamily {
983 scalar_registry!(value_coercion_family_from_registry, self)
984 }
985}
986
987impl From<Vec<Self>> for Value {
988 fn from(vec: Vec<Self>) -> Self {
989 Self::List(vec)
990 }
991}
992
993impl TryFrom<Vec<(Self, Self)>> for Value {
994 type Error = SchemaInvariantError;
995
996 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
997 Self::from_map(entries).map_err(Self::Error::from)
998 }
999}
1000
1001impl From<()> for Value {
1002 fn from((): ()) -> Self {
1003 Self::Unit
1004 }
1005}
1006
1007impl PartialOrd for Value {
1013 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1014 match (self, other) {
1015 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
1016 (Self::Date(a), Self::Date(b)) => a.partial_cmp(b),
1017 (Self::Decimal(a), Self::Decimal(b)) => a.partial_cmp(b),
1018 (Self::Duration(a), Self::Duration(b)) => a.partial_cmp(b),
1019 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b),
1020 (Self::Float32(a), Self::Float32(b)) => a.partial_cmp(b),
1021 (Self::Float64(a), Self::Float64(b)) => a.partial_cmp(b),
1022 (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
1023 (Self::Int128(a), Self::Int128(b)) => a.partial_cmp(b),
1024 (Self::IntBig(a), Self::IntBig(b)) => a.partial_cmp(b),
1025 (Self::Principal(a), Self::Principal(b)) => a.partial_cmp(b),
1026 (Self::Subaccount(a), Self::Subaccount(b)) => a.partial_cmp(b),
1027 (Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
1028 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
1029 (Self::Uint(a), Self::Uint(b)) => a.partial_cmp(b),
1030 (Self::Uint128(a), Self::Uint128(b)) => a.partial_cmp(b),
1031 (Self::UintBig(a), Self::UintBig(b)) => a.partial_cmp(b),
1032 (Self::Ulid(a), Self::Ulid(b)) => a.partial_cmp(b),
1033 (Self::Map(a), Self::Map(b)) => {
1034 for ((left_key, left_value), (right_key, right_value)) in a.iter().zip(b.iter()) {
1035 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
1036 if key_cmp != Ordering::Equal {
1037 return Some(key_cmp);
1038 }
1039
1040 match left_value.partial_cmp(right_value) {
1041 Some(Ordering::Equal) => {}
1042 non_eq => return non_eq,
1043 }
1044 }
1045 a.len().partial_cmp(&b.len())
1046 }
1047
1048 _ => None,
1050 }
1051 }
1052}
1053
1054#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd)]
1060pub struct ValueEnum {
1061 variant: String,
1062 path: Option<String>,
1063 payload: Option<Box<Value>>,
1064}
1065
1066impl ValueEnum {
1067 #[must_use]
1069 pub fn new(variant: &str, path: Option<&str>) -> Self {
1070 Self {
1071 variant: variant.to_string(),
1072 path: path.map(ToString::to_string),
1073 payload: None,
1074 }
1075 }
1076
1077 #[must_use]
1079 pub fn strict<E: Path>(variant: &str) -> Self {
1080 Self::new(variant, Some(E::PATH))
1081 }
1082
1083 #[must_use]
1085 pub fn from_enum<E: EnumValue>(value: E) -> Self {
1086 value.to_value_enum()
1087 }
1088
1089 #[must_use]
1092 pub fn loose(variant: &str) -> Self {
1093 Self::new(variant, None)
1094 }
1095
1096 #[must_use]
1098 pub fn with_payload(mut self, payload: Value) -> Self {
1099 self.payload = Some(Box::new(payload));
1100 self
1101 }
1102
1103 #[must_use]
1104 pub fn variant(&self) -> &str {
1105 &self.variant
1106 }
1107
1108 #[must_use]
1109 pub fn path(&self) -> Option<&str> {
1110 self.path.as_deref()
1111 }
1112
1113 #[must_use]
1114 pub fn payload(&self) -> Option<&Value> {
1115 self.payload.as_deref()
1116 }
1117
1118 pub(crate) fn set_path(&mut self, path: Option<String>) {
1119 self.path = path;
1120 }
1121}