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, Deserializer, 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(Clone, Debug, Eq, PartialEq)]
105pub enum SchemaInvariantError {
106 InvalidMapValue(MapValueError),
107}
108
109impl std::fmt::Display for SchemaInvariantError {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 match self {
112 Self::InvalidMapValue(err) => write!(f, "{err}"),
113 }
114 }
115}
116
117impl std::error::Error for SchemaInvariantError {}
118
119impl From<MapValueError> for SchemaInvariantError {
120 fn from(value: MapValueError) -> Self {
121 Self::InvalidMapValue(value)
122 }
123}
124
125#[derive(CandidType, Clone, Debug, Eq, PartialEq, Serialize)]
134pub enum Value {
135 Account(Account),
136 Blob(Vec<u8>),
137 Bool(bool),
138 Date(Date),
139 Decimal(Decimal),
140 Duration(Duration),
141 Enum(ValueEnum),
142 E8s(E8s),
143 E18s(E18s),
144 Float32(Float32),
145 Float64(Float64),
146 Int(i64),
147 Int128(Int128),
148 IntBig(Int),
149 List(Vec<Self>),
153 Map(Vec<(Self, Self)>),
160 Null,
161 Principal(Principal),
162 Subaccount(Subaccount),
163 Text(String),
164 Timestamp(Timestamp),
165 Uint(u64),
166 Uint128(Nat128),
167 UintBig(Nat),
168 Ulid(Ulid),
169 Unit,
170}
171
172#[derive(Deserialize)]
174enum ValueWire {
175 Account(Account),
176 Blob(Vec<u8>),
177 Bool(bool),
178 Date(Date),
179 Decimal(Decimal),
180 Duration(Duration),
181 Enum(ValueEnum),
182 E8s(E8s),
183 E18s(E18s),
184 Float32(Float32),
185 Float64(Float64),
186 Int(i64),
187 Int128(Int128),
188 IntBig(Int),
189 List(Vec<Self>),
190 Map(Vec<(Self, Self)>),
191 Null,
192 Principal(Principal),
193 Subaccount(Subaccount),
194 Text(String),
195 Timestamp(Timestamp),
196 Uint(u64),
197 Uint128(Nat128),
198 UintBig(Nat),
199 Ulid(Ulid),
200 Unit,
201}
202
203impl ValueWire {
204 fn into_value(self) -> Result<Value, MapValueError> {
205 match self {
206 Self::Account(v) => Ok(Value::Account(v)),
207 Self::Blob(v) => Ok(Value::Blob(v)),
208 Self::Bool(v) => Ok(Value::Bool(v)),
209 Self::Date(v) => Ok(Value::Date(v)),
210 Self::Decimal(v) => Ok(Value::Decimal(v)),
211 Self::Duration(v) => Ok(Value::Duration(v)),
212 Self::Enum(v) => Ok(Value::Enum(v)),
213 Self::E8s(v) => Ok(Value::E8s(v)),
214 Self::E18s(v) => Ok(Value::E18s(v)),
215 Self::Float32(v) => Ok(Value::Float32(v)),
216 Self::Float64(v) => Ok(Value::Float64(v)),
217 Self::Int(v) => Ok(Value::Int(v)),
218 Self::Int128(v) => Ok(Value::Int128(v)),
219 Self::IntBig(v) => Ok(Value::IntBig(v)),
220 Self::List(items) => {
221 let items = items
222 .into_iter()
223 .map(Self::into_value)
224 .collect::<Result<Vec<_>, _>>()?;
225 Ok(Value::List(items))
226 }
227 Self::Map(entries) => {
228 let entries = entries
229 .into_iter()
230 .map(|(key, value)| Ok((key.into_value()?, value.into_value()?)))
231 .collect::<Result<Vec<_>, MapValueError>>()?;
232 Value::from_map(entries)
233 }
234 Self::Null => Ok(Value::Null),
235 Self::Principal(v) => Ok(Value::Principal(v)),
236 Self::Subaccount(v) => Ok(Value::Subaccount(v)),
237 Self::Text(v) => Ok(Value::Text(v)),
238 Self::Timestamp(v) => Ok(Value::Timestamp(v)),
239 Self::Uint(v) => Ok(Value::Uint(v)),
240 Self::Uint128(v) => Ok(Value::Uint128(v)),
241 Self::UintBig(v) => Ok(Value::UintBig(v)),
242 Self::Ulid(v) => Ok(Value::Ulid(v)),
243 Self::Unit => Ok(Value::Unit),
244 }
245 }
246}
247
248impl<'de> Deserialize<'de> for Value {
249 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
250 where
251 D: Deserializer<'de>,
252 {
253 let wire = ValueWire::deserialize(deserializer)?;
254 wire.into_value().map_err(serde::de::Error::custom)
255 }
256}
257
258macro_rules! value_is_numeric_from_registry {
260 ( @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) ),* $(,)? ) => {
261 match $value {
262 $( $value_pat => $is_numeric, )*
263 _ => false,
264 }
265 };
266}
267
268macro_rules! value_supports_numeric_coercion_from_registry {
269 ( @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) ),* $(,)? ) => {
270 match $value {
271 $( $value_pat => $supports_numeric_coercion, )*
272 _ => false,
273 }
274 };
275}
276
277macro_rules! value_storage_key_case {
278 ( $value:expr, Unit, true ) => {
279 if let Value::Unit = $value {
280 Some(StorageKey::Unit)
281 } else {
282 None
283 }
284 };
285 ( $value:expr, $scalar:ident, true ) => {
286 if let Value::$scalar(v) = $value {
287 Some(StorageKey::$scalar(*v))
288 } else {
289 None
290 }
291 };
292 ( $value:expr, $scalar:ident, false ) => {
293 None
294 };
295}
296
297macro_rules! value_storage_key_from_registry {
298 ( @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) ),* $(,)? ) => {
299 {
300 let mut key = None;
301 $(
302 match key {
303 Some(_) => {}
304 None => {
305 key = value_storage_key_case!($value, $scalar, $is_keyable);
306 }
307 }
308 )*
309 key
310 }
311 };
312}
313
314macro_rules! value_coercion_family_from_registry {
315 ( @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) ),* $(,)? ) => {
316 match $value {
317 $( $value_pat => $coercion_family, )*
318 Value::List(_) => CoercionFamily::Collection,
319 Value::Map(_) => CoercionFamily::Collection,
320 Value::Null => CoercionFamily::Null,
321 }
322 };
323}
324
325impl Value {
326 pub fn from_slice<T>(items: &[T]) -> Self
335 where
336 T: Into<Self> + Clone,
337 {
338 Self::List(items.iter().cloned().map(Into::into).collect())
339 }
340
341 pub fn from_list<T>(items: Vec<T>) -> Self
345 where
346 T: Into<Self>,
347 {
348 Self::List(items.into_iter().map(Into::into).collect())
349 }
350
351 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
359 let normalized = Self::normalize_map_entries(entries)?;
360 Ok(Self::Map(normalized))
361 }
362
363 pub fn validate_map_entries(entries: &[(Self, Self)]) -> Result<(), MapValueError> {
365 for (index, (key, value)) in entries.iter().enumerate() {
366 if matches!(key, Self::Null) {
367 return Err(MapValueError::EmptyKey { index });
368 }
369 if !key.is_scalar() {
370 return Err(MapValueError::NonScalarKey {
371 index,
372 key: key.clone(),
373 });
374 }
375
376 if !value.is_scalar() {
377 return Err(MapValueError::NonScalarValue {
378 index,
379 value: value.clone(),
380 });
381 }
382 }
383
384 Ok(())
385 }
386
387 pub fn normalize_map_entries(
389 mut entries: Vec<(Self, Self)>,
390 ) -> Result<Vec<(Self, Self)>, MapValueError> {
391 Self::validate_map_entries(&entries)?;
392 entries
393 .sort_by(|(left_key, _), (right_key, _)| Self::canonical_cmp_key(left_key, right_key));
394
395 for i in 1..entries.len() {
396 let (left_key, _) = &entries[i - 1];
397 let (right_key, _) = &entries[i];
398 if Self::canonical_cmp_key(left_key, right_key) == Ordering::Equal {
399 return Err(MapValueError::DuplicateKey {
400 left_index: i - 1,
401 right_index: i,
402 });
403 }
404 }
405
406 Ok(entries)
407 }
408
409 pub fn from_enum<E: EnumValue>(value: E) -> Self {
411 Self::Enum(value.to_value_enum())
412 }
413
414 #[must_use]
416 pub fn enum_strict<E: Path>(variant: &str) -> Self {
417 Self::Enum(ValueEnum::strict::<E>(variant))
418 }
419
420 #[must_use]
427 pub const fn is_numeric(&self) -> bool {
428 scalar_registry!(value_is_numeric_from_registry, self)
429 }
430
431 #[must_use]
433 pub const fn supports_numeric_coercion(&self) -> bool {
434 scalar_registry!(value_supports_numeric_coercion_from_registry, self)
435 }
436
437 #[must_use]
439 pub const fn is_text(&self) -> bool {
440 matches!(self, Self::Text(_))
441 }
442
443 #[must_use]
445 pub const fn is_unit(&self) -> bool {
446 matches!(self, Self::Unit)
447 }
448
449 #[must_use]
450 pub const fn is_scalar(&self) -> bool {
451 match self {
452 Self::List(_) | Self::Map(_) | Self::Unit => false,
454 _ => true,
455 }
456 }
457
458 #[must_use]
460 pub fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
461 let rank = Self::canonical_key_rank(left).cmp(&Self::canonical_key_rank(right));
462 if rank != Ordering::Equal {
463 return rank;
464 }
465
466 #[allow(clippy::match_same_arms)]
467 match (left, right) {
468 (Self::Account(a), Self::Account(b)) => a.cmp(b),
469 (Self::Blob(a), Self::Blob(b)) => a.cmp(b),
470 (Self::Bool(a), Self::Bool(b)) => a.cmp(b),
471 (Self::Date(a), Self::Date(b)) => a.cmp(b),
472 (Self::Decimal(a), Self::Decimal(b)) => a.cmp(b),
473 (Self::Duration(a), Self::Duration(b)) => a.cmp(b),
474 (Self::Enum(a), Self::Enum(b)) => Self::canonical_cmp_value_enum_key(a, b),
475 (Self::E8s(a), Self::E8s(b)) => a.cmp(b),
476 (Self::E18s(a), Self::E18s(b)) => a.cmp(b),
477 (Self::Float32(a), Self::Float32(b)) => a.cmp(b),
478 (Self::Float64(a), Self::Float64(b)) => a.cmp(b),
479 (Self::Int(a), Self::Int(b)) => a.cmp(b),
480 (Self::Int128(a), Self::Int128(b)) => a.cmp(b),
481 (Self::IntBig(a), Self::IntBig(b)) => a.cmp(b),
482 (Self::List(a), Self::List(b)) => Self::canonical_cmp_value_list_key(a, b),
483 (Self::Map(a), Self::Map(b)) => Self::canonical_cmp_value_map_key(a, b),
484 (Self::Principal(a), Self::Principal(b)) => a.cmp(b),
485 (Self::Subaccount(a), Self::Subaccount(b)) => a.cmp(b),
486 (Self::Text(a), Self::Text(b)) => a.cmp(b),
487 (Self::Timestamp(a), Self::Timestamp(b)) => a.cmp(b),
488 (Self::Uint(a), Self::Uint(b)) => a.cmp(b),
489 (Self::Uint128(a), Self::Uint128(b)) => a.cmp(b),
490 (Self::UintBig(a), Self::UintBig(b)) => a.cmp(b),
491 (Self::Ulid(a), Self::Ulid(b)) => a.cmp(b),
492 (Self::Null, Self::Null) | (Self::Unit, Self::Unit) => Ordering::Equal,
493 _ => Ordering::Equal,
494 }
495 }
496
497 const fn canonical_key_rank(value: &Self) -> u8 {
498 match value {
499 Self::Account(_) => 0,
500 Self::Blob(_) => 1,
501 Self::Bool(_) => 2,
502 Self::Date(_) => 3,
503 Self::Decimal(_) => 4,
504 Self::Duration(_) => 5,
505 Self::Enum(_) => 6,
506 Self::E8s(_) => 7,
507 Self::E18s(_) => 8,
508 Self::Float32(_) => 9,
509 Self::Float64(_) => 10,
510 Self::Int(_) => 11,
511 Self::Int128(_) => 12,
512 Self::IntBig(_) => 13,
513 Self::List(_) => 14,
514 Self::Map(_) => 15,
515 Self::Null => 16,
516 Self::Principal(_) => 17,
517 Self::Subaccount(_) => 18,
518 Self::Text(_) => 19,
519 Self::Timestamp(_) => 20,
520 Self::Uint(_) => 21,
521 Self::Uint128(_) => 22,
522 Self::UintBig(_) => 23,
523 Self::Ulid(_) => 24,
524 Self::Unit => 25,
525 }
526 }
527
528 fn canonical_cmp_value_list_key(left: &[Self], right: &[Self]) -> Ordering {
529 for (left, right) in left.iter().zip(right.iter()) {
530 let cmp = Self::canonical_cmp_key(left, right);
531 if cmp != Ordering::Equal {
532 return cmp;
533 }
534 }
535
536 left.len().cmp(&right.len())
537 }
538
539 fn canonical_cmp_value_map_key(left: &[(Self, Self)], right: &[(Self, Self)]) -> Ordering {
540 for ((left_key, left_value), (right_key, right_value)) in left.iter().zip(right.iter()) {
541 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
542 if key_cmp != Ordering::Equal {
543 return key_cmp;
544 }
545
546 let value_cmp = Self::canonical_cmp_key(left_value, right_value);
547 if value_cmp != Ordering::Equal {
548 return value_cmp;
549 }
550 }
551
552 left.len().cmp(&right.len())
553 }
554
555 fn canonical_cmp_value_enum_key(left: &ValueEnum, right: &ValueEnum) -> Ordering {
556 let cmp = left.variant.cmp(&right.variant);
557 if cmp != Ordering::Equal {
558 return cmp;
559 }
560
561 let cmp = left.path.cmp(&right.path);
562 if cmp != Ordering::Equal {
563 return cmp;
564 }
565
566 match (&left.payload, &right.payload) {
567 (None, None) => Ordering::Equal,
568 (None, Some(_)) => Ordering::Less,
569 (Some(_), None) => Ordering::Greater,
570 (Some(left), Some(right)) => Self::canonical_cmp_key(left, right),
571 }
572 }
573
574 fn numeric_repr(&self) -> NumericRepr {
575 if !self.supports_numeric_coercion() {
577 return NumericRepr::None;
578 }
579
580 if let Some(d) = self.to_decimal() {
581 return NumericRepr::Decimal(d);
582 }
583 if let Some(f) = self.to_f64_lossless() {
584 return NumericRepr::F64(f);
585 }
586 NumericRepr::None
587 }
588
589 #[must_use]
598 pub const fn as_storage_key(&self) -> Option<StorageKey> {
599 scalar_registry!(value_storage_key_from_registry, self)
600 }
601
602 #[must_use]
603 pub const fn as_text(&self) -> Option<&str> {
604 if let Self::Text(s) = self {
605 Some(s.as_str())
606 } else {
607 None
608 }
609 }
610
611 #[must_use]
612 pub const fn as_list(&self) -> Option<&[Self]> {
613 if let Self::List(xs) = self {
614 Some(xs.as_slice())
615 } else {
616 None
617 }
618 }
619
620 #[must_use]
621 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
622 if let Self::Map(entries) = self {
623 Some(entries.as_slice())
624 } else {
625 None
626 }
627 }
628
629 fn to_decimal(&self) -> Option<Decimal> {
630 match self {
631 Self::Decimal(d) => Some(*d),
632 Self::Duration(d) => Decimal::from_u64(d.get()),
633 Self::E8s(v) => Some(v.to_decimal()),
634 Self::E18s(v) => v.to_decimal(),
635 Self::Float64(f) => Decimal::from_f64(f.get()),
636 Self::Float32(f) => Decimal::from_f32(f.get()),
637 Self::Int(i) => Decimal::from_i64(*i),
638 Self::Int128(i) => Decimal::from_i128(i.get()),
639 Self::IntBig(i) => i.to_i128().and_then(Decimal::from_i128),
640 Self::Timestamp(t) => Decimal::from_u64(t.get()),
641 Self::Uint(u) => Decimal::from_u64(*u),
642 Self::Uint128(u) => Decimal::from_u128(u.get()),
643 Self::UintBig(u) => u.to_u128().and_then(Decimal::from_u128),
644
645 _ => None,
646 }
647 }
648
649 #[allow(clippy::cast_precision_loss)]
651 fn to_f64_lossless(&self) -> Option<f64> {
652 match self {
653 Self::Duration(d) if d.get() <= F64_SAFE_U64 => Some(d.get() as f64),
654 Self::Float64(f) => Some(f.get()),
655 Self::Float32(f) => Some(f64::from(f.get())),
656 Self::Int(i) if (-F64_SAFE_I64..=F64_SAFE_I64).contains(i) => Some(*i as f64),
657 Self::Int128(i) if (-F64_SAFE_I128..=F64_SAFE_I128).contains(&i.get()) => {
658 Some(i.get() as f64)
659 }
660 Self::IntBig(i) => i.to_i128().and_then(|v| {
661 (-F64_SAFE_I128..=F64_SAFE_I128)
662 .contains(&v)
663 .then_some(v as f64)
664 }),
665 Self::Timestamp(t) if t.get() <= F64_SAFE_U64 => Some(t.get() as f64),
666 Self::Uint(u) if *u <= F64_SAFE_U64 => Some(*u as f64),
667 Self::Uint128(u) if u.get() <= F64_SAFE_U128 => Some(u.get() as f64),
668 Self::UintBig(u) => u
669 .to_u128()
670 .and_then(|v| (v <= F64_SAFE_U128).then_some(v as f64)),
671
672 _ => None,
673 }
674 }
675
676 #[must_use]
678 pub fn cmp_numeric(&self, other: &Self) -> Option<Ordering> {
679 if !self.supports_numeric_coercion() || !other.supports_numeric_coercion() {
680 return None;
681 }
682
683 match (self.numeric_repr(), other.numeric_repr()) {
684 (NumericRepr::Decimal(a), NumericRepr::Decimal(b)) => a.partial_cmp(&b),
685 (NumericRepr::F64(a), NumericRepr::F64(b)) => a.partial_cmp(&b),
686 _ => None,
687 }
688 }
689
690 fn fold_ci(s: &str) -> std::borrow::Cow<'_, str> {
695 if s.is_ascii() {
696 return std::borrow::Cow::Owned(s.to_ascii_lowercase());
697 }
698 std::borrow::Cow::Owned(s.to_lowercase())
701 }
702
703 fn text_with_mode(s: &'_ str, mode: TextMode) -> std::borrow::Cow<'_, str> {
704 match mode {
705 TextMode::Cs => std::borrow::Cow::Borrowed(s),
706 TextMode::Ci => Self::fold_ci(s),
707 }
708 }
709
710 fn text_op(
711 &self,
712 other: &Self,
713 mode: TextMode,
714 f: impl Fn(&str, &str) -> bool,
715 ) -> Option<bool> {
716 let (a, b) = (self.as_text()?, other.as_text()?);
717 let a = Self::text_with_mode(a, mode);
718 let b = Self::text_with_mode(b, mode);
719 Some(f(&a, &b))
720 }
721
722 fn ci_key(&self) -> Option<String> {
723 match self {
724 Self::Text(s) => Some(Self::fold_ci(s).into_owned()),
725 Self::Ulid(u) => Some(u.to_string().to_ascii_lowercase()),
726 Self::Principal(p) => Some(p.to_string().to_ascii_lowercase()),
727 Self::Account(a) => Some(a.to_string().to_ascii_lowercase()),
728 _ => None,
729 }
730 }
731
732 fn eq_ci(a: &Self, b: &Self) -> bool {
733 if let (Some(ak), Some(bk)) = (a.ci_key(), b.ci_key()) {
734 return ak == bk;
735 }
736
737 a == b
738 }
739
740 fn normalize_list_ref(v: &Self) -> Vec<&Self> {
741 match v {
742 Self::List(vs) => vs.iter().collect(),
743 v => vec![v],
744 }
745 }
746
747 fn contains_by<F>(&self, needle: &Self, eq: F) -> Option<bool>
748 where
749 F: Fn(&Self, &Self) -> bool,
750 {
751 self.as_list()
752 .map(|items| items.iter().any(|v| eq(v, needle)))
753 }
754
755 #[allow(clippy::unnecessary_wraps)]
756 fn contains_any_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
757 where
758 F: Fn(&Self, &Self) -> bool,
759 {
760 let needles = Self::normalize_list_ref(needles);
761 match self {
762 Self::List(items) => Some(needles.iter().any(|n| items.iter().any(|v| eq(v, n)))),
763 scalar => Some(needles.iter().any(|n| eq(scalar, n))),
764 }
765 }
766
767 #[allow(clippy::unnecessary_wraps)]
768 fn contains_all_by<F>(&self, needles: &Self, eq: F) -> Option<bool>
769 where
770 F: Fn(&Self, &Self) -> bool,
771 {
772 let needles = Self::normalize_list_ref(needles);
773 match self {
774 Self::List(items) => Some(needles.iter().all(|n| items.iter().any(|v| eq(v, n)))),
775 scalar => Some(needles.len() == 1 && eq(scalar, needles[0])),
776 }
777 }
778
779 fn in_list_by<F>(&self, haystack: &Self, eq: F) -> Option<bool>
780 where
781 F: Fn(&Self, &Self) -> bool,
782 {
783 if let Self::List(items) = haystack {
784 Some(items.iter().any(|h| eq(h, self)))
785 } else {
786 None
787 }
788 }
789
790 #[must_use]
791 pub fn text_eq(&self, other: &Self, mode: TextMode) -> Option<bool> {
793 self.text_op(other, mode, |a, b| a == b)
794 }
795
796 #[must_use]
797 pub fn text_contains(&self, needle: &Self, mode: TextMode) -> Option<bool> {
799 self.text_op(needle, mode, |a, b| a.contains(b))
800 }
801
802 #[must_use]
803 pub fn text_starts_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
805 self.text_op(needle, mode, |a, b| a.starts_with(b))
806 }
807
808 #[must_use]
809 pub fn text_ends_with(&self, needle: &Self, mode: TextMode) -> Option<bool> {
811 self.text_op(needle, mode, |a, b| a.ends_with(b))
812 }
813
814 #[must_use]
819 pub const fn is_empty(&self) -> Option<bool> {
820 match self {
821 Self::List(xs) => Some(xs.is_empty()),
822 Self::Map(entries) => Some(entries.is_empty()),
823 Self::Text(s) => Some(s.is_empty()),
824 Self::Blob(b) => Some(b.is_empty()),
825
826 Self::Null => Some(true),
828
829 _ => None,
830 }
831 }
832
833 #[must_use]
834 pub fn is_not_empty(&self) -> Option<bool> {
836 self.is_empty().map(|b| !b)
837 }
838
839 #[must_use]
844 pub fn contains(&self, needle: &Self) -> Option<bool> {
846 self.contains_by(needle, |a, b| a == b)
847 }
848
849 #[must_use]
850 pub fn contains_any(&self, needles: &Self) -> Option<bool> {
852 self.contains_any_by(needles, |a, b| a == b)
853 }
854
855 #[must_use]
856 pub fn contains_all(&self, needles: &Self) -> Option<bool> {
858 self.contains_all_by(needles, |a, b| a == b)
859 }
860
861 #[must_use]
862 pub fn in_list(&self, haystack: &Self) -> Option<bool> {
864 self.in_list_by(haystack, |a, b| a == b)
865 }
866
867 #[must_use]
868 pub fn contains_ci(&self, needle: &Self) -> Option<bool> {
870 match self {
871 Self::List(_) => self.contains_by(needle, Self::eq_ci),
872 _ => Some(Self::eq_ci(self, needle)),
873 }
874 }
875
876 #[must_use]
877 pub fn contains_any_ci(&self, needles: &Self) -> Option<bool> {
879 self.contains_any_by(needles, Self::eq_ci)
880 }
881
882 #[must_use]
883 pub fn contains_all_ci(&self, needles: &Self) -> Option<bool> {
885 self.contains_all_by(needles, Self::eq_ci)
886 }
887
888 #[must_use]
889 pub fn in_list_ci(&self, haystack: &Self) -> Option<bool> {
891 self.in_list_by(haystack, Self::eq_ci)
892 }
893}
894
895impl FieldValue for Value {
896 fn kind() -> crate::traits::FieldValueKind {
897 crate::traits::FieldValueKind::Atomic
898 }
899
900 fn to_value(&self) -> Value {
901 self.clone()
902 }
903
904 fn from_value(value: &Value) -> Option<Self> {
905 Some(value.clone())
906 }
907}
908
909#[macro_export]
910macro_rules! impl_from_for {
911 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
912 $(
913 impl From<$type> for Value {
914 fn from(v: $type) -> Self {
915 Self::$variant(v.into())
916 }
917 }
918 )*
919 };
920}
921
922impl_from_for! {
923 Account => Account,
924 Date => Date,
925 Decimal => Decimal,
926 Duration => Duration,
927 E8s => E8s,
928 E18s => E18s,
929 bool => Bool,
930 i8 => Int,
931 i16 => Int,
932 i32 => Int,
933 i64 => Int,
934 i128 => Int128,
935 Int => IntBig,
936 Principal => Principal,
937 Subaccount => Subaccount,
938 &str => Text,
939 String => Text,
940 Timestamp => Timestamp,
941 u8 => Uint,
942 u16 => Uint,
943 u32 => Uint,
944 u64 => Uint,
945 u128 => Uint128,
946 Nat => UintBig,
947 Ulid => Ulid,
948}
949
950impl CoercionFamilyExt for Value {
951 fn coercion_family(&self) -> CoercionFamily {
957 scalar_registry!(value_coercion_family_from_registry, self)
958 }
959}
960
961impl From<Vec<Self>> for Value {
962 fn from(vec: Vec<Self>) -> Self {
963 Self::List(vec)
964 }
965}
966
967impl TryFrom<Vec<(Self, Self)>> for Value {
968 type Error = SchemaInvariantError;
969
970 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
971 Self::from_map(entries).map_err(Self::Error::from)
972 }
973}
974
975impl From<()> for Value {
976 fn from((): ()) -> Self {
977 Self::Unit
978 }
979}
980
981impl PartialOrd for Value {
987 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
988 match (self, other) {
989 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
990 (Self::Date(a), Self::Date(b)) => a.partial_cmp(b),
991 (Self::Decimal(a), Self::Decimal(b)) => a.partial_cmp(b),
992 (Self::Duration(a), Self::Duration(b)) => a.partial_cmp(b),
993 (Self::E8s(a), Self::E8s(b)) => a.partial_cmp(b),
994 (Self::E18s(a), Self::E18s(b)) => a.partial_cmp(b),
995 (Self::Enum(a), Self::Enum(b)) => a.partial_cmp(b),
996 (Self::Float32(a), Self::Float32(b)) => a.partial_cmp(b),
997 (Self::Float64(a), Self::Float64(b)) => a.partial_cmp(b),
998 (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
999 (Self::Int128(a), Self::Int128(b)) => a.partial_cmp(b),
1000 (Self::IntBig(a), Self::IntBig(b)) => a.partial_cmp(b),
1001 (Self::Principal(a), Self::Principal(b)) => a.partial_cmp(b),
1002 (Self::Subaccount(a), Self::Subaccount(b)) => a.partial_cmp(b),
1003 (Self::Text(a), Self::Text(b)) => a.partial_cmp(b),
1004 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
1005 (Self::Uint(a), Self::Uint(b)) => a.partial_cmp(b),
1006 (Self::Uint128(a), Self::Uint128(b)) => a.partial_cmp(b),
1007 (Self::UintBig(a), Self::UintBig(b)) => a.partial_cmp(b),
1008 (Self::Ulid(a), Self::Ulid(b)) => a.partial_cmp(b),
1009 (Self::Map(a), Self::Map(b)) => {
1010 for ((left_key, left_value), (right_key, right_value)) in a.iter().zip(b.iter()) {
1011 let key_cmp = Self::canonical_cmp_key(left_key, right_key);
1012 if key_cmp != Ordering::Equal {
1013 return Some(key_cmp);
1014 }
1015
1016 match left_value.partial_cmp(right_value) {
1017 Some(Ordering::Equal) => {}
1018 non_eq => return non_eq,
1019 }
1020 }
1021 a.len().partial_cmp(&b.len())
1022 }
1023
1024 _ => None,
1026 }
1027 }
1028}
1029
1030#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, PartialOrd, Serialize)]
1036pub struct ValueEnum {
1037 pub variant: String,
1038 pub path: Option<String>,
1039 pub payload: Option<Box<Value>>,
1040}
1041
1042impl ValueEnum {
1043 #[must_use]
1044 pub fn new(variant: &str, path: Option<&str>) -> Self {
1046 Self {
1047 variant: variant.to_string(),
1048 path: path.map(ToString::to_string),
1049 payload: None,
1050 }
1051 }
1052
1053 #[must_use]
1054 pub fn strict<E: Path>(variant: &str) -> Self {
1056 Self::new(variant, Some(E::PATH))
1057 }
1058
1059 #[must_use]
1060 pub fn from_enum<E: EnumValue>(value: E) -> Self {
1062 value.to_value_enum()
1063 }
1064
1065 #[must_use]
1066 pub fn loose(variant: &str) -> Self {
1068 Self::new(variant, None)
1069 }
1070
1071 #[must_use]
1072 pub fn with_payload(mut self, payload: Value) -> Self {
1074 self.payload = Some(Box::new(payload));
1075 self
1076 }
1077}