1mod coercion;
8mod compare;
9mod hash;
10mod rank;
11mod storage_key;
12mod tag;
13mod wire;
14
15#[cfg(test)]
16mod tests;
17
18use crate::{
19 model::field::{FieldKind, FieldStorageDecode},
20 prelude::*,
21 traits::{EnumValue, FieldTypeMeta, FieldValue, NumericValue, Repr},
22 types::*,
23};
24use candid::CandidType;
25use serde::Deserialize;
26use std::{cmp::Ordering, fmt};
27
28pub use coercion::{CoercionFamily, CoercionFamilyExt};
30#[cfg(test)]
31pub(crate) use hash::with_test_hash_override;
32pub(crate) use hash::{ValueHashWriter, hash_single_list_identity_canonical_value, hash_value};
33pub use storage_key::{StorageKey, StorageKeyDecodeError, StorageKeyEncodeError};
34pub use tag::ValueTag;
35
36const F64_SAFE_I64: i64 = 1i64 << 53;
41const F64_SAFE_U64: u64 = 1u64 << 53;
42const F64_SAFE_I128: i128 = 1i128 << 53;
43const F64_SAFE_U128: u128 = 1u128 << 53;
44pub(crate) const VALUE_WIRE_TYPE_NAME: &str = "Value";
45pub(crate) const VALUE_WIRE_VARIANT_LABELS: &[&str] = &[
46 "Account",
47 "Blob",
48 "Bool",
49 "Date",
50 "Decimal",
51 "Duration",
52 "Enum",
53 "Float32",
54 "Float64",
55 "Int",
56 "Int128",
57 "IntBig",
58 "List",
59 "Map",
60 "Null",
61 "Principal",
62 "Subaccount",
63 "Text",
64 "Timestamp",
65 "Uint",
66 "Uint128",
67 "UintBig",
68 "Ulid",
69 "Unit",
70];
71
72enum NumericRepr {
77 Decimal(Decimal),
78 F64(f64),
79 None,
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(Clone, Debug, Eq, PartialEq)]
161pub enum MapValueError {
162 EmptyKey {
163 index: usize,
164 },
165 NonScalarKey {
166 index: usize,
167 key: Value,
168 },
169 NonScalarValue {
170 index: usize,
171 value: Value,
172 },
173 DuplicateKey {
174 left_index: usize,
175 right_index: usize,
176 },
177}
178
179impl std::fmt::Display for MapValueError {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 match self {
182 Self::EmptyKey { index } => write!(f, "map key at index {index} must be non-null"),
183 Self::NonScalarKey { index, key } => {
184 write!(f, "map key at index {index} is not scalar: {key:?}")
185 }
186 Self::NonScalarValue { index, value } => {
187 write!(
188 f,
189 "map value at index {index} is not scalar/ref-like: {value:?}"
190 )
191 }
192 Self::DuplicateKey {
193 left_index,
194 right_index,
195 } => write!(
196 f,
197 "map contains duplicate keys at normalized positions {left_index} and {right_index}"
198 ),
199 }
200 }
201}
202
203impl std::error::Error for MapValueError {}
204
205#[derive(Clone, Debug, Eq, PartialEq)]
212pub enum SchemaInvariantError {
213 InvalidMapValue(MapValueError),
214}
215
216impl std::fmt::Display for SchemaInvariantError {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 match self {
219 Self::InvalidMapValue(err) => write!(f, "{err}"),
220 }
221 }
222}
223
224impl std::error::Error for SchemaInvariantError {}
225
226impl From<MapValueError> for SchemaInvariantError {
227 fn from(value: MapValueError) -> Self {
228 Self::InvalidMapValue(value)
229 }
230}
231
232#[derive(CandidType, Clone, Eq, PartialEq)]
241pub enum Value {
242 Account(Account),
243 Blob(Vec<u8>),
244 Bool(bool),
245 Date(Date),
246 Decimal(Decimal),
247 Duration(Duration),
248 Enum(ValueEnum),
249 Float32(Float32),
250 Float64(Float64),
251 Int(i64),
252 Int128(Int128),
253 IntBig(Int),
254 List(Vec<Self>),
258 Map(Vec<(Self, Self)>),
265 Null,
266 Principal(Principal),
267 Subaccount(Subaccount),
268 Text(String),
269 Timestamp(Timestamp),
270 Uint(u64),
271 Uint128(Nat128),
272 UintBig(Nat),
273 Ulid(Ulid),
274 Unit,
275}
276
277impl fmt::Debug for Value {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 match self {
280 Self::Account(value) => f.debug_tuple("Account").field(value).finish(),
281 Self::Blob(value) => write!(f, "Blob({} bytes)", value.len()),
282 Self::Bool(value) => f.debug_tuple("Bool").field(value).finish(),
283 Self::Date(value) => f.debug_tuple("Date").field(value).finish(),
284 Self::Decimal(value) => f.debug_tuple("Decimal").field(value).finish(),
285 Self::Duration(value) => f.debug_tuple("Duration").field(value).finish(),
286 Self::Enum(value) => f.debug_tuple("Enum").field(value).finish(),
287 Self::Float32(value) => f.debug_tuple("Float32").field(value).finish(),
288 Self::Float64(value) => f.debug_tuple("Float64").field(value).finish(),
289 Self::Int(value) => f.debug_tuple("Int").field(value).finish(),
290 Self::Int128(value) => f.debug_tuple("Int128").field(value).finish(),
291 Self::IntBig(value) => f.debug_tuple("IntBig").field(value).finish(),
292 Self::List(value) => f.debug_tuple("List").field(value).finish(),
293 Self::Map(value) => f.debug_tuple("Map").field(value).finish(),
294 Self::Null => f.write_str("Null"),
295 Self::Principal(value) => f.debug_tuple("Principal").field(value).finish(),
296 Self::Subaccount(value) => f.debug_tuple("Subaccount").field(value).finish(),
297 Self::Text(value) => f.debug_tuple("Text").field(value).finish(),
298 Self::Timestamp(value) => f.debug_tuple("Timestamp").field(value).finish(),
299 Self::Uint(value) => f.debug_tuple("Uint").field(value).finish(),
300 Self::Uint128(value) => f.debug_tuple("Uint128").field(value).finish(),
301 Self::UintBig(value) => f.debug_tuple("UintBig").field(value).finish(),
302 Self::Ulid(value) => f.debug_tuple("Ulid").field(value).finish(),
303 Self::Unit => f.write_str("Unit"),
304 }
305 }
306}
307
308impl FieldTypeMeta for Value {
309 const KIND: FieldKind = FieldKind::Structured { queryable: false };
310 const STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
311}
312
313impl Value {
314 pub const __KIND: FieldKind = FieldKind::Structured { queryable: false };
315 pub const __STORAGE_DECODE: FieldStorageDecode = FieldStorageDecode::Value;
316}
317
318macro_rules! value_is_numeric_from_registry {
320 ( @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) ),* $(,)? ) => {
321 match $value {
322 $( $value_pat => $is_numeric, )*
323 _ => false,
324 }
325 };
326}
327
328macro_rules! value_supports_numeric_coercion_from_registry {
329 ( @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) ),* $(,)? ) => {
330 match $value {
331 $( $value_pat => $supports_numeric_coercion, )*
332 _ => false,
333 }
334 };
335}
336
337macro_rules! value_storage_key_case {
338 ( $value:expr, Unit, true ) => {
339 if let Value::Unit = $value {
340 Some(StorageKey::Unit)
341 } else {
342 None
343 }
344 };
345 ( $value:expr, $scalar:ident, true ) => {
346 if let Value::$scalar(v) = $value {
347 Some(StorageKey::$scalar(*v))
348 } else {
349 None
350 }
351 };
352 ( $value:expr, $scalar:ident, false ) => {
353 None
354 };
355}
356
357macro_rules! value_storage_key_from_registry {
358 ( @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) ),* $(,)? ) => {
359 {
360 let mut key = None;
361 $(
362 match key {
363 Some(_) => {}
364 None => {
365 key = value_storage_key_case!($value, $scalar, $is_storage_key_encodable);
366 }
367 }
368 )*
369 key
370 }
371 };
372}
373
374macro_rules! value_coercion_family_from_registry {
375 ( @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) ),* $(,)? ) => {
376 match $value {
377 $( $value_pat => $coercion_family, )*
378 Value::List(_) => CoercionFamily::Collection,
379 Value::Map(_) => CoercionFamily::Collection,
380 Value::Null => CoercionFamily::Null,
381 }
382 };
383}
384
385impl Value {
386 pub fn from_slice<T>(items: &[T]) -> Self
395 where
396 T: Into<Self> + Clone,
397 {
398 Self::List(items.iter().cloned().map(Into::into).collect())
399 }
400
401 pub fn from_list<T>(items: Vec<T>) -> Self
405 where
406 T: Into<Self>,
407 {
408 Self::List(items.into_iter().map(Into::into).collect())
409 }
410
411 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
419 let normalized = Self::normalize_map_entries(entries)?;
420 Ok(Self::Map(normalized))
421 }
422
423 pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
425 for (index, (key, _value)) in entries.iter().enumerate() {
426 if matches!(key, Self::Null) {
427 return Err(MapValueError::EmptyKey { index });
428 }
429 if !key.is_scalar() {
430 return Err(MapValueError::NonScalarKey {
431 index,
432 key: key.clone(),
433 });
434 }
435 }
436
437 Ok(())
438 }
439
440 pub(crate) fn compare_map_entry_keys(left: &(Self, Self), right: &(Self, Self)) -> Ordering {
442 Self::canonical_cmp_key(&left.0, &right.0)
443 }
444
445 pub(crate) fn sort_map_entries_in_place(entries: &mut [(Self, Self)]) {
447 entries.sort_by(Self::compare_map_entry_keys);
448 }
449
450 pub(crate) fn map_entries_are_strictly_canonical(entries: &[(Self, Self)]) -> bool {
453 entries.windows(2).all(|pair| {
454 let [left, right] = pair else {
455 return true;
456 };
457
458 Self::compare_map_entry_keys(left, right) == Ordering::Less
459 })
460 }
461
462 pub fn normalize_map_entries(
464 mut entries: Vec<(Self, Self)>,
465 ) -> Result<Vec<(Self, Self)>, MapValueError> {
466 Self::validate_map_entries(&entries)?;
467 Self::sort_map_entries_in_place(entries.as_mut_slice());
468
469 for i in 1..entries.len() {
470 let (left_key, _) = &entries[i - 1];
471 let (right_key, _) = &entries[i];
472 if Self::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
473 return Err(MapValueError::DuplicateKey {
474 left_index: i - 1,
475 right_index: i,
476 });
477 }
478 }
479
480 Ok(entries)
481 }
482
483 pub fn from_enum<E: EnumValue>(value: E) -> Self {
485 Self::Enum(value.to_value_enum())
486 }
487
488 #[must_use]
490 pub fn enum_strict<E: Path>(variant: &str) -> Self {
491 Self::Enum(ValueEnum::strict::<E>(variant))
492 }
493
494 #[must_use]
501 pub const fn is_numeric(&self) -> bool {
502 scalar_registry!(value_is_numeric_from_registry, self)
503 }
504
505 #[must_use]
507 pub const fn supports_numeric_coercion(&self) -> bool {
508 scalar_registry!(value_supports_numeric_coercion_from_registry, self)
509 }
510
511 #[must_use]
513 pub const fn is_text(&self) -> bool {
514 matches!(self, Self::Text(_))
515 }
516
517 #[must_use]
519 pub const fn is_unit(&self) -> bool {
520 matches!(self, Self::Unit)
521 }
522
523 #[must_use]
524 pub const fn is_scalar(&self) -> bool {
525 match self {
526 Self::List(_) | Self::Map(_) | Self::Unit => false,
528 _ => true,
529 }
530 }
531
532 #[must_use]
534 pub(crate) const fn canonical_tag(&self) -> ValueTag {
535 tag::canonical_tag(self)
536 }
537
538 #[must_use]
540 pub(crate) const fn canonical_rank(&self) -> u8 {
541 rank::canonical_rank(self)
542 }
543
544 #[must_use]
546 pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
547 compare::canonical_cmp(left, right)
548 }
549
550 #[must_use]
552 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
553 compare::canonical_cmp_key(left, right)
554 }
555
556 #[must_use]
561 pub(crate) fn canonical_cmp_map_entry(
562 left_key: &Self,
563 left_value: &Self,
564 right_key: &Self,
565 right_value: &Self,
566 ) -> Ordering {
567 Self::canonical_cmp_key(left_key, right_key)
568 .then_with(|| Self::canonical_cmp(left_value, right_value))
569 }
570
571 #[must_use]
574 pub(crate) fn ordered_map_entries(entries: &[(Self, Self)]) -> Vec<&(Self, Self)> {
575 let mut ordered = entries.iter().collect::<Vec<_>>();
576 ordered.sort_by(|left, right| {
577 Self::canonical_cmp_map_entry(&left.0, &left.1, &right.0, &right.1)
578 });
579
580 ordered
581 }
582
583 #[must_use]
587 pub(crate) fn strict_order_cmp(left: &Self, right: &Self) -> Option<Ordering> {
588 compare::strict_order_cmp(left, right)
589 }
590
591 fn numeric_repr(&self) -> NumericRepr {
592 if !self.supports_numeric_coercion() {
594 return NumericRepr::None;
595 }
596
597 if let Some(d) = self.to_decimal() {
598 return NumericRepr::Decimal(d);
599 }
600 if let Some(f) = self.to_f64_lossless() {
601 return NumericRepr::F64(f);
602 }
603 NumericRepr::None
604 }
605
606 #[must_use]
615 pub const fn as_storage_key(&self) -> Option<StorageKey> {
616 scalar_registry!(value_storage_key_from_registry, self)
617 }
618
619 #[must_use]
620 pub const fn as_text(&self) -> Option<&str> {
621 if let Self::Text(s) = self {
622 Some(s.as_str())
623 } else {
624 None
625 }
626 }
627
628 #[must_use]
629 pub const fn as_list(&self) -> Option<&[Self]> {
630 if let Self::List(xs) = self {
631 Some(xs.as_slice())
632 } else {
633 None
634 }
635 }
636
637 #[must_use]
638 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
639 if let Self::Map(entries) = self {
640 Some(entries.as_slice())
641 } else {
642 None
643 }
644 }
645
646 fn to_decimal(&self) -> Option<Decimal> {
647 match self {
648 Self::Decimal(d) => d.try_to_decimal(),
649 Self::Duration(d) => d.try_to_decimal(),
650 Self::Float64(f) => f.try_to_decimal(),
651 Self::Float32(f) => f.try_to_decimal(),
652 Self::Int(i) => i.try_to_decimal(),
653 Self::Int128(i) => i.try_to_decimal(),
654 Self::IntBig(i) => i.try_to_decimal(),
655 Self::Timestamp(t) => t.try_to_decimal(),
656 Self::Uint(u) => u.try_to_decimal(),
657 Self::Uint128(u) => u.try_to_decimal(),
658 Self::UintBig(u) => u.try_to_decimal(),
659
660 _ => None,
661 }
662 }
663
664 pub(crate) fn to_numeric_decimal(&self) -> Option<Decimal> {
666 self.to_decimal()
667 }
668
669 #[expect(clippy::cast_precision_loss)]
671 fn to_f64_lossless(&self) -> Option<f64> {
672 match self {
673 Self::Duration(d) if d.repr() <= F64_SAFE_U64 => Some(d.repr() as f64),
674 Self::Float64(f) => Some(f.get()),
675 Self::Float32(f) => Some(f64::from(f.get())),
676 Self::Int(i) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(i) => Some(*i as f64),
677 Self::Int128(i) if (-F64_SAFE_I128..=F64_SAFE_I128).contains(&i.get()) => {
678 Some(i.get() as f64)
679 }
680 Self::IntBig(i) => i.to_i128().and_then(|v| {
681 (-F64_SAFE_I128..=F64_SAFE_I128)
682 .contains(&v)
683 .then_some(v as f64)
684 }),
685 Self::Timestamp(t) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(&t.repr()) => {
686 Some(t.repr() as f64)
687 }
688 Self::Uint(u) if *u <= F64_SAFE_U64 => Some(*u as f64),
689 Self::Uint128(u) if u.get() <= F64_SAFE_U128 => Some(u.get() as f64),
690 Self::UintBig(u) => u
691 .to_u128()
692 .and_then(|v| (v <= F64_SAFE_U128).then_some(v as f64)),
693
694 _ => None,
695 }
696 }
697
698 #[must_use]
700 pub fn cmp_numeric(&self, other: &Self) -> Option<Ordering> {
701 if !self.supports_numeric_coercion() || !other.supports_numeric_coercion() {
702 return None;
703 }
704
705 match (self.numeric_repr(), other.numeric_repr()) {
706 (NumericRepr::Decimal(a), NumericRepr::Decimal(b)) => a.partial_cmp(&b),
707 (NumericRepr::F64(a), NumericRepr::F64(b)) => a.partial_cmp(&b),
708 _ => None,
709 }
710 }
711
712 fn fold_ci(s: &str) -> std::borrow::Cow<'_, str> {
717 if s.is_ascii() {
718 return std::borrow::Cow::Owned(s.to_ascii_lowercase());
719 }
720 std::borrow::Cow::Owned(s.to_lowercase())
723 }
724
725 fn text_with_mode(s: &'_ str, mode: TextMode) -> std::borrow::Cow<'_, str> {
726 match mode {
727 TextMode::Cs => std::borrow::Cow::Borrowed(s),
728 TextMode::Ci => Self::fold_ci(s),
729 }
730 }
731
732 fn text_op(
733 &self,
734 other: &Self,
735 mode: TextMode,
736 f: impl Fn(&str, &str) -> bool,
737 ) -> Option<bool> {
738 let (a, b) = (self.as_text()?, other.as_text()?);
739 let a = Self::text_with_mode(a, mode);
740 let b = Self::text_with_mode(b, mode);
741 Some(f(&a, &b))
742 }
743
744 fn ci_key(&self) -> Option<String> {
745 match self {
746 Self::Text(s) => Some(Self::fold_ci(s).into_owned()),
747 Self::Ulid(u) => Some(u.to_string().to_ascii_lowercase()),
748 Self::Principal(p) => Some(p.to_string().to_ascii_lowercase()),
749 Self::Account(a) => Some(a.to_string().to_ascii_lowercase()),
750 _ => None,
751 }
752 }
753
754 fn eq_ci(a: &Self, b: &Self) -> bool {
755 if let (Some(ak), Some(bk)) = (a.ci_key(), b.ci_key()) {
756 return ak == bk;
757 }
758
759 a == b
760 }
761
762 fn normalize_list_ref(v: &Self) -> Vec<&Self> {
763 match v {
764 Self::List(vs) => vs.iter().collect(),
765 v => vec![v],
766 }
767 }
768
769 fn contains_by<F>(&self, needle: &Self, eq: F) -> Option<bool>
770 where
771 F: Fn(&Self, &Self) -> bool,
772 {
773 self.as_list()
774 .map(|items| items.iter().any(|v| eq(v, needle)))
775 }
776
777 #[expect(clippy::unnecessary_wraps)]
778 fn contains_any_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
779 where
780 F: Fn(&Self, &Self) -> bool,
781 {
782 let needles = Self::normalize_list_ref(needles);
783 match self {
784 Self::List(items) => Some(needles.iter().any(|n| items.iter().any(|v| eq(v, n)))),
785 scalar => Some(needles.iter().any(|n| eq(scalar, n))),
786 }
787 }
788
789 #[expect(clippy::unnecessary_wraps)]
790 fn contains_all_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
791 where
792 F: Fn(&Self, &Self) -> bool,
793 {
794 let needles = Self::normalize_list_ref(needles);
795 match self {
796 Self::List(items) => Some(needles.iter().all(|n| items.iter().any(|v| eq(v, n)))),
797 scalar => Some(needles.len() == 1 && eq(scalar, needles[0])),
798 }
799 }
800
801 fn in_list_by<F>(&self, haystack: &Self, eq: F) -> Option<bool>
802 where
803 F: Fn(&Self, &Self) -> bool,
804 {
805 if let Self::List(items) = haystack {
806 Some(items.iter().any(|h| eq(h, self)))
807 } else {
808 None
809 }
810 }
811
812 #[must_use]
814 pub fn text_eq(&self, other: &Self, mode: TextMode) -> Option<bool> {
815 self.text_op(other, mode, |a, b| a == b)
816 }
817
818 #[must_use]
820 pub fn text_contains(&self, needle: &Self, mode: TextMode) -> Option<bool> {
821 self.text_op(needle, mode, |a, b| a.contains(b))
822 }
823
824 #[must_use]
826 pub fn text_starts_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
827 self.text_op(needle, mode, |a, b| a.starts_with(b))
828 }
829
830 #[must_use]
832 pub fn text_ends_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
833 self.text_op(needle, mode, |a, b| a.ends_with(b))
834 }
835
836 #[must_use]
841 pub const fn is_empty(&self) -> Option<bool> {
842 match self {
843 Self::List(xs) => Some(xs.is_empty()),
844 Self::Map(entries) => Some(entries.is_empty()),
845 Self::Text(s) => Some(s.is_empty()),
846 Self::Blob(b) => Some(b.is_empty()),
847
848 Self::Null => Some(true),
850
851 _ => None,
852 }
853 }
854
855 #[must_use]
857 pub fn is_not_empty(&self) -> Option<bool> {
858 self.is_empty().map(|b| !b)
859 }
860
861 #[must_use]
867 pub fn contains(&self, needle: &Self) -> Option<bool> {
868 self.contains_by(needle, |a, b| a == b)
869 }
870
871 #[must_use]
873 pub fn contains_any(&self, needles: &Self) -> Option<bool> {
874 self.contains_any_by(needles, |a, b| a == b)
875 }
876
877 #[must_use]
879 pub fn contains_all(&self, needles: &Self) -> Option<bool> {
880 self.contains_all_by(needles, |a, b| a == b)
881 }
882
883 #[must_use]
885 pub fn in_list(&self, haystack: &Self) -> Option<bool> {
886 self.in_list_by(haystack, |a, b| a == b)
887 }
888
889 #[must_use]
891 pub fn contains_ci(&self, needle: &Self) -> Option<bool> {
892 match self {
893 Self::List(_) => self.contains_by(needle, Self::eq_ci),
894 _ => Some(Self::eq_ci(self, needle)),
895 }
896 }
897
898 #[must_use]
900 pub fn contains_any_ci(&self, needles: &Self) -> Option<bool> {
901 self.contains_any_by(needles, Self::eq_ci)
902 }
903
904 #[must_use]
906 pub fn contains_all_ci(&self, needles: &Self) -> Option<bool> {
907 self.contains_all_by(needles, Self::eq_ci)
908 }
909
910 #[must_use]
912 pub fn in_list_ci(&self, haystack: &Self) -> Option<bool> {
913 self.in_list_by(haystack, Self::eq_ci)
914 }
915}
916
917impl FieldValue for Value {
918 fn kind() -> crate::traits::FieldValueKind {
919 crate::traits::FieldValueKind::Atomic
920 }
921
922 fn to_value(&self) -> Value {
923 self.clone()
924 }
925
926 fn from_value(value: &Value) -> Option<Self> {
927 Some(value.clone())
928 }
929}
930
931#[macro_export]
932macro_rules! impl_from_for {
933 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
934 $(
935 impl From<$type> for Value {
936 fn from(v: $type) -> Self {
937 Self::$variant(v.into())
938 }
939 }
940 )*
941 };
942}
943
944impl_from_for! {
945 Account => Account,
946 Date => Date,
947 Decimal => Decimal,
948 Duration => Duration,
949 bool => Bool,
950 i8 => Int,
951 i16 => Int,
952 i32 => Int,
953 i64 => Int,
954 i128 => Int128,
955 Int => IntBig,
956 Principal => Principal,
957 Subaccount => Subaccount,
958 &str => Text,
959 String => Text,
960 Timestamp => Timestamp,
961 u8 => Uint,
962 u16 => Uint,
963 u32 => Uint,
964 u64 => Uint,
965 u128 => Uint128,
966 Nat => UintBig,
967 Ulid => Ulid,
968}
969
970impl CoercionFamilyExt for Value {
971 fn coercion_family(&self) -> CoercionFamily {
977 scalar_registry!(value_coercion_family_from_registry, self)
978 }
979}
980
981impl From<Vec<Self>> for Value {
982 fn from(vec: Vec<Self>) -> Self {
983 Self::List(vec)
984 }
985}
986
987impl TryFrom<Vec<(Self, Self)>> for Value {
988 type Error = SchemaInvariantError;
989
990 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
991 Self::from_map(entries).map_err(Self::Error::from)
992 }
993}
994
995impl From<()> for Value {
996 fn from((): ()) -> Self {
997 Self::Unit
998 }
999}
1000
1001impl PartialOrd for Value {
1007 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1008 match (self, other) {
1009 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
1010 (Self::Date(a), Self::Date(b)) => a.partial_cmp(b),
1011 (Self::Decimal(a), Self::Decimal(b)) => a.partial_cmp(b),
1012 (Self::Duration(a), Self::Duration(b)) => a.partial_cmp(b),
1013 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b),
1014 (Self::Float32(a), Self::Float32(b)) => a.partial_cmp(b),
1015 (Self::Float64(a), Self::Float64(b)) => a.partial_cmp(b),
1016 (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
1017 (Self::Int128(a), Self::Int128(b)) => a.partial_cmp(b),
1018 (Self::IntBig(a), Self::IntBig(b)) => a.partial_cmp(b),
1019 (Self::Principal(a), Self::Principal(b)) => a.partial_cmp(b),
1020 (Self::Subaccount(a), Self::Subaccount(b)) => a.partial_cmp(b),
1021 (Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
1022 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
1023 (Self::Uint(a), Self::Uint(b)) => a.partial_cmp(b),
1024 (Self::Uint128(a), Self::Uint128(b)) => a.partial_cmp(b),
1025 (Self::UintBig(a), Self::UintBig(b)) => a.partial_cmp(b),
1026 (Self::Ulid(a), Self::Ulid(b)) => a.partial_cmp(b),
1027 (Self::Map(a), Self::Map(b)) => {
1028 for ((left_key, left_value), (right_key, right_value)) in a.iter().zip(b.iter()) {
1029 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
1030 if key_cmp != Ordering::Equal {
1031 return Some(key_cmp);
1032 }
1033
1034 match left_value.partial_cmp(right_value) {
1035 Some(Ordering::Equal) => {}
1036 non_eq => return non_eq,
1037 }
1038 }
1039 a.len().partial_cmp(&b.len())
1040 }
1041
1042 _ => None,
1044 }
1045 }
1046}
1047
1048#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd)]
1054pub struct ValueEnum {
1055 variant: String,
1056 path: Option<String>,
1057 payload: Option<Box<Value>>,
1058}
1059
1060impl ValueEnum {
1061 #[must_use]
1063 pub fn new(variant: &str, path: Option<&str>) -> Self {
1064 Self {
1065 variant: variant.to_string(),
1066 path: path.map(ToString::to_string),
1067 payload: None,
1068 }
1069 }
1070
1071 #[must_use]
1073 pub fn strict<E: Path>(variant: &str) -> Self {
1074 Self::new(variant, Some(E::PATH))
1075 }
1076
1077 #[must_use]
1079 pub fn from_enum<E: EnumValue>(value: E) -> Self {
1080 value.to_value_enum()
1081 }
1082
1083 #[must_use]
1086 pub fn loose(variant: &str) -> Self {
1087 Self::new(variant, None)
1088 }
1089
1090 #[must_use]
1092 pub fn with_payload(mut self, payload: Value) -> Self {
1093 self.payload = Some(Box::new(payload));
1094 self
1095 }
1096
1097 #[must_use]
1098 pub fn variant(&self) -> &str {
1099 &self.variant
1100 }
1101
1102 #[must_use]
1103 pub fn path(&self) -> Option<&str> {
1104 self.path.as_deref()
1105 }
1106
1107 #[must_use]
1108 pub fn payload(&self) -> Option<&Value> {
1109 self.payload.as_deref()
1110 }
1111
1112 pub(crate) fn set_path(&mut self, path: Option<String>) {
1113 self.path = path;
1114 }
1115}