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)]
109pub enum Value {
110 Account(Account),
111 Blob(Vec<u8>),
112 Bool(bool),
113 Date(Date),
114 Decimal(Decimal),
115 Duration(Duration),
116 Enum(ValueEnum),
117 E8s(E8s),
118 E18s(E18s),
119 Float32(Float32),
120 Float64(Float64),
121 Int(i64),
122 Int128(Int128),
123 IntBig(Int),
124 List(Vec<Self>),
128 Map(Vec<(Self, Self)>),
132 None,
133 Principal(Principal),
134 Subaccount(Subaccount),
135 Text(String),
136 Timestamp(Timestamp),
137 Uint(u64),
138 Uint128(Nat128),
139 UintBig(Nat),
140 Ulid(Ulid),
141 Unit,
142 Unsupported,
143}
144
145macro_rules! value_is_numeric_from_registry {
147 ( @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) ),* $(,)? ) => {
148 match $value {
149 $( $value_pat => $is_numeric, )*
150 _ => false,
151 }
152 };
153}
154
155macro_rules! value_supports_numeric_coercion_from_registry {
156 ( @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) ),* $(,)? ) => {
157 match $value {
158 $( $value_pat => $supports_numeric_coercion, )*
159 _ => false,
160 }
161 };
162}
163
164macro_rules! value_storage_key_case {
165 ( $value:expr, Unit, true ) => {
166 if let Value::Unit = $value {
167 Some(StorageKey::Unit)
168 } else {
169 None
170 }
171 };
172 ( $value:expr, $scalar:ident, true ) => {
173 if let Value::$scalar(v) = $value {
174 Some(StorageKey::$scalar(*v))
175 } else {
176 None
177 }
178 };
179 ( $value:expr, $scalar:ident, false ) => {
180 None
181 };
182}
183
184macro_rules! value_storage_key_from_registry {
185 ( @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) ),* $(,)? ) => {
186 {
187 let mut key = None;
188 $(
189 match key {
190 Some(_) => {}
191 None => {
192 key = value_storage_key_case!($value, $scalar, $is_keyable);
193 }
194 }
195 )*
196 key
197 }
198 };
199}
200
201macro_rules! value_coercion_family_from_registry {
202 ( @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) ),* $(,)? ) => {
203 match $value {
204 $( $value_pat => $coercion_family, )*
205 Value::List(_) => CoercionFamily::Collection,
206 Value::Map(_) => CoercionFamily::Collection,
207 Value::None => CoercionFamily::Null,
208 Value::Unsupported => CoercionFamily::Unsupported,
209 }
210 };
211}
212
213impl Value {
214 pub fn from_slice<T>(items: &[T]) -> Self
223 where
224 T: Into<Self> + Clone,
225 {
226 Self::List(items.iter().cloned().map(Into::into).collect())
227 }
228
229 pub fn from_list<T>(items: Vec<T>) -> Self
233 where
234 T: Into<Self>,
235 {
236 Self::List(items.into_iter().map(Into::into).collect())
237 }
238
239 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
247 let normalized = Self::normalize_map_entries(entries)?;
248 Ok(Self::Map(normalized))
249 }
250
251 pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
253 for (index, (key, value)) in entries.iter().enumerate() {
254 if matches!(key, Self::None) {
255 return Err(MapValueError::EmptyKey { index });
256 }
257 if !key.is_scalar() || matches!(key, Self::Unsupported) {
258 return Err(MapValueError::NonScalarKey {
259 index,
260 key: key.clone(),
261 });
262 }
263
264 if !value.is_scalar() || matches!(value, Self::Unsupported) {
265 return Err(MapValueError::NonScalarValue {
266 index,
267 value: value.clone(),
268 });
269 }
270 }
271
272 Ok(())
273 }
274
275 pub fn normalize_map_entries(
277 mut entries: Vec<(Self, Self)>,
278 ) -> Result<Vec<(Self, Self)>, MapValueError> {
279 Self::validate_map_entries(&entries)?;
280 entries
281 .sort_by(|(left_key, _), (right_key, _)| Self::canonical_cmp_key(left_key, right_key));
282
283 for i in 1..entries.len() {
284 let (left_key, _) = &entries[i - 1];
285 let (right_key, _) = &entries[i];
286 if Self::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
287 return Err(MapValueError::DuplicateKey {
288 left_index: i - 1,
289 right_index: i,
290 });
291 }
292 }
293
294 Ok(entries)
295 }
296
297 pub fn from_enum<E: EnumValue>(value: E) -> Self {
299 Self::Enum(value.to_value_enum())
300 }
301
302 #[must_use]
304 pub fn enum_strict<E: Path>(variant: &str) -> Self {
305 Self::Enum(ValueEnum::strict::<E>(variant))
306 }
307
308 #[must_use]
315 pub const fn is_numeric(&self) -> bool {
316 scalar_registry!(value_is_numeric_from_registry, self)
317 }
318
319 #[must_use]
321 pub const fn supports_numeric_coercion(&self) -> bool {
322 scalar_registry!(value_supports_numeric_coercion_from_registry, self)
323 }
324
325 #[must_use]
327 pub const fn is_text(&self) -> bool {
328 matches!(self, Self::Text(_))
329 }
330
331 #[must_use]
333 pub const fn is_unit(&self) -> bool {
334 matches!(self, Self::Unit)
335 }
336
337 #[must_use]
338 pub const fn is_scalar(&self) -> bool {
339 match self {
340 Self::List(_) | Self::Map(_) | Self::Unit => false,
342 _ => true,
343 }
344 }
345
346 #[must_use]
348 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
349 let rank = Self::canonical_key_rank(left).cmp(&Self::canonical_key_rank(right));
350 if rank != Ordering::Equal {
351 return rank;
352 }
353
354 match (left, right) {
355 (Self::Account(a), Self::Account(b)) => a.cmp(b),
356 (Self::Blob(a), Self::Blob(b)) => a.cmp(b),
357 (Self::Bool(a), Self::Bool(b)) => a.cmp(b),
358 (Self::Date(a), Self::Date(b)) => a.cmp(b),
359 (Self::Decimal(a), Self::Decimal(b)) => a.cmp(b),
360 (Self::Duration(a), Self::Duration(b)) => a.cmp(b),
361 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
362 (Self::E8s(a), Self::E8s(b)) => a.cmp(b),
363 (Self::E18s(a), Self::E18s(b)) => a.cmp(b),
364 (Self::Float32(a), Self::Float32(b)) => a.cmp(b),
365 (Self::Float64(a), Self::Float64(b)) => a.cmp(b),
366 (Self::Int(a), Self::Int(b)) => a.cmp(b),
367 (Self::Int128(a), Self::Int128(b)) => a.cmp(b),
368 (Self::IntBig(a), Self::IntBig(b)) => a.cmp(b),
369 (Self::Principal(a), Self::Principal(b)) => a.cmp(b),
370 (Self::Subaccount(a), Self::Subaccount(b)) => a.cmp(b),
371 (Self::Text(a), Self::Text(b)) => a.cmp(b),
372 (Self::Timestamp(a), Self::Timestamp(b)) => a.cmp(b),
373 (Self::Uint(a), Self::Uint(b)) => a.cmp(b),
374 (Self::Uint128(a), Self::Uint128(b)) => a.cmp(b),
375 (Self::UintBig(a), Self::UintBig(b)) => a.cmp(b),
376 (Self::Ulid(a), Self::Ulid(b)) => a.cmp(b),
377 _ => Ordering::Equal,
378 }
379 }
380
381 const fn canonical_key_rank(value: &Self) -> u8 {
382 match value {
383 Self::Account(_) => 0,
384 Self::Blob(_) => 1,
385 Self::Bool(_) => 2,
386 Self::Date(_) => 3,
387 Self::Decimal(_) => 4,
388 Self::Duration(_) => 5,
389 Self::Enum(_) => 6,
390 Self::E8s(_) => 7,
391 Self::E18s(_) => 8,
392 Self::Float32(_) => 9,
393 Self::Float64(_) => 10,
394 Self::Int(_) => 11,
395 Self::Int128(_) => 12,
396 Self::IntBig(_) => 13,
397 Self::List(_) => 14,
398 Self::Map(_) => 15,
399 Self::None => 16,
400 Self::Principal(_) => 17,
401 Self::Subaccount(_) => 18,
402 Self::Text(_) => 19,
403 Self::Timestamp(_) => 20,
404 Self::Uint(_) => 21,
405 Self::Uint128(_) => 22,
406 Self::UintBig(_) => 23,
407 Self::Ulid(_) => 24,
408 Self::Unit => 25,
409 Self::Unsupported => 26,
410 }
411 }
412
413 fn numeric_repr(&self) -> NumericRepr {
414 if !self.supports_numeric_coercion() {
416 return NumericRepr::None;
417 }
418
419 if let Some(d) = self.to_decimal() {
420 return NumericRepr::Decimal(d);
421 }
422 if let Some(f) = self.to_f64_lossless() {
423 return NumericRepr::F64(f);
424 }
425 NumericRepr::None
426 }
427
428 #[must_use]
437 pub const fn as_storage_key(&self) -> Option<StorageKey> {
438 scalar_registry!(value_storage_key_from_registry, self)
439 }
440
441 #[must_use]
442 pub const fn as_text(&self) -> Option<&str> {
443 if let Self::Text(s) = self {
444 Some(s.as_str())
445 } else {
446 None
447 }
448 }
449
450 #[must_use]
451 pub const fn as_list(&self) -> Option<&[Self]> {
452 if let Self::List(xs) = self {
453 Some(xs.as_slice())
454 } else {
455 None
456 }
457 }
458
459 #[must_use]
460 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
461 if let Self::Map(entries) = self {
462 Some(entries.as_slice())
463 } else {
464 None
465 }
466 }
467
468 fn to_decimal(&self) -> Option<Decimal> {
469 match self {
470 Self::Decimal(d) => Some(*d),
471 Self::Duration(d) => Decimal::from_u64(d.get()),
472 Self::E8s(v) => Some(v.to_decimal()),
473 Self::E18s(v) => v.to_decimal(),
474 Self::Float64(f) => Decimal::from_f64(f.get()),
475 Self::Float32(f) => Decimal::from_f32(f.get()),
476 Self::Int(i) => Decimal::from_i64(*i),
477 Self::Int128(i) => Decimal::from_i128(i.get()),
478 Self::IntBig(i) => i.to_i128().and_then(Decimal::from_i128),
479 Self::Timestamp(t) => Decimal::from_u64(t.get()),
480 Self::Uint(u) => Decimal::from_u64(*u),
481 Self::Uint128(u) => Decimal::from_u128(u.get()),
482 Self::UintBig(u) => u.to_u128().and_then(Decimal::from_u128),
483
484 _ => None,
485 }
486 }
487
488 #[allow(clippy::cast_precision_loss)]
490 fn to_f64_lossless(&self) -> Option<f64> {
491 match self {
492 Self::Duration(d) if d.get() <= F64_SAFE_U64 => Some(d.get() as f64),
493 Self::Float64(f) => Some(f.get()),
494 Self::Float32(f) => Some(f64::from(f.get())),
495 Self::Int(i) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(i) => Some(*i as f64),
496 Self::Int128(i) if (-F64_SAFE_I128..=F64_SAFE_I128).contains(&i.get()) => {
497 Some(i.get() as f64)
498 }
499 Self::IntBig(i) => i.to_i128().and_then(|v| {
500 (-F64_SAFE_I128..=F64_SAFE_I128)
501 .contains(&v)
502 .then_some(v as f64)
503 }),
504 Self::Timestamp(t) if t.get() <= F64_SAFE_U64 => Some(t.get() as f64),
505 Self::Uint(u) if *u <= F64_SAFE_U64 => Some(*u as f64),
506 Self::Uint128(u) if u.get() <= F64_SAFE_U128 => Some(u.get() as f64),
507 Self::UintBig(u) => u
508 .to_u128()
509 .and_then(|v| (v <= F64_SAFE_U128).then_some(v as f64)),
510
511 _ => None,
512 }
513 }
514
515 #[must_use]
517 pub fn cmp_numeric(&self, other: &Self) -> Option<Ordering> {
518 if !self.supports_numeric_coercion() || !other.supports_numeric_coercion() {
519 return None;
520 }
521
522 match (self.numeric_repr(), other.numeric_repr()) {
523 (NumericRepr::Decimal(a), NumericRepr::Decimal(b)) => a.partial_cmp(&b),
524 (NumericRepr::F64(a), NumericRepr::F64(b)) => a.partial_cmp(&b),
525 _ => None,
526 }
527 }
528
529 fn fold_ci(s: &str) -> std::borrow::Cow<'_, str> {
534 if s.is_ascii() {
535 return std::borrow::Cow::Owned(s.to_ascii_lowercase());
536 }
537 std::borrow::Cow::Owned(s.to_lowercase())
540 }
541
542 fn text_with_mode(s: &'_ str, mode: TextMode) -> std::borrow::Cow<'_, str> {
543 match mode {
544 TextMode::Cs => std::borrow::Cow::Borrowed(s),
545 TextMode::Ci => Self::fold_ci(s),
546 }
547 }
548
549 fn text_op(
550 &self,
551 other: &Self,
552 mode: TextMode,
553 f: impl Fn(&str, &str) -> bool,
554 ) -> Option<bool> {
555 let (a, b) = (self.as_text()?, other.as_text()?);
556 let a = Self::text_with_mode(a, mode);
557 let b = Self::text_with_mode(b, mode);
558 Some(f(&a, &b))
559 }
560
561 fn ci_key(&self) -> Option<String> {
562 match self {
563 Self::Text(s) => Some(Self::fold_ci(s).into_owned()),
564 Self::Ulid(u) => Some(u.to_string().to_ascii_lowercase()),
565 Self::Principal(p) => Some(p.to_string().to_ascii_lowercase()),
566 Self::Account(a) => Some(a.to_string().to_ascii_lowercase()),
567 _ => None,
568 }
569 }
570
571 fn eq_ci(a: &Self, b: &Self) -> bool {
572 if let (Some(ak), Some(bk)) = (a.ci_key(), b.ci_key()) {
573 return ak == bk;
574 }
575
576 a == b
577 }
578
579 fn normalize_list_ref(v: &Self) -> Vec<&Self> {
580 match v {
581 Self::List(vs) => vs.iter().collect(),
582 v => vec![v],
583 }
584 }
585
586 fn contains_by<F>(&self, needle: &Self, eq: F) -> Option<bool>
587 where
588 F: Fn(&Self, &Self) -> bool,
589 {
590 self.as_list()
591 .map(|items| items.iter().any(|v| eq(v, needle)))
592 }
593
594 #[allow(clippy::unnecessary_wraps)]
595 fn contains_any_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
596 where
597 F: Fn(&Self, &Self) -> bool,
598 {
599 let needles = Self::normalize_list_ref(needles);
600 match self {
601 Self::List(items) => Some(needles.iter().any(|n| items.iter().any(|v| eq(v, n)))),
602 scalar => Some(needles.iter().any(|n| eq(scalar, n))),
603 }
604 }
605
606 #[allow(clippy::unnecessary_wraps)]
607 fn contains_all_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
608 where
609 F: Fn(&Self, &Self) -> bool,
610 {
611 let needles = Self::normalize_list_ref(needles);
612 match self {
613 Self::List(items) => Some(needles.iter().all(|n| items.iter().any(|v| eq(v, n)))),
614 scalar => Some(needles.len() == 1 && eq(scalar, needles[0])),
615 }
616 }
617
618 fn in_list_by<F>(&self, haystack: &Self, eq: F) -> Option<bool>
619 where
620 F: Fn(&Self, &Self) -> bool,
621 {
622 if let Self::List(items) = haystack {
623 Some(items.iter().any(|h| eq(h, self)))
624 } else {
625 None
626 }
627 }
628
629 #[must_use]
630 pub fn text_eq(&self, other: &Self, mode: TextMode) -> Option<bool> {
632 self.text_op(other, mode, |a, b| a == b)
633 }
634
635 #[must_use]
636 pub fn text_contains(&self, needle: &Self, mode: TextMode) -> Option<bool> {
638 self.text_op(needle, mode, |a, b| a.contains(b))
639 }
640
641 #[must_use]
642 pub fn text_starts_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
644 self.text_op(needle, mode, |a, b| a.starts_with(b))
645 }
646
647 #[must_use]
648 pub fn text_ends_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
650 self.text_op(needle, mode, |a, b| a.ends_with(b))
651 }
652
653 #[must_use]
658 pub const fn is_empty(&self) -> Option<bool> {
659 match self {
660 Self::List(xs) => Some(xs.is_empty()),
661 Self::Map(entries) => Some(entries.is_empty()),
662 Self::Text(s) => Some(s.is_empty()),
663 Self::Blob(b) => Some(b.is_empty()),
664
665 Self::None => Some(true),
667
668 _ => None,
669 }
670 }
671
672 #[must_use]
673 pub fn is_not_empty(&self) -> Option<bool> {
675 self.is_empty().map(|b| !b)
676 }
677
678 #[must_use]
683 pub fn contains(&self, needle: &Self) -> Option<bool> {
685 self.contains_by(needle, |a, b| a == b)
686 }
687
688 #[must_use]
689 pub fn contains_any(&self, needles: &Self) -> Option<bool> {
691 self.contains_any_by(needles, |a, b| a == b)
692 }
693
694 #[must_use]
695 pub fn contains_all(&self, needles: &Self) -> Option<bool> {
697 self.contains_all_by(needles, |a, b| a == b)
698 }
699
700 #[must_use]
701 pub fn in_list(&self, haystack: &Self) -> Option<bool> {
703 self.in_list_by(haystack, |a, b| a == b)
704 }
705
706 #[must_use]
707 pub fn contains_ci(&self, needle: &Self) -> Option<bool> {
709 match self {
710 Self::List(_) => self.contains_by(needle, Self::eq_ci),
711 _ => Some(Self::eq_ci(self, needle)),
712 }
713 }
714
715 #[must_use]
716 pub fn contains_any_ci(&self, needles: &Self) -> Option<bool> {
718 self.contains_any_by(needles, Self::eq_ci)
719 }
720
721 #[must_use]
722 pub fn contains_all_ci(&self, needles: &Self) -> Option<bool> {
724 self.contains_all_by(needles, Self::eq_ci)
725 }
726
727 #[must_use]
728 pub fn in_list_ci(&self, haystack: &Self) -> Option<bool> {
730 self.in_list_by(haystack, Self::eq_ci)
731 }
732}
733
734impl FieldValue for Value {
735 fn to_value(&self) -> Value {
736 self.clone()
737 }
738}
739
740#[macro_export]
741macro_rules! impl_from_for {
742 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
743 $(
744 impl From<$type> for Value {
745 fn from(v: $type) -> Self {
746 Self::$variant(v.into())
747 }
748 }
749 )*
750 };
751}
752
753impl_from_for! {
754 Account => Account,
755 Date => Date,
756 Decimal => Decimal,
757 Duration => Duration,
758 E8s => E8s,
759 E18s => E18s,
760 bool => Bool,
761 i8 => Int,
762 i16 => Int,
763 i32 => Int,
764 i64 => Int,
765 i128 => Int128,
766 Int => IntBig,
767 Principal => Principal,
768 Subaccount => Subaccount,
769 &str => Text,
770 String => Text,
771 Timestamp => Timestamp,
772 u8 => Uint,
773 u16 => Uint,
774 u32 => Uint,
775 u64 => Uint,
776 u128 => Uint128,
777 Nat => UintBig,
778 Ulid => Ulid,
779}
780
781impl CoercionFamilyExt for Value {
782 fn coercion_family(&self) -> CoercionFamily {
788 scalar_registry!(value_coercion_family_from_registry, self)
789 }
790}
791
792impl From<Vec<Self>> for Value {
793 fn from(vec: Vec<Self>) -> Self {
794 Self::List(vec)
795 }
796}
797
798impl From<Vec<(Self, Self)>> for Value {
799 fn from(entries: Vec<(Self, Self)>) -> Self {
800 Self::from_map(entries).unwrap_or_else(|err| {
801 panic!("invalid map invariant while constructing Value::Map from Vec: {err}")
802 })
803 }
804}
805
806impl From<()> for Value {
807 fn from((): ()) -> Self {
808 Self::Unit
809 }
810}
811
812impl PartialOrd for Value {
818 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
819 match (self, other) {
820 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
821 (Self::Date(a), Self::Date(b)) => a.partial_cmp(b),
822 (Self::Decimal(a), Self::Decimal(b)) => a.partial_cmp(b),
823 (Self::Duration(a), Self::Duration(b)) => a.partial_cmp(b),
824 (Self::E8s(a), Self::E8s(b)) => a.partial_cmp(b),
825 (Self::E18s(a), Self::E18s(b)) => a.partial_cmp(b),
826 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b),
827 (Self::Float32(a), Self::Float32(b)) => a.partial_cmp(b),
828 (Self::Float64(a), Self::Float64(b)) => a.partial_cmp(b),
829 (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
830 (Self::Int128(a), Self::Int128(b)) => a.partial_cmp(b),
831 (Self::IntBig(a), Self::IntBig(b)) => a.partial_cmp(b),
832 (Self::Principal(a), Self::Principal(b)) => a.partial_cmp(b),
833 (Self::Subaccount(a), Self::Subaccount(b)) => a.partial_cmp(b),
834 (Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
835 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
836 (Self::Uint(a), Self::Uint(b)) => a.partial_cmp(b),
837 (Self::Uint128(a), Self::Uint128(b)) => a.partial_cmp(b),
838 (Self::UintBig(a), Self::UintBig(b)) => a.partial_cmp(b),
839 (Self::Ulid(a), Self::Ulid(b)) => a.partial_cmp(b),
840 (Self::Map(a), Self::Map(b)) => {
841 for ((left_key, left_value), (right_key, right_value)) in a.iter().zip(b.iter()) {
842 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
843 if key_cmp != Ordering::Equal {
844 return Some(key_cmp);
845 }
846
847 match left_value.partial_cmp(right_value) {
848 Some(Ordering::Equal) => {}
849 non_eq => return non_eq,
850 }
851 }
852 a.len().partial_cmp(&b.len())
853 }
854
855 _ => None,
857 }
858 }
859}
860
861#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd, Serialize)]
867pub struct ValueEnum {
868 pub variant: String,
869 pub path: Option<String>,
870 pub payload: Option<Box<Value>>,
871}
872
873impl ValueEnum {
874 #[must_use]
875 pub fn new(variant: &str, path: Option<&str>) -> Self {
877 Self {
878 variant: variant.to_string(),
879 path: path.map(ToString::to_string),
880 payload: None,
881 }
882 }
883
884 #[must_use]
885 pub fn strict<E: Path>(variant: &str) -> Self {
887 Self::new(variant, Some(E::PATH))
888 }
889
890 #[must_use]
891 pub fn from_enum<E: EnumValue>(value: E) -> Self {
893 value.to_value_enum()
894 }
895
896 #[must_use]
897 pub fn loose(variant: &str) -> Self {
899 Self::new(variant, None)
900 }
901
902 #[must_use]
903 pub fn with_payload(mut self, payload: Value) -> Self {
905 self.payload = Some(Box::new(payload));
906 self
907 }
908}