1mod family;
2
3#[cfg(test)]
4mod tests;
5
6use crate::{
7 db::store::StorageKey,
8 prelude::*,
9 traits::{EnumValue, FieldValue, NumFromPrimitive},
10 types::*,
11};
12use candid::CandidType;
13use serde::{Deserialize, Serialize};
14use std::cmp::Ordering;
15
16pub use family::{CoercionFamily, CoercionFamilyExt};
18
19const F64_SAFE_I64: i64 = 1i64 << 53;
24const F64_SAFE_U64: u64 = 1u64 << 53;
25const F64_SAFE_I128: i128 = 1i128 << 53;
26const F64_SAFE_U128: u128 = 1u128 << 53;
27
28enum NumericRepr {
33 Decimal(Decimal),
34 F64(f64),
35 None,
36}
37
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub enum TextMode {
44 Cs, Ci, }
47
48#[derive(Clone, Debug, Eq, PartialEq)]
55pub enum MapValueError {
56 EmptyKey {
57 index: usize,
58 },
59 NonScalarKey {
60 index: usize,
61 key: Value,
62 },
63 NonScalarValue {
64 index: usize,
65 value: Value,
66 },
67 DuplicateKey {
68 left_index: usize,
69 right_index: usize,
70 },
71}
72
73impl std::fmt::Display for MapValueError {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 match self {
76 Self::EmptyKey { index } => write!(f, "map key at index {index} must be non-null"),
77 Self::NonScalarKey { index, key } => {
78 write!(f, "map key at index {index} is not scalar: {key:?}")
79 }
80 Self::NonScalarValue { index, value } => {
81 write!(
82 f,
83 "map value at index {index} is not scalar/ref-like: {value:?}"
84 )
85 }
86 Self::DuplicateKey {
87 left_index,
88 right_index,
89 } => write!(
90 f,
91 "map contains duplicate keys at normalized positions {left_index} and {right_index}"
92 ),
93 }
94 }
95}
96
97impl std::error::Error for MapValueError {}
98
99#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
108pub enum Value {
109 Account(Account),
110 Blob(Vec<u8>),
111 Bool(bool),
112 Date(Date),
113 Decimal(Decimal),
114 Duration(Duration),
115 Enum(ValueEnum),
116 E8s(E8s),
117 E18s(E18s),
118 Float32(Float32),
119 Float64(Float64),
120 Int(i64),
121 Int128(Int128),
122 IntBig(Int),
123 List(Vec<Self>),
127 Map(Vec<(Self, Self)>),
131 Null,
132 Principal(Principal),
133 Subaccount(Subaccount),
134 Text(String),
135 Timestamp(Timestamp),
136 Uint(u64),
137 Uint128(Nat128),
138 UintBig(Nat),
139 Ulid(Ulid),
140 Unit,
141}
142
143macro_rules! value_is_numeric_from_registry {
145 ( @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) ),* $(,)? ) => {
146 match $value {
147 $( $value_pat => $is_numeric, )*
148 _ => false,
149 }
150 };
151}
152
153macro_rules! value_supports_numeric_coercion_from_registry {
154 ( @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) ),* $(,)? ) => {
155 match $value {
156 $( $value_pat => $supports_numeric_coercion, )*
157 _ => false,
158 }
159 };
160}
161
162macro_rules! value_storage_key_case {
163 ( $value:expr, Unit, true ) => {
164 if let Value::Unit = $value {
165 Some(StorageKey::Unit)
166 } else {
167 None
168 }
169 };
170 ( $value:expr, $scalar:ident, true ) => {
171 if let Value::$scalar(v) = $value {
172 Some(StorageKey::$scalar(*v))
173 } else {
174 None
175 }
176 };
177 ( $value:expr, $scalar:ident, false ) => {
178 None
179 };
180}
181
182macro_rules! value_storage_key_from_registry {
183 ( @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:expr) ),* $(,)? ) => {
184 {
185 let mut key = None;
186 $(
187 match key {
188 Some(_) => {}
189 None => {
190 key = value_storage_key_case!($value, $scalar, $is_keyable);
191 }
192 }
193 )*
194 key
195 }
196 };
197}
198
199macro_rules! value_coercion_family_from_registry {
200 ( @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) ),* $(,)? ) => {
201 match $value {
202 $( $value_pat => $coercion_family, )*
203 Value::List(_) => CoercionFamily::Collection,
204 Value::Map(_) => CoercionFamily::Collection,
205 Value::Null => CoercionFamily::Null,
206 }
207 };
208}
209
210impl Value {
211 pub fn from_slice<T>(items: &[T]) -> Self
220 where
221 T: Into<Self> + Clone,
222 {
223 Self::List(items.iter().cloned().map(Into::into).collect())
224 }
225
226 pub fn from_list<T>(items: Vec<T>) -> Self
230 where
231 T: Into<Self>,
232 {
233 Self::List(items.into_iter().map(Into::into).collect())
234 }
235
236 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
244 let normalized = Self::normalize_map_entries(entries)?;
245 Ok(Self::Map(normalized))
246 }
247
248 pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
250 for (index, (key, value)) in entries.iter().enumerate() {
251 if matches!(key, Self::Null) {
252 return Err(MapValueError::EmptyKey { index });
253 }
254 if !key.is_scalar() {
255 return Err(MapValueError::NonScalarKey {
256 index,
257 key: key.clone(),
258 });
259 }
260
261 if !value.is_scalar() {
262 return Err(MapValueError::NonScalarValue {
263 index,
264 value: value.clone(),
265 });
266 }
267 }
268
269 Ok(())
270 }
271
272 pub fn normalize_map_entries(
274 mut entries: Vec<(Self, Self)>,
275 ) -> Result<Vec<(Self, Self)>, MapValueError> {
276 Self::validate_map_entries(&entries)?;
277 entries
278 .sort_by(|(left_key, _), (right_key, _)| Self::canonical_cmp_key(left_key, right_key));
279
280 for i in 1..entries.len() {
281 let (left_key, _) = &entries[i - 1];
282 let (right_key, _) = &entries[i];
283 if Self::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
284 return Err(MapValueError::DuplicateKey {
285 left_index: i - 1,
286 right_index: i,
287 });
288 }
289 }
290
291 Ok(entries)
292 }
293
294 pub fn from_enum<E: EnumValue>(value: E) -> Self {
296 Self::Enum(value.to_value_enum())
297 }
298
299 #[must_use]
301 pub fn enum_strict<E: Path>(variant: &str) -> Self {
302 Self::Enum(ValueEnum::strict::<E>(variant))
303 }
304
305 #[must_use]
312 pub const fn is_numeric(&self) -> bool {
313 scalar_registry!(value_is_numeric_from_registry, self)
314 }
315
316 #[must_use]
318 pub const fn supports_numeric_coercion(&self) -> bool {
319 scalar_registry!(value_supports_numeric_coercion_from_registry, self)
320 }
321
322 #[must_use]
324 pub const fn is_text(&self) -> bool {
325 matches!(self, Self::Text(_))
326 }
327
328 #[must_use]
330 pub const fn is_unit(&self) -> bool {
331 matches!(self, Self::Unit)
332 }
333
334 #[must_use]
335 pub const fn is_scalar(&self) -> bool {
336 match self {
337 Self::List(_) | Self::Map(_) | Self::Unit => false,
339 _ => true,
340 }
341 }
342
343 #[must_use]
345 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
346 let rank = Self::canonical_key_rank(left).cmp(&Self::canonical_key_rank(right));
347 if rank != Ordering::Equal {
348 return rank;
349 }
350
351 match (left, right) {
352 (Self::Account(a), Self::Account(b)) => a.cmp(b),
353 (Self::Blob(a), Self::Blob(b)) => a.cmp(b),
354 (Self::Bool(a), Self::Bool(b)) => a.cmp(b),
355 (Self::Date(a), Self::Date(b)) => a.cmp(b),
356 (Self::Decimal(a), Self::Decimal(b)) => a.cmp(b),
357 (Self::Duration(a), Self::Duration(b)) => a.cmp(b),
358 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
359 (Self::E8s(a), Self::E8s(b)) => a.cmp(b),
360 (Self::E18s(a), Self::E18s(b)) => a.cmp(b),
361 (Self::Float32(a), Self::Float32(b)) => a.cmp(b),
362 (Self::Float64(a), Self::Float64(b)) => a.cmp(b),
363 (Self::Int(a), Self::Int(b)) => a.cmp(b),
364 (Self::Int128(a), Self::Int128(b)) => a.cmp(b),
365 (Self::IntBig(a), Self::IntBig(b)) => a.cmp(b),
366 (Self::Principal(a), Self::Principal(b)) => a.cmp(b),
367 (Self::Subaccount(a), Self::Subaccount(b)) => a.cmp(b),
368 (Self::Text(a), Self::Text(b)) => a.cmp(b),
369 (Self::Timestamp(a), Self::Timestamp(b)) => a.cmp(b),
370 (Self::Uint(a), Self::Uint(b)) => a.cmp(b),
371 (Self::Uint128(a), Self::Uint128(b)) => a.cmp(b),
372 (Self::UintBig(a), Self::UintBig(b)) => a.cmp(b),
373 (Self::Ulid(a), Self::Ulid(b)) => a.cmp(b),
374 _ => Ordering::Equal,
375 }
376 }
377
378 const fn canonical_key_rank(value: &Self) -> u8 {
379 match value {
380 Self::Account(_) => 0,
381 Self::Blob(_) => 1,
382 Self::Bool(_) => 2,
383 Self::Date(_) => 3,
384 Self::Decimal(_) => 4,
385 Self::Duration(_) => 5,
386 Self::Enum(_) => 6,
387 Self::E8s(_) => 7,
388 Self::E18s(_) => 8,
389 Self::Float32(_) => 9,
390 Self::Float64(_) => 10,
391 Self::Int(_) => 11,
392 Self::Int128(_) => 12,
393 Self::IntBig(_) => 13,
394 Self::List(_) => 14,
395 Self::Map(_) => 15,
396 Self::Null => 16,
397 Self::Principal(_) => 17,
398 Self::Subaccount(_) => 18,
399 Self::Text(_) => 19,
400 Self::Timestamp(_) => 20,
401 Self::Uint(_) => 21,
402 Self::Uint128(_) => 22,
403 Self::UintBig(_) => 23,
404 Self::Ulid(_) => 24,
405 Self::Unit => 25,
406 }
407 }
408
409 fn numeric_repr(&self) -> NumericRepr {
410 if !self.supports_numeric_coercion() {
412 return NumericRepr::None;
413 }
414
415 if let Some(d) = self.to_decimal() {
416 return NumericRepr::Decimal(d);
417 }
418 if let Some(f) = self.to_f64_lossless() {
419 return NumericRepr::F64(f);
420 }
421 NumericRepr::None
422 }
423
424 #[must_use]
433 pub const fn as_storage_key(&self) -> Option<StorageKey> {
434 scalar_registry!(value_storage_key_from_registry, self)
435 }
436
437 #[must_use]
438 pub const fn as_text(&self) -> Option<&str> {
439 if let Self::Text(s) = self {
440 Some(s.as_str())
441 } else {
442 None
443 }
444 }
445
446 #[must_use]
447 pub const fn as_list(&self) -> Option<&[Self]> {
448 if let Self::List(xs) = self {
449 Some(xs.as_slice())
450 } else {
451 None
452 }
453 }
454
455 #[must_use]
456 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
457 if let Self::Map(entries) = self {
458 Some(entries.as_slice())
459 } else {
460 None
461 }
462 }
463
464 fn to_decimal(&self) -> Option<Decimal> {
465 match self {
466 Self::Decimal(d) => Some(*d),
467 Self::Duration(d) => Decimal::from_u64(d.get()),
468 Self::E8s(v) => Some(v.to_decimal()),
469 Self::E18s(v) => v.to_decimal(),
470 Self::Float64(f) => Decimal::from_f64(f.get()),
471 Self::Float32(f) => Decimal::from_f32(f.get()),
472 Self::Int(i) => Decimal::from_i64(*i),
473 Self::Int128(i) => Decimal::from_i128(i.get()),
474 Self::IntBig(i) => i.to_i128().and_then(Decimal::from_i128),
475 Self::Timestamp(t) => Decimal::from_u64(t.get()),
476 Self::Uint(u) => Decimal::from_u64(*u),
477 Self::Uint128(u) => Decimal::from_u128(u.get()),
478 Self::UintBig(u) => u.to_u128().and_then(Decimal::from_u128),
479
480 _ => None,
481 }
482 }
483
484 #[allow(clippy::cast_precision_loss)]
486 fn to_f64_lossless(&self) -> Option<f64> {
487 match self {
488 Self::Duration(d) if d.get() <= F64_SAFE_U64 => Some(d.get() as f64),
489 Self::Float64(f) => Some(f.get()),
490 Self::Float32(f) => Some(f64::from(f.get())),
491 Self::Int(i) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(i) => Some(*i as f64),
492 Self::Int128(i) if (-F64_SAFE_I128..=F64_SAFE_I128).contains(&i.get()) => {
493 Some(i.get() as f64)
494 }
495 Self::IntBig(i) => i.to_i128().and_then(|v| {
496 (-F64_SAFE_I128..=F64_SAFE_I128)
497 .contains(&v)
498 .then_some(v as f64)
499 }),
500 Self::Timestamp(t) if t.get() <= F64_SAFE_U64 => Some(t.get() as f64),
501 Self::Uint(u) if *u <= F64_SAFE_U64 => Some(*u as f64),
502 Self::Uint128(u) if u.get() <= F64_SAFE_U128 => Some(u.get() as f64),
503 Self::UintBig(u) => u
504 .to_u128()
505 .and_then(|v| (v <= F64_SAFE_U128).then_some(v as f64)),
506
507 _ => None,
508 }
509 }
510
511 #[must_use]
513 pub fn cmp_numeric(&self, other: &Self) -> Option<Ordering> {
514 if !self.supports_numeric_coercion() || !other.supports_numeric_coercion() {
515 return None;
516 }
517
518 match (self.numeric_repr(), other.numeric_repr()) {
519 (NumericRepr::Decimal(a), NumericRepr::Decimal(b)) => a.partial_cmp(&b),
520 (NumericRepr::F64(a), NumericRepr::F64(b)) => a.partial_cmp(&b),
521 _ => None,
522 }
523 }
524
525 fn fold_ci(s: &str) -> std::borrow::Cow<'_, str> {
530 if s.is_ascii() {
531 return std::borrow::Cow::Owned(s.to_ascii_lowercase());
532 }
533 std::borrow::Cow::Owned(s.to_lowercase())
536 }
537
538 fn text_with_mode(s: &'_ str, mode: TextMode) -> std::borrow::Cow<'_, str> {
539 match mode {
540 TextMode::Cs => std::borrow::Cow::Borrowed(s),
541 TextMode::Ci => Self::fold_ci(s),
542 }
543 }
544
545 fn text_op(
546 &self,
547 other: &Self,
548 mode: TextMode,
549 f: impl Fn(&str, &str) -> bool,
550 ) -> Option<bool> {
551 let (a, b) = (self.as_text()?, other.as_text()?);
552 let a = Self::text_with_mode(a, mode);
553 let b = Self::text_with_mode(b, mode);
554 Some(f(&a, &b))
555 }
556
557 fn ci_key(&self) -> Option<String> {
558 match self {
559 Self::Text(s) => Some(Self::fold_ci(s).into_owned()),
560 Self::Ulid(u) => Some(u.to_string().to_ascii_lowercase()),
561 Self::Principal(p) => Some(p.to_string().to_ascii_lowercase()),
562 Self::Account(a) => Some(a.to_string().to_ascii_lowercase()),
563 _ => None,
564 }
565 }
566
567 fn eq_ci(a: &Self, b: &Self) -> bool {
568 if let (Some(ak), Some(bk)) = (a.ci_key(), b.ci_key()) {
569 return ak == bk;
570 }
571
572 a == b
573 }
574
575 fn normalize_list_ref(v: &Self) -> Vec<&Self> {
576 match v {
577 Self::List(vs) => vs.iter().collect(),
578 v => vec![v],
579 }
580 }
581
582 fn contains_by<F>(&self, needle: &Self, eq: F) -> Option<bool>
583 where
584 F: Fn(&Self, &Self) -> bool,
585 {
586 self.as_list()
587 .map(|items| items.iter().any(|v| eq(v, needle)))
588 }
589
590 #[allow(clippy::unnecessary_wraps)]
591 fn contains_any_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
592 where
593 F: Fn(&Self, &Self) -> bool,
594 {
595 let needles = Self::normalize_list_ref(needles);
596 match self {
597 Self::List(items) => Some(needles.iter().any(|n| items.iter().any(|v| eq(v, n)))),
598 scalar => Some(needles.iter().any(|n| eq(scalar, n))),
599 }
600 }
601
602 #[allow(clippy::unnecessary_wraps)]
603 fn contains_all_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
604 where
605 F: Fn(&Self, &Self) -> bool,
606 {
607 let needles = Self::normalize_list_ref(needles);
608 match self {
609 Self::List(items) => Some(needles.iter().all(|n| items.iter().any(|v| eq(v, n)))),
610 scalar => Some(needles.len() == 1 && eq(scalar, needles[0])),
611 }
612 }
613
614 fn in_list_by<F>(&self, haystack: &Self, eq: F) -> Option<bool>
615 where
616 F: Fn(&Self, &Self) -> bool,
617 {
618 if let Self::List(items) = haystack {
619 Some(items.iter().any(|h| eq(h, self)))
620 } else {
621 None
622 }
623 }
624
625 #[must_use]
626 pub fn text_eq(&self, other: &Self, mode: TextMode) -> Option<bool> {
628 self.text_op(other, mode, |a, b| a == b)
629 }
630
631 #[must_use]
632 pub fn text_contains(&self, needle: &Self, mode: TextMode) -> Option<bool> {
634 self.text_op(needle, mode, |a, b| a.contains(b))
635 }
636
637 #[must_use]
638 pub fn text_starts_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
640 self.text_op(needle, mode, |a, b| a.starts_with(b))
641 }
642
643 #[must_use]
644 pub fn text_ends_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
646 self.text_op(needle, mode, |a, b| a.ends_with(b))
647 }
648
649 #[must_use]
654 pub const fn is_empty(&self) -> Option<bool> {
655 match self {
656 Self::List(xs) => Some(xs.is_empty()),
657 Self::Map(entries) => Some(entries.is_empty()),
658 Self::Text(s) => Some(s.is_empty()),
659 Self::Blob(b) => Some(b.is_empty()),
660
661 Self::Null => Some(true),
663
664 _ => None,
665 }
666 }
667
668 #[must_use]
669 pub fn is_not_empty(&self) -> Option<bool> {
671 self.is_empty().map(|b| !b)
672 }
673
674 #[must_use]
679 pub fn contains(&self, needle: &Self) -> Option<bool> {
681 self.contains_by(needle, |a, b| a == b)
682 }
683
684 #[must_use]
685 pub fn contains_any(&self, needles: &Self) -> Option<bool> {
687 self.contains_any_by(needles, |a, b| a == b)
688 }
689
690 #[must_use]
691 pub fn contains_all(&self, needles: &Self) -> Option<bool> {
693 self.contains_all_by(needles, |a, b| a == b)
694 }
695
696 #[must_use]
697 pub fn in_list(&self, haystack: &Self) -> Option<bool> {
699 self.in_list_by(haystack, |a, b| a == b)
700 }
701
702 #[must_use]
703 pub fn contains_ci(&self, needle: &Self) -> Option<bool> {
705 match self {
706 Self::List(_) => self.contains_by(needle, Self::eq_ci),
707 _ => Some(Self::eq_ci(self, needle)),
708 }
709 }
710
711 #[must_use]
712 pub fn contains_any_ci(&self, needles: &Self) -> Option<bool> {
714 self.contains_any_by(needles, Self::eq_ci)
715 }
716
717 #[must_use]
718 pub fn contains_all_ci(&self, needles: &Self) -> Option<bool> {
720 self.contains_all_by(needles, Self::eq_ci)
721 }
722
723 #[must_use]
724 pub fn in_list_ci(&self, haystack: &Self) -> Option<bool> {
726 self.in_list_by(haystack, Self::eq_ci)
727 }
728}
729
730impl FieldValue for Value {
731 fn kind() -> crate::traits::FieldValueKind {
732 crate::traits::FieldValueKind::Atomic
733 }
734
735 fn to_value(&self) -> Value {
736 self.clone()
737 }
738
739 fn from_value(value: &Value) -> Option<Self> {
740 Some(value.clone())
741 }
742}
743
744#[macro_export]
745macro_rules! impl_from_for {
746 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
747 $(
748 impl From<$type> for Value {
749 fn from(v: $type) -> Self {
750 Self::$variant(v.into())
751 }
752 }
753 )*
754 };
755}
756
757impl_from_for! {
758 Account => Account,
759 Date => Date,
760 Decimal => Decimal,
761 Duration => Duration,
762 E8s => E8s,
763 E18s => E18s,
764 bool => Bool,
765 i8 => Int,
766 i16 => Int,
767 i32 => Int,
768 i64 => Int,
769 i128 => Int128,
770 Int => IntBig,
771 Principal => Principal,
772 Subaccount => Subaccount,
773 &str => Text,
774 String => Text,
775 Timestamp => Timestamp,
776 u8 => Uint,
777 u16 => Uint,
778 u32 => Uint,
779 u64 => Uint,
780 u128 => Uint128,
781 Nat => UintBig,
782 Ulid => Ulid,
783}
784
785impl CoercionFamilyExt for Value {
786 fn coercion_family(&self) -> CoercionFamily {
792 scalar_registry!(value_coercion_family_from_registry, self)
793 }
794}
795
796impl From<Vec<Self>> for Value {
797 fn from(vec: Vec<Self>) -> Self {
798 Self::List(vec)
799 }
800}
801
802impl From<Vec<(Self, Self)>> for Value {
803 fn from(entries: Vec<(Self, Self)>) -> Self {
804 Self::from_map(entries).unwrap_or_else(|err| {
805 panic!("invalid map invariant while constructing Value::Map from Vec: {err}")
806 })
807 }
808}
809
810impl From<()> for Value {
811 fn from((): ()) -> Self {
812 Self::Unit
813 }
814}
815
816impl PartialOrd for Value {
822 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
823 match (self, other) {
824 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
825 (Self::Date(a), Self::Date(b)) => a.partial_cmp(b),
826 (Self::Decimal(a), Self::Decimal(b)) => a.partial_cmp(b),
827 (Self::Duration(a), Self::Duration(b)) => a.partial_cmp(b),
828 (Self::E8s(a), Self::E8s(b)) => a.partial_cmp(b),
829 (Self::E18s(a), Self::E18s(b)) => a.partial_cmp(b),
830 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b),
831 (Self::Float32(a), Self::Float32(b)) => a.partial_cmp(b),
832 (Self::Float64(a), Self::Float64(b)) => a.partial_cmp(b),
833 (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
834 (Self::Int128(a), Self::Int128(b)) => a.partial_cmp(b),
835 (Self::IntBig(a), Self::IntBig(b)) => a.partial_cmp(b),
836 (Self::Principal(a), Self::Principal(b)) => a.partial_cmp(b),
837 (Self::Subaccount(a), Self::Subaccount(b)) => a.partial_cmp(b),
838 (Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
839 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
840 (Self::Uint(a), Self::Uint(b)) => a.partial_cmp(b),
841 (Self::Uint128(a), Self::Uint128(b)) => a.partial_cmp(b),
842 (Self::UintBig(a), Self::UintBig(b)) => a.partial_cmp(b),
843 (Self::Ulid(a), Self::Ulid(b)) => a.partial_cmp(b),
844 (Self::Map(a), Self::Map(b)) => {
845 for ((left_key, left_value), (right_key, right_value)) in a.iter().zip(b.iter()) {
846 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
847 if key_cmp != Ordering::Equal {
848 return Some(key_cmp);
849 }
850
851 match left_value.partial_cmp(right_value) {
852 Some(Ordering::Equal) => {}
853 non_eq => return non_eq,
854 }
855 }
856 a.len().partial_cmp(&b.len())
857 }
858
859 _ => None,
861 }
862 }
863}
864
865#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd, Serialize)]
871pub struct ValueEnum {
872 pub variant: String,
873 pub path: Option<String>,
874 pub payload: Option<Box<Value>>,
875}
876
877impl ValueEnum {
878 #[must_use]
879 pub fn new(variant: &str, path: Option<&str>) -> Self {
881 Self {
882 variant: variant.to_string(),
883 path: path.map(ToString::to_string),
884 payload: None,
885 }
886 }
887
888 #[must_use]
889 pub fn strict<E: Path>(variant: &str) -> Self {
891 Self::new(variant, Some(E::PATH))
892 }
893
894 #[must_use]
895 pub fn from_enum<E: EnumValue>(value: E) -> Self {
897 value.to_value_enum()
898 }
899
900 #[must_use]
901 pub fn loose(variant: &str) -> Self {
903 Self::new(variant, None)
904 }
905
906 #[must_use]
907 pub fn with_payload(mut self, payload: Value) -> Self {
909 self.payload = Some(Box::new(payload));
910 self
911 }
912}