1mod canonical;
12mod canonical_enum;
13mod cleanup;
14mod coercion;
15mod compare;
16pub(crate) mod decimal;
17pub(crate) mod format;
18mod hash;
19mod input;
20mod map;
21pub(crate) mod ops;
22mod output;
23mod public;
24mod rank;
25mod semantics;
26mod tag;
27mod wire;
28
29#[cfg(test)]
30mod tests;
31
32use crate::types::*;
33use serde::{Deserialize, Deserializer, de};
34use std::{cmp::Ordering, fmt};
35
36pub(crate) use canonical::{canonicalize_value_set, value_set_is_strictly_canonical};
38pub(crate) use canonical_enum::{CanonicalEnumBody, CanonicalEnumValue, EnumTypeId, EnumVariantId};
39pub(crate) use cleanup::clear_value;
40pub use coercion::CoercionFamily;
41pub(crate) use hash::{ValueHashWriter, hash_single_list_identity_canonical_value, hash_value};
42#[cfg(test)]
43pub(crate) use hash::{test_hash_budget_error, with_test_hash_override};
44pub use input::InputValue;
45pub use map::{MapValueError, SchemaInvariantError};
46pub(crate) use ops::{casefold_text, lower_text, lower_text_construction_allowance, upper_text};
47pub use output::{OutputValue, render_output_value_text};
48pub use public::{PublicEnumValue, PublicValue};
49pub use tag::ValueTag;
50
51const VALUE_WIRE_TYPE_NAME: &str = "Value";
56const VALUE_WIRE_VARIANT_LABELS: &[&str] = &[
57 "Account",
58 "Blob",
59 "Bool",
60 "Date",
61 "Decimal",
62 "Duration",
63 "Enum",
64 "Float32",
65 "Float64",
66 "Int",
67 "Int128",
68 "IntBig",
69 "List",
70 "Map",
71 "Null",
72 "Principal",
73 "Subaccount",
74 "Text",
75 "Timestamp",
76 "Nat",
77 "Nat128",
78 "NatBig",
79 "Ulid",
80 "Unit",
81 "U256",
82];
83
84#[derive(Clone, Copy)]
86enum ValueWireVariant {
87 Account,
88 Blob,
89 Bool,
90 Date,
91 Decimal,
92 Duration,
93 Enum,
94 Float32,
95 Float64,
96 Int64,
97 Int128,
98 IntBig,
99 List,
100 Map,
101 Null,
102 Principal,
103 Subaccount,
104 Text,
105 Timestamp,
106 Nat64,
107 Nat128,
108 NatBig,
109 Ulid,
110 Unit,
111 U256,
112}
113
114impl ValueWireVariant {
115 fn from_label(label: &str) -> Option<Self> {
117 match label {
118 "Account" => Some(Self::Account),
119 "Blob" => Some(Self::Blob),
120 "Bool" => Some(Self::Bool),
121 "Date" => Some(Self::Date),
122 "Decimal" => Some(Self::Decimal),
123 "Duration" => Some(Self::Duration),
124 "Enum" => Some(Self::Enum),
125 "Float32" => Some(Self::Float32),
126 "Float64" => Some(Self::Float64),
127 "Int" => Some(Self::Int64),
128 "Int128" => Some(Self::Int128),
129 "IntBig" => Some(Self::IntBig),
130 "List" => Some(Self::List),
131 "Map" => Some(Self::Map),
132 "Null" => Some(Self::Null),
133 "Principal" => Some(Self::Principal),
134 "Subaccount" => Some(Self::Subaccount),
135 "Text" => Some(Self::Text),
136 "Timestamp" => Some(Self::Timestamp),
137 "Nat" => Some(Self::Nat64),
138 "Nat128" => Some(Self::Nat128),
139 "NatBig" => Some(Self::NatBig),
140 "Ulid" => Some(Self::Ulid),
141 "Unit" => Some(Self::Unit),
142 "U256" => Some(Self::U256),
143 _ => None,
144 }
145 }
146}
147
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
153pub enum TextMode {
154 Cs, Ci, }
157
158#[derive(Clone, Eq, PartialEq)]
172pub enum Value {
173 Account(Account),
174 Blob(Vec<u8>),
175 Bool(bool),
176 Date(Date),
177 Decimal(Decimal),
178 Duration(Duration),
179 Enum(ValueEnum),
180 Float32(Float32),
181 Float64(Float64),
182 Int64(i64),
183 Int128(i128),
184 IntBig(IntBig),
185 List(Vec<Self>),
189 Map(Vec<(Self, Self)>),
196 Null,
197 Principal(Principal),
198 Subaccount(Subaccount),
199 Text(String),
200 Timestamp(Timestamp),
201 Nat64(u64),
202 Nat128(u128),
203 NatBig(NatBig),
204 Ulid(Ulid),
205 Unit,
206 U256(U256),
207}
208
209impl fmt::Debug for Value {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 Self::Account(value) => f.debug_tuple("Account").field(value).finish(),
213 Self::Blob(value) => write!(f, "Blob({} bytes)", value.len()),
214 Self::Bool(value) => f.debug_tuple("Bool").field(value).finish(),
215 Self::Date(value) => f.debug_tuple("Date").field(value).finish(),
216 Self::Decimal(value) => f.debug_tuple("Decimal").field(value).finish(),
217 Self::Duration(value) => f.debug_tuple("Duration").field(value).finish(),
218 Self::Enum(value) => f.debug_tuple("Enum").field(value).finish(),
219 Self::Float32(value) => f.debug_tuple("Float32").field(value).finish(),
220 Self::Float64(value) => f.debug_tuple("Float64").field(value).finish(),
221 Self::Int64(value) => f.debug_tuple("Int64").field(value).finish(),
222 Self::Int128(value) => f.debug_tuple("Int128").field(value).finish(),
223 Self::IntBig(value) => f.debug_tuple("IntBig").field(value).finish(),
224 Self::List(value) => f.debug_tuple("List").field(value).finish(),
225 Self::Map(value) => f.debug_tuple("Map").field(value).finish(),
226 Self::Null => f.write_str("Null"),
227 Self::Principal(value) => f.debug_tuple("Principal").field(value).finish(),
228 Self::Subaccount(value) => f.debug_tuple("Subaccount").field(value).finish(),
229 Self::Text(value) => f.debug_tuple("Text").field(value).finish(),
230 Self::Timestamp(value) => f.debug_tuple("Timestamp").field(value).finish(),
231 Self::Nat64(value) => f.debug_tuple("Nat64").field(value).finish(),
232 Self::Nat128(value) => f.debug_tuple("Nat128").field(value).finish(),
233 Self::NatBig(value) => f.debug_tuple("NatBig").field(value).finish(),
234 Self::Ulid(value) => f.debug_tuple("Ulid").field(value).finish(),
235 Self::Unit => f.write_str("Unit"),
236 Self::U256(value) => f.debug_tuple("U256").field(value).finish(),
237 }
238 }
239}
240
241impl Value {
242 pub fn from_slice<T>(items: &[T]) -> Self
251 where
252 T: Into<Self> + Clone,
253 {
254 Self::List(items.iter().cloned().map(Into::into).collect())
255 }
256
257 pub fn from_list<T>(items: Vec<T>) -> Self
261 where
262 T: Into<Self>,
263 {
264 Self::List(items.into_iter().map(Into::into).collect())
265 }
266
267 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
275 let normalized = map::normalize_map_entries(entries)?;
276 Ok(Self::Map(normalized))
277 }
278
279 #[must_use]
285 pub const fn is_text(&self) -> bool {
286 matches!(self, Self::Text(_))
287 }
288
289 #[must_use]
290 pub const fn is_scalar(&self) -> bool {
291 match self {
292 Self::List(_) | Self::Map(_) | Self::Unit => false,
294 _ => true,
295 }
296 }
297
298 #[must_use]
300 pub(crate) const fn canonical_tag(&self) -> ValueTag {
301 tag::canonical_tag(self)
302 }
303
304 #[must_use]
306 pub(crate) const fn canonical_rank(&self) -> u8 {
307 rank::canonical_rank(self)
308 }
309
310 #[must_use]
312 pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
313 compare::canonical_cmp(left, right)
314 }
315
316 #[must_use]
318 pub(crate) fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
319 compare::canonical_cmp_key(left, right)
320 }
321
322 #[must_use]
327 pub const fn as_text(&self) -> Option<&str> {
328 if let Self::Text(s) = self {
329 Some(s.as_str())
330 } else {
331 None
332 }
333 }
334
335 #[must_use]
336 pub const fn as_list(&self) -> Option<&[Self]> {
337 if let Self::List(xs) = self {
338 Some(xs.as_slice())
339 } else {
340 None
341 }
342 }
343
344 #[must_use]
345 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
346 if let Self::Map(entries) = self {
347 Some(entries.as_slice())
348 } else {
349 None
350 }
351 }
352}
353
354macro_rules! impl_from_for {
355 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
356 $(
357 impl From<$type> for Value {
358 fn from(v: $type) -> Self {
359 Self::$variant(v.into())
360 }
361 }
362 )*
363 };
364}
365
366impl_from_for! {
367 Account => Account,
368 Date => Date,
369 Decimal => Decimal,
370 Duration => Duration,
371 bool => Bool,
372 i8 => Int64,
373 i16 => Int64,
374 i32 => Int64,
375 i64 => Int64,
376 i128 => Int128,
377 IntBig => IntBig,
378 Principal => Principal,
379 Subaccount => Subaccount,
380 &str => Text,
381 String => Text,
382 Timestamp => Timestamp,
383 u8 => Nat64,
384 u16 => Nat64,
385 u32 => Nat64,
386 u64 => Nat64,
387 u128 => Nat128,
388 NatBig => NatBig,
389 Ulid => Ulid,
390 U256 => U256,
391}
392
393impl From<Vec<Self>> for Value {
394 fn from(vec: Vec<Self>) -> Self {
395 Self::List(vec)
396 }
397}
398
399impl TryFrom<Vec<(Self, Self)>> for Value {
400 type Error = SchemaInvariantError;
401
402 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
403 Self::from_map(entries).map_err(Self::Error::from)
404 }
405}
406
407impl From<()> for Value {
408 fn from((): ()) -> Self {
409 Self::Unit
410 }
411}
412
413#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
419pub struct ValueEnum(CanonicalEnumValue<Value>);
420
421impl ValueEnum {
422 #[cfg(test)]
423 pub(crate) const fn test_unit(type_id: u32, variant_id: u32) -> Self {
424 Self::new(
425 EnumTypeId::new(type_id).expect("test enum type ID must be non-zero"),
426 EnumVariantId::new(variant_id).expect("test enum variant ID must be non-zero"),
427 CanonicalEnumBody::Unit,
428 )
429 }
430
431 #[cfg(test)]
432 pub(crate) fn test_payload(type_id: u32, variant_id: u32, payload: Value) -> Self {
433 Self::new(
434 EnumTypeId::new(type_id).expect("test enum type ID must be non-zero"),
435 EnumVariantId::new(variant_id).expect("test enum variant ID must be non-zero"),
436 CanonicalEnumBody::Payload(Box::new(payload)),
437 )
438 }
439
440 #[cfg(test)]
441 pub(crate) fn test_with_payload(self, payload: Value) -> Self {
442 Self::new(
443 self.type_id(),
444 self.variant_id(),
445 CanonicalEnumBody::Payload(Box::new(payload)),
446 )
447 }
448
449 #[must_use]
450 pub(crate) const fn from_canonical(value: CanonicalEnumValue<Value>) -> Self {
451 Self(value)
452 }
453
454 #[must_use]
455 pub(crate) const fn new(
456 type_id: EnumTypeId,
457 variant_id: EnumVariantId,
458 body: CanonicalEnumBody<Value>,
459 ) -> Self {
460 Self(CanonicalEnumValue::new(type_id, variant_id, body))
461 }
462
463 #[must_use]
464 pub(crate) const fn canonical(&self) -> &CanonicalEnumValue<Value> {
465 &self.0
466 }
467
468 #[must_use]
469 pub(crate) const fn type_id(&self) -> EnumTypeId {
470 self.0.type_id()
471 }
472
473 #[must_use]
474 pub(crate) const fn variant_id(&self) -> EnumVariantId {
475 self.0.variant_id()
476 }
477
478 #[must_use]
479 pub(crate) const fn body(&self) -> &CanonicalEnumBody<Value> {
480 self.0.body()
481 }
482
483 #[must_use]
485 pub(crate) fn into_body(self) -> CanonicalEnumBody<Value> {
486 self.0.into_body()
487 }
488
489 #[must_use]
490 pub(crate) fn payload(&self) -> Option<&Value> {
491 match self.body() {
492 CanonicalEnumBody::Unit => None,
493 CanonicalEnumBody::Payload(payload) => Some(payload.as_ref()),
494 }
495 }
496}
497
498impl<'de> Deserialize<'de> for ValueEnum {
499 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
500 where
501 D: Deserializer<'de>,
502 {
503 let (type_id, variant_id, payload): (u32, u32, Option<Box<Value>>) =
504 Deserialize::deserialize(deserializer)?;
505 let type_id = EnumTypeId::new(type_id)
506 .ok_or_else(|| de::Error::custom("enum type ID must be non-zero"))?;
507 let variant_id = EnumVariantId::new(variant_id)
508 .ok_or_else(|| de::Error::custom("enum variant ID must be non-zero"))?;
509 let body = payload.map_or(CanonicalEnumBody::Unit, CanonicalEnumBody::Payload);
510 Ok(Self::new(type_id, variant_id, body))
511 }
512}
513
514crate::retained::retained_fields!(Value {
516Self::Account(field_0) => [field_0],
517Self::Blob(field_0) => [field_0],
518Self::Bool(field_0) => [field_0],
519Self::Date(field_0) => [field_0],
520Self::Decimal(field_0) => [field_0],
521Self::Duration(field_0) => [field_0],
522Self::Enum(field_0) => [field_0],
523Self::Float32(field_0) => [field_0],
524Self::Float64(field_0) => [field_0],
525Self::Int64(field_0) => [field_0],
526Self::Int128(field_0) => [field_0],
527Self::IntBig(field_0) => [field_0],
528Self::List(field_0) => [field_0],
529Self::Map(field_0) => [field_0],
530Self::Null => [],
531Self::Principal(field_0) => [field_0],
532Self::Subaccount(field_0) => [field_0],
533Self::Text(field_0) => [field_0],
534Self::Timestamp(field_0) => [field_0],
535Self::Nat64(field_0) => [field_0],
536Self::Nat128(field_0) => [field_0],
537Self::NatBig(field_0) => [field_0],
538Self::Ulid(field_0) => [field_0],
539Self::Unit => [],
540Self::U256(field_0) => [field_0],
541});
542crate::retained::retained_fields!(ValueEnum {
543Self(field_0) => [field_0],
544});