1mod coercion;
2mod compare;
3mod rank;
4mod tag;
5
6#[cfg(test)]
7mod tests;
8
9use crate::{
10 db::store::StorageKey,
11 prelude::*,
12 traits::{EnumValue, FieldValue, NumFromPrimitive},
13 types::*,
14};
15use candid::CandidType;
16use serde::{Deserialize, Deserializer, Serialize};
17use std::cmp::Ordering;
18
19pub use coercion::{CoercionFamily, CoercionFamilyExt};
21pub(crate) use tag::ValueTag;
22
23const F64_SAFE_I64: i64 = 1i64 << 53;
28const F64_SAFE_U64: u64 = 1u64 << 53;
29const F64_SAFE_I128: i128 = 1i128 << 53;
30const F64_SAFE_U128: u128 = 1u128 << 53;
31
32enum NumericRepr {
37 Decimal(Decimal),
38 F64(f64),
39 None,
40}
41
42#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub enum TextMode {
48 Cs, Ci, }
51
52#[derive(Clone, Debug, Eq, PartialEq)]
59pub enum MapValueError {
60 EmptyKey {
61 index: usize,
62 },
63 NonScalarKey {
64 index: usize,
65 key: Value,
66 },
67 NonScalarValue {
68 index: usize,
69 value: Value,
70 },
71 DuplicateKey {
72 left_index: usize,
73 right_index: usize,
74 },
75}
76
77impl std::fmt::Display for MapValueError {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 Self::EmptyKey { index } => write!(f, "map key at index {index} must be non-null"),
81 Self::NonScalarKey { index, key } => {
82 write!(f, "map key at index {index} is not scalar: {key:?}")
83 }
84 Self::NonScalarValue { index, value } => {
85 write!(
86 f,
87 "map value at index {index} is not scalar/ref-like: {value:?}"
88 )
89 }
90 Self::DuplicateKey {
91 left_index,
92 right_index,
93 } => write!(
94 f,
95 "map contains duplicate keys at normalized positions {left_index} and {right_index}"
96 ),
97 }
98 }
99}
100
101impl std::error::Error for MapValueError {}
102
103#[derive(Clone, Debug, Eq, PartialEq)]
109pub enum SchemaInvariantError {
110 InvalidMapValue(MapValueError),
111}
112
113impl std::fmt::Display for SchemaInvariantError {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 match self {
116 Self::InvalidMapValue(err) => write!(f, "{err}"),
117 }
118 }
119}
120
121impl std::error::Error for SchemaInvariantError {}
122
123impl From<MapValueError> for SchemaInvariantError {
124 fn from(value: MapValueError) -> Self {
125 Self::InvalidMapValue(value)
126 }
127}
128
129#[derive(CandidType, Clone, Debug, Eq, PartialEq, Serialize)]
138pub enum Value {
139 Account(Account),
140 Blob(Vec<u8>),
141 Bool(bool),
142 Date(Date),
143 Decimal(Decimal),
144 Duration(Duration),
145 Enum(ValueEnum),
146 E8s(E8s),
147 E18s(E18s),
148 Float32(Float32),
149 Float64(Float64),
150 Int(i64),
151 Int128(Int128),
152 IntBig(Int),
153 List(Vec<Self>),
157 Map(Vec<(Self, Self)>),
164 Null,
165 Principal(Principal),
166 Subaccount(Subaccount),
167 Text(String),
168 Timestamp(Timestamp),
169 Uint(u64),
170 Uint128(Nat128),
171 UintBig(Nat),
172 Ulid(Ulid),
173 Unit,
174}
175
176#[derive(Deserialize)]
178enum ValueWire {
179 Account(Account),
180 Blob(Vec<u8>),
181 Bool(bool),
182 Date(Date),
183 Decimal(Decimal),
184 Duration(Duration),
185 Enum(ValueEnum),
186 E8s(E8s),
187 E18s(E18s),
188 Float32(Float32),
189 Float64(Float64),
190 Int(i64),
191 Int128(Int128),
192 IntBig(Int),
193 List(Vec<Self>),
194 Map(Vec<(Self, Self)>),
195 Null,
196 Principal(Principal),
197 Subaccount(Subaccount),
198 Text(String),
199 Timestamp(Timestamp),
200 Uint(u64),
201 Uint128(Nat128),
202 UintBig(Nat),
203 Ulid(Ulid),
204 Unit,
205}
206
207impl ValueWire {
208 fn into_value(self) -> Result<Value, MapValueError> {
209 match self {
210 Self::Account(v) => Ok(Value::Account(v)),
211 Self::Blob(v) => Ok(Value::Blob(v)),
212 Self::Bool(v) => Ok(Value::Bool(v)),
213 Self::Date(v) => Ok(Value::Date(v)),
214 Self::Decimal(v) => Ok(Value::Decimal(v)),
215 Self::Duration(v) => Ok(Value::Duration(v)),
216 Self::Enum(v) => Ok(Value::Enum(v)),
217 Self::E8s(v) => Ok(Value::E8s(v)),
218 Self::E18s(v) => Ok(Value::E18s(v)),
219 Self::Float32(v) => Ok(Value::Float32(v)),
220 Self::Float64(v) => Ok(Value::Float64(v)),
221 Self::Int(v) => Ok(Value::Int(v)),
222 Self::Int128(v) => Ok(Value::Int128(v)),
223 Self::IntBig(v) => Ok(Value::IntBig(v)),
224 Self::List(items) => {
225 let items = items
226 .into_iter()
227 .map(Self::into_value)
228 .collect::<Result<Vec<_>, _>>()?;
229 Ok(Value::List(items))
230 }
231 Self::Map(entries) => {
232 let entries = entries
233 .into_iter()
234 .map(|(key, value)| Ok((key.into_value()?, value.into_value()?)))
235 .collect::<Result<Vec<_>, MapValueError>>()?;
236 Value::from_map(entries)
237 }
238 Self::Null => Ok(Value::Null),
239 Self::Principal(v) => Ok(Value::Principal(v)),
240 Self::Subaccount(v) => Ok(Value::Subaccount(v)),
241 Self::Text(v) => Ok(Value::Text(v)),
242 Self::Timestamp(v) => Ok(Value::Timestamp(v)),
243 Self::Uint(v) => Ok(Value::Uint(v)),
244 Self::Uint128(v) => Ok(Value::Uint128(v)),
245 Self::UintBig(v) => Ok(Value::UintBig(v)),
246 Self::Ulid(v) => Ok(Value::Ulid(v)),
247 Self::Unit => Ok(Value::Unit),
248 }
249 }
250}
251
252impl<'de> Deserialize<'de> for Value {
253 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
254 where
255 D: Deserializer<'de>,
256 {
257 let wire = ValueWire::deserialize(deserializer)?;
258 wire.into_value().map_err(serde::de::Error::custom)
259 }
260}
261
262macro_rules! value_is_numeric_from_registry {
264 ( @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) ),* $(,)? ) => {
265 match $value {
266 $( $value_pat => $is_numeric, )*
267 _ => false,
268 }
269 };
270}
271
272macro_rules! value_supports_numeric_coercion_from_registry {
273 ( @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) ),* $(,)? ) => {
274 match $value {
275 $( $value_pat => $supports_numeric_coercion, )*
276 _ => false,
277 }
278 };
279}
280
281macro_rules! value_storage_key_case {
282 ( $value:expr, Unit, true ) => {
283 if let Value::Unit = $value {
284 Some(StorageKey::Unit)
285 } else {
286 None
287 }
288 };
289 ( $value:expr, $scalar:ident, true ) => {
290 if let Value::$scalar(v) = $value {
291 Some(StorageKey::$scalar(*v))
292 } else {
293 None
294 }
295 };
296 ( $value:expr, $scalar:ident, false ) => {
297 None
298 };
299}
300
301macro_rules! value_storage_key_from_registry {
302 ( @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) ),* $(,)? ) => {
303 {
304 let mut key = None;
305 $(
306 match key {
307 Some(_) => {}
308 None => {
309 key = value_storage_key_case!($value, $scalar, $is_keyable);
310 }
311 }
312 )*
313 key
314 }
315 };
316}
317
318macro_rules! value_coercion_family_from_registry {
319 ( @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) ),* $(,)? ) => {
320 match $value {
321 $( $value_pat => $coercion_family, )*
322 Value::List(_) => CoercionFamily::Collection,
323 Value::Map(_) => CoercionFamily::Collection,
324 Value::Null => CoercionFamily::Null,
325 }
326 };
327}
328
329impl Value {
330 pub fn from_slice<T>(items: &[T]) -> Self
339 where
340 T: Into<Self> + Clone,
341 {
342 Self::List(items.iter().cloned().map(Into::into).collect())
343 }
344
345 pub fn from_list<T>(items: Vec<T>) -> Self
349 where
350 T: Into<Self>,
351 {
352 Self::List(items.into_iter().map(Into::into).collect())
353 }
354
355 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
363 let normalized = Self::normalize_map_entries(entries)?;
364 Ok(Self::Map(normalized))
365 }
366
367 pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
369 for (index, (key, value)) in entries.iter().enumerate() {
370 if matches!(key, Self::Null) {
371 return Err(MapValueError::EmptyKey { index });
372 }
373 if !key.is_scalar() {
374 return Err(MapValueError::NonScalarKey {
375 index,
376 key: key.clone(),
377 });
378 }
379
380 if !value.is_scalar() {
381 return Err(MapValueError::NonScalarValue {
382 index,
383 value: value.clone(),
384 });
385 }
386 }
387
388 Ok(())
389 }
390
391 pub fn normalize_map_entries(
393 mut entries: Vec<(Self, Self)>,
394 ) -> Result<Vec<(Self, Self)>, MapValueError> {
395 Self::validate_map_entries(&entries)?;
396 entries
397 .sort_by(|(left_key, _), (right_key, _)| Self::canonical_cmp_key(left_key, right_key));
398
399 for i in 1..entries.len() {
400 let (left_key, _) = &entries[i - 1];
401 let (right_key, _) = &entries[i];
402 if Self::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
403 return Err(MapValueError::DuplicateKey {
404 left_index: i - 1,
405 right_index: i,
406 });
407 }
408 }
409
410 Ok(entries)
411 }
412
413 pub fn from_enum<E: EnumValue>(value: E) -> Self {
415 Self::Enum(value.to_value_enum())
416 }
417
418 #[must_use]
420 pub fn enum_strict<E: Path>(variant: &str) -> Self {
421 Self::Enum(ValueEnum::strict::<E>(variant))
422 }
423
424 #[must_use]
431 pub const fn is_numeric(&self) -> bool {
432 scalar_registry!(value_is_numeric_from_registry, self)
433 }
434
435 #[must_use]
437 pub const fn supports_numeric_coercion(&self) -> bool {
438 scalar_registry!(value_supports_numeric_coercion_from_registry, self)
439 }
440
441 #[must_use]
443 pub const fn is_text(&self) -> bool {
444 matches!(self, Self::Text(_))
445 }
446
447 #[must_use]
449 pub const fn is_unit(&self) -> bool {
450 matches!(self, Self::Unit)
451 }
452
453 #[must_use]
454 pub const fn is_scalar(&self) -> bool {
455 match self {
456 Self::List(_) | Self::Map(_) | Self::Unit => false,
458 _ => true,
459 }
460 }
461
462 #[must_use]
464 pub(crate) const fn canonical_tag(&self) -> ValueTag {
465 tag::canonical_tag(self)
466 }
467
468 #[must_use]
470 pub(crate) const fn canonical_rank(&self) -> u8 {
471 rank::canonical_rank(self)
472 }
473
474 #[must_use]
476 pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
477 compare::canonical_cmp(left, right)
478 }
479
480 #[must_use]
482 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
483 compare::canonical_cmp_key(left, right)
484 }
485
486 #[must_use]
490 pub(crate) fn strict_order_cmp(left: &Self, right: &Self) -> Option<Ordering> {
491 compare::strict_order_cmp(left, right)
492 }
493
494 fn numeric_repr(&self) -> NumericRepr {
495 if !self.supports_numeric_coercion() {
497 return NumericRepr::None;
498 }
499
500 if let Some(d) = self.to_decimal() {
501 return NumericRepr::Decimal(d);
502 }
503 if let Some(f) = self.to_f64_lossless() {
504 return NumericRepr::F64(f);
505 }
506 NumericRepr::None
507 }
508
509 #[must_use]
518 pub const fn as_storage_key(&self) -> Option<StorageKey> {
519 scalar_registry!(value_storage_key_from_registry, self)
520 }
521
522 #[must_use]
523 pub const fn as_text(&self) -> Option<&str> {
524 if let Self::Text(s) = self {
525 Some(s.as_str())
526 } else {
527 None
528 }
529 }
530
531 #[must_use]
532 pub const fn as_list(&self) -> Option<&[Self]> {
533 if let Self::List(xs) = self {
534 Some(xs.as_slice())
535 } else {
536 None
537 }
538 }
539
540 #[must_use]
541 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
542 if let Self::Map(entries) = self {
543 Some(entries.as_slice())
544 } else {
545 None
546 }
547 }
548
549 fn to_decimal(&self) -> Option<Decimal> {
550 match self {
551 Self::Decimal(d) => Some(*d),
552 Self::Duration(d) => Decimal::from_u64(d.get()),
553 Self::E8s(v) => Some(v.to_decimal()),
554 Self::E18s(v) => v.to_decimal(),
555 Self::Float64(f) => Decimal::from_f64(f.get()),
556 Self::Float32(f) => Decimal::from_f32(f.get()),
557 Self::Int(i) => Decimal::from_i64(*i),
558 Self::Int128(i) => Decimal::from_i128(i.get()),
559 Self::IntBig(i) => i.to_i128().and_then(Decimal::from_i128),
560 Self::Timestamp(t) => Decimal::from_u64(t.get()),
561 Self::Uint(u) => Decimal::from_u64(*u),
562 Self::Uint128(u) => Decimal::from_u128(u.get()),
563 Self::UintBig(u) => u.to_u128().and_then(Decimal::from_u128),
564
565 _ => None,
566 }
567 }
568
569 #[allow(clippy::cast_precision_loss)]
571 fn to_f64_lossless(&self) -> Option<f64> {
572 match self {
573 Self::Duration(d) if d.get() <= F64_SAFE_U64 => Some(d.get() as f64),
574 Self::Float64(f) => Some(f.get()),
575 Self::Float32(f) => Some(f64::from(f.get())),
576 Self::Int(i) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(i) => Some(*i as f64),
577 Self::Int128(i) if (-F64_SAFE_I128..=F64_SAFE_I128).contains(&i.get()) => {
578 Some(i.get() as f64)
579 }
580 Self::IntBig(i) => i.to_i128().and_then(|v| {
581 (-F64_SAFE_I128..=F64_SAFE_I128)
582 .contains(&v)
583 .then_some(v as f64)
584 }),
585 Self::Timestamp(t) if t.get() <= F64_SAFE_U64 => Some(t.get() as f64),
586 Self::Uint(u) if *u <= F64_SAFE_U64 => Some(*u as f64),
587 Self::Uint128(u) if u.get() <= F64_SAFE_U128 => Some(u.get() as f64),
588 Self::UintBig(u) => u
589 .to_u128()
590 .and_then(|v| (v <= F64_SAFE_U128).then_some(v as f64)),
591
592 _ => None,
593 }
594 }
595
596 #[must_use]
598 pub fn cmp_numeric(&self, other: &Self) -> Option<Ordering> {
599 if !self.supports_numeric_coercion() || !other.supports_numeric_coercion() {
600 return None;
601 }
602
603 match (self.numeric_repr(), other.numeric_repr()) {
604 (NumericRepr::Decimal(a), NumericRepr::Decimal(b)) => a.partial_cmp(&b),
605 (NumericRepr::F64(a), NumericRepr::F64(b)) => a.partial_cmp(&b),
606 _ => None,
607 }
608 }
609
610 fn fold_ci(s: &str) -> std::borrow::Cow<'_, str> {
615 if s.is_ascii() {
616 return std::borrow::Cow::Owned(s.to_ascii_lowercase());
617 }
618 std::borrow::Cow::Owned(s.to_lowercase())
621 }
622
623 fn text_with_mode(s: &'_ str, mode: TextMode) -> std::borrow::Cow<'_, str> {
624 match mode {
625 TextMode::Cs => std::borrow::Cow::Borrowed(s),
626 TextMode::Ci => Self::fold_ci(s),
627 }
628 }
629
630 fn text_op(
631 &self,
632 other: &Self,
633 mode: TextMode,
634 f: impl Fn(&str, &str) -> bool,
635 ) -> Option<bool> {
636 let (a, b) = (self.as_text()?, other.as_text()?);
637 let a = Self::text_with_mode(a, mode);
638 let b = Self::text_with_mode(b, mode);
639 Some(f(&a, &b))
640 }
641
642 fn ci_key(&self) -> Option<String> {
643 match self {
644 Self::Text(s) => Some(Self::fold_ci(s).into_owned()),
645 Self::Ulid(u) => Some(u.to_string().to_ascii_lowercase()),
646 Self::Principal(p) => Some(p.to_string().to_ascii_lowercase()),
647 Self::Account(a) => Some(a.to_string().to_ascii_lowercase()),
648 _ => None,
649 }
650 }
651
652 fn eq_ci(a: &Self, b: &Self) -> bool {
653 if let (Some(ak), Some(bk)) = (a.ci_key(), b.ci_key()) {
654 return ak == bk;
655 }
656
657 a == b
658 }
659
660 fn normalize_list_ref(v: &Self) -> Vec<&Self> {
661 match v {
662 Self::List(vs) => vs.iter().collect(),
663 v => vec![v],
664 }
665 }
666
667 fn contains_by<F>(&self, needle: &Self, eq: F) -> Option<bool>
668 where
669 F: Fn(&Self, &Self) -> bool,
670 {
671 self.as_list()
672 .map(|items| items.iter().any(|v| eq(v, needle)))
673 }
674
675 #[allow(clippy::unnecessary_wraps)]
676 fn contains_any_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
677 where
678 F: Fn(&Self, &Self) -> bool,
679 {
680 let needles = Self::normalize_list_ref(needles);
681 match self {
682 Self::List(items) => Some(needles.iter().any(|n| items.iter().any(|v| eq(v, n)))),
683 scalar => Some(needles.iter().any(|n| eq(scalar, n))),
684 }
685 }
686
687 #[allow(clippy::unnecessary_wraps)]
688 fn contains_all_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
689 where
690 F: Fn(&Self, &Self) -> bool,
691 {
692 let needles = Self::normalize_list_ref(needles);
693 match self {
694 Self::List(items) => Some(needles.iter().all(|n| items.iter().any(|v| eq(v, n)))),
695 scalar => Some(needles.len() == 1 && eq(scalar, needles[0])),
696 }
697 }
698
699 fn in_list_by<F>(&self, haystack: &Self, eq: F) -> Option<bool>
700 where
701 F: Fn(&Self, &Self) -> bool,
702 {
703 if let Self::List(items) = haystack {
704 Some(items.iter().any(|h| eq(h, self)))
705 } else {
706 None
707 }
708 }
709
710 #[must_use]
711 pub fn text_eq(&self, other: &Self, mode: TextMode) -> Option<bool> {
713 self.text_op(other, mode, |a, b| a == b)
714 }
715
716 #[must_use]
717 pub fn text_contains(&self, needle: &Self, mode: TextMode) -> Option<bool> {
719 self.text_op(needle, mode, |a, b| a.contains(b))
720 }
721
722 #[must_use]
723 pub fn text_starts_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
725 self.text_op(needle, mode, |a, b| a.starts_with(b))
726 }
727
728 #[must_use]
729 pub fn text_ends_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
731 self.text_op(needle, mode, |a, b| a.ends_with(b))
732 }
733
734 #[must_use]
739 pub const fn is_empty(&self) -> Option<bool> {
740 match self {
741 Self::List(xs) => Some(xs.is_empty()),
742 Self::Map(entries) => Some(entries.is_empty()),
743 Self::Text(s) => Some(s.is_empty()),
744 Self::Blob(b) => Some(b.is_empty()),
745
746 Self::Null => Some(true),
748
749 _ => None,
750 }
751 }
752
753 #[must_use]
754 pub fn is_not_empty(&self) -> Option<bool> {
756 self.is_empty().map(|b| !b)
757 }
758
759 #[must_use]
764 pub fn contains(&self, needle: &Self) -> Option<bool> {
766 self.contains_by(needle, |a, b| a == b)
767 }
768
769 #[must_use]
770 pub fn contains_any(&self, needles: &Self) -> Option<bool> {
772 self.contains_any_by(needles, |a, b| a == b)
773 }
774
775 #[must_use]
776 pub fn contains_all(&self, needles: &Self) -> Option<bool> {
778 self.contains_all_by(needles, |a, b| a == b)
779 }
780
781 #[must_use]
782 pub fn in_list(&self, haystack: &Self) -> Option<bool> {
784 self.in_list_by(haystack, |a, b| a == b)
785 }
786
787 #[must_use]
788 pub fn contains_ci(&self, needle: &Self) -> Option<bool> {
790 match self {
791 Self::List(_) => self.contains_by(needle, Self::eq_ci),
792 _ => Some(Self::eq_ci(self, needle)),
793 }
794 }
795
796 #[must_use]
797 pub fn contains_any_ci(&self, needles: &Self) -> Option<bool> {
799 self.contains_any_by(needles, Self::eq_ci)
800 }
801
802 #[must_use]
803 pub fn contains_all_ci(&self, needles: &Self) -> Option<bool> {
805 self.contains_all_by(needles, Self::eq_ci)
806 }
807
808 #[must_use]
809 pub fn in_list_ci(&self, haystack: &Self) -> Option<bool> {
811 self.in_list_by(haystack, Self::eq_ci)
812 }
813}
814
815impl FieldValue for Value {
816 fn kind() -> crate::traits::FieldValueKind {
817 crate::traits::FieldValueKind::Atomic
818 }
819
820 fn to_value(&self) -> Value {
821 self.clone()
822 }
823
824 fn from_value(value: &Value) -> Option<Self> {
825 Some(value.clone())
826 }
827}
828
829#[macro_export]
830macro_rules! impl_from_for {
831 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
832 $(
833 impl From<$type> for Value {
834 fn from(v: $type) -> Self {
835 Self::$variant(v.into())
836 }
837 }
838 )*
839 };
840}
841
842impl_from_for! {
843 Account => Account,
844 Date => Date,
845 Decimal => Decimal,
846 Duration => Duration,
847 E8s => E8s,
848 E18s => E18s,
849 bool => Bool,
850 i8 => Int,
851 i16 => Int,
852 i32 => Int,
853 i64 => Int,
854 i128 => Int128,
855 Int => IntBig,
856 Principal => Principal,
857 Subaccount => Subaccount,
858 &str => Text,
859 String => Text,
860 Timestamp => Timestamp,
861 u8 => Uint,
862 u16 => Uint,
863 u32 => Uint,
864 u64 => Uint,
865 u128 => Uint128,
866 Nat => UintBig,
867 Ulid => Ulid,
868}
869
870impl CoercionFamilyExt for Value {
871 fn coercion_family(&self) -> CoercionFamily {
877 scalar_registry!(value_coercion_family_from_registry, self)
878 }
879}
880
881impl From<Vec<Self>> for Value {
882 fn from(vec: Vec<Self>) -> Self {
883 Self::List(vec)
884 }
885}
886
887impl TryFrom<Vec<(Self, Self)>> for Value {
888 type Error = SchemaInvariantError;
889
890 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
891 Self::from_map(entries).map_err(Self::Error::from)
892 }
893}
894
895impl From<()> for Value {
896 fn from((): ()) -> Self {
897 Self::Unit
898 }
899}
900
901impl PartialOrd for Value {
907 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
908 match (self, other) {
909 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
910 (Self::Date(a), Self::Date(b)) => a.partial_cmp(b),
911 (Self::Decimal(a), Self::Decimal(b)) => a.partial_cmp(b),
912 (Self::Duration(a), Self::Duration(b)) => a.partial_cmp(b),
913 (Self::E8s(a), Self::E8s(b)) => a.partial_cmp(b),
914 (Self::E18s(a), Self::E18s(b)) => a.partial_cmp(b),
915 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b),
916 (Self::Float32(a), Self::Float32(b)) => a.partial_cmp(b),
917 (Self::Float64(a), Self::Float64(b)) => a.partial_cmp(b),
918 (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
919 (Self::Int128(a), Self::Int128(b)) => a.partial_cmp(b),
920 (Self::IntBig(a), Self::IntBig(b)) => a.partial_cmp(b),
921 (Self::Principal(a), Self::Principal(b)) => a.partial_cmp(b),
922 (Self::Subaccount(a), Self::Subaccount(b)) => a.partial_cmp(b),
923 (Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
924 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
925 (Self::Uint(a), Self::Uint(b)) => a.partial_cmp(b),
926 (Self::Uint128(a), Self::Uint128(b)) => a.partial_cmp(b),
927 (Self::UintBig(a), Self::UintBig(b)) => a.partial_cmp(b),
928 (Self::Ulid(a), Self::Ulid(b)) => a.partial_cmp(b),
929 (Self::Map(a), Self::Map(b)) => {
930 for ((left_key, left_value), (right_key, right_value)) in a.iter().zip(b.iter()) {
931 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
932 if key_cmp != Ordering::Equal {
933 return Some(key_cmp);
934 }
935
936 match left_value.partial_cmp(right_value) {
937 Some(Ordering::Equal) => {}
938 non_eq => return non_eq,
939 }
940 }
941 a.len().partial_cmp(&b.len())
942 }
943
944 _ => None,
946 }
947 }
948}
949
950#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd, Serialize)]
956pub struct ValueEnum {
957 pub variant: String,
958 pub path: Option<String>,
959 pub payload: Option<Box<Value>>,
960}
961
962impl ValueEnum {
963 #[must_use]
964 pub fn new(variant: &str, path: Option<&str>) -> Self {
966 Self {
967 variant: variant.to_string(),
968 path: path.map(ToString::to_string),
969 payload: None,
970 }
971 }
972
973 #[must_use]
974 pub fn strict<E: Path>(variant: &str) -> Self {
976 Self::new(variant, Some(E::PATH))
977 }
978
979 #[must_use]
980 pub fn from_enum<E: EnumValue>(value: E) -> Self {
982 value.to_value_enum()
983 }
984
985 #[must_use]
986 pub fn loose(variant: &str) -> Self {
988 Self::new(variant, None)
989 }
990
991 #[must_use]
992 pub fn with_payload(mut self, payload: Value) -> Self {
994 self.payload = Some(Box::new(payload));
995 self
996 }
997}