1mod canonical;
12mod canonical_enum;
13mod coercion;
14mod compare;
15#[cfg(any(test, feature = "query"))]
16mod hash;
17mod input;
18mod map;
19pub(crate) mod ops;
20mod output;
21mod rank;
22mod semantics;
23mod tag;
24mod wire;
25
26#[cfg(test)]
27mod tests;
28
29use crate::types::*;
30use serde::{Deserialize, Deserializer, de};
31use std::{cmp::Ordering, fmt};
32
33pub(crate) use canonical::canonicalize_value_set;
35pub(crate) use canonical_enum::{CanonicalEnumBody, CanonicalEnumValue, EnumTypeId, EnumVariantId};
36pub use coercion::CoercionFamily;
37#[cfg(test)]
38pub(crate) use hash::with_test_hash_override;
39#[cfg(any(test, feature = "query"))]
40pub(crate) use hash::{ValueHashWriter, hash_single_list_identity_canonical_value, hash_value};
41pub use input::{InputValue, InputValueEnum};
42pub use map::{MapValueError, SchemaInvariantError};
43pub use output::{OutputValue, OutputValueEnum, render_output_value_text};
44pub use tag::ValueTag;
45
46const VALUE_WIRE_TYPE_NAME: &str = "Value";
51const VALUE_WIRE_VARIANT_LABELS: &[&str] = &[
52 "Account",
53 "Blob",
54 "Bool",
55 "Date",
56 "Decimal",
57 "Duration",
58 "Enum",
59 "Float32",
60 "Float64",
61 "Int",
62 "Int128",
63 "IntBig",
64 "List",
65 "Map",
66 "Null",
67 "Principal",
68 "Subaccount",
69 "Text",
70 "Timestamp",
71 "Nat",
72 "Nat128",
73 "NatBig",
74 "Ulid",
75 "Unit",
76];
77
78#[derive(Clone, Copy)]
80enum ValueWireVariant {
81 Account,
82 Blob,
83 Bool,
84 Date,
85 Decimal,
86 Duration,
87 Enum,
88 Float32,
89 Float64,
90 Int64,
91 Int128,
92 IntBig,
93 List,
94 Map,
95 Null,
96 Principal,
97 Subaccount,
98 Text,
99 Timestamp,
100 Nat64,
101 Nat128,
102 NatBig,
103 Ulid,
104 Unit,
105}
106
107impl ValueWireVariant {
108 fn from_label(label: &str) -> Option<Self> {
110 match label {
111 "Account" => Some(Self::Account),
112 "Blob" => Some(Self::Blob),
113 "Bool" => Some(Self::Bool),
114 "Date" => Some(Self::Date),
115 "Decimal" => Some(Self::Decimal),
116 "Duration" => Some(Self::Duration),
117 "Enum" => Some(Self::Enum),
118 "Float32" => Some(Self::Float32),
119 "Float64" => Some(Self::Float64),
120 "Int" => Some(Self::Int64),
121 "Int128" => Some(Self::Int128),
122 "IntBig" => Some(Self::IntBig),
123 "List" => Some(Self::List),
124 "Map" => Some(Self::Map),
125 "Null" => Some(Self::Null),
126 "Principal" => Some(Self::Principal),
127 "Subaccount" => Some(Self::Subaccount),
128 "Text" => Some(Self::Text),
129 "Timestamp" => Some(Self::Timestamp),
130 "Nat" => Some(Self::Nat64),
131 "Nat128" => Some(Self::Nat128),
132 "NatBig" => Some(Self::NatBig),
133 "Ulid" => Some(Self::Ulid),
134 "Unit" => Some(Self::Unit),
135 _ => None,
136 }
137 }
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145pub enum TextMode {
146 Cs, Ci, }
149
150#[derive(Clone, Eq, PartialEq)]
164pub enum Value {
165 Account(Account),
166 Blob(Vec<u8>),
167 Bool(bool),
168 Date(Date),
169 Decimal(Decimal),
170 Duration(Duration),
171 Enum(ValueEnum),
172 Float32(Float32),
173 Float64(Float64),
174 Int64(i64),
175 Int128(i128),
176 IntBig(IntBig),
177 List(Vec<Self>),
181 Map(Vec<(Self, Self)>),
188 Null,
189 Principal(Principal),
190 Subaccount(Subaccount),
191 Text(String),
192 Timestamp(Timestamp),
193 Nat64(u64),
194 Nat128(u128),
195 NatBig(NatBig),
196 Ulid(Ulid),
197 Unit,
198}
199
200impl fmt::Debug for Value {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 match self {
203 Self::Account(value) => f.debug_tuple("Account").field(value).finish(),
204 Self::Blob(value) => write!(f, "Blob({} bytes)", value.len()),
205 Self::Bool(value) => f.debug_tuple("Bool").field(value).finish(),
206 Self::Date(value) => f.debug_tuple("Date").field(value).finish(),
207 Self::Decimal(value) => f.debug_tuple("Decimal").field(value).finish(),
208 Self::Duration(value) => f.debug_tuple("Duration").field(value).finish(),
209 Self::Enum(value) => f.debug_tuple("Enum").field(value).finish(),
210 Self::Float32(value) => f.debug_tuple("Float32").field(value).finish(),
211 Self::Float64(value) => f.debug_tuple("Float64").field(value).finish(),
212 Self::Int64(value) => f.debug_tuple("Int64").field(value).finish(),
213 Self::Int128(value) => f.debug_tuple("Int128").field(value).finish(),
214 Self::IntBig(value) => f.debug_tuple("IntBig").field(value).finish(),
215 Self::List(value) => f.debug_tuple("List").field(value).finish(),
216 Self::Map(value) => f.debug_tuple("Map").field(value).finish(),
217 Self::Null => f.write_str("Null"),
218 Self::Principal(value) => f.debug_tuple("Principal").field(value).finish(),
219 Self::Subaccount(value) => f.debug_tuple("Subaccount").field(value).finish(),
220 Self::Text(value) => f.debug_tuple("Text").field(value).finish(),
221 Self::Timestamp(value) => f.debug_tuple("Timestamp").field(value).finish(),
222 Self::Nat64(value) => f.debug_tuple("Nat64").field(value).finish(),
223 Self::Nat128(value) => f.debug_tuple("Nat128").field(value).finish(),
224 Self::NatBig(value) => f.debug_tuple("NatBig").field(value).finish(),
225 Self::Ulid(value) => f.debug_tuple("Ulid").field(value).finish(),
226 Self::Unit => f.write_str("Unit"),
227 }
228 }
229}
230
231impl Value {
232 pub fn from_slice<T>(items: &[T]) -> Self
241 where
242 T: Into<Self> + Clone,
243 {
244 Self::List(items.iter().cloned().map(Into::into).collect())
245 }
246
247 pub fn from_list<T>(items: Vec<T>) -> Self
251 where
252 T: Into<Self>,
253 {
254 Self::List(items.into_iter().map(Into::into).collect())
255 }
256
257 pub fn from_map(entries: Vec<(Self, Self)>) -> Result<Self, MapValueError> {
265 let normalized = map::normalize_map_entries(entries)?;
266 Ok(Self::Map(normalized))
267 }
268
269 #[must_use]
275 pub const fn is_text(&self) -> bool {
276 matches!(self, Self::Text(_))
277 }
278
279 #[must_use]
280 pub const fn is_scalar(&self) -> bool {
281 match self {
282 Self::List(_) | Self::Map(_) | Self::Unit => false,
284 _ => true,
285 }
286 }
287
288 #[must_use]
290 #[cfg(any(test, feature = "query"))]
291 pub(crate) fn contains_enum(&self) -> bool {
292 match self {
293 Self::Enum(_) => true,
294 Self::List(values) => values.iter().any(Self::contains_enum),
295 Self::Map(entries) => entries
296 .iter()
297 .any(|(key, value)| key.contains_enum() || value.contains_enum()),
298 _ => false,
299 }
300 }
301
302 #[must_use]
304 pub(crate) const fn canonical_tag(&self) -> ValueTag {
305 tag::canonical_tag(self)
306 }
307
308 #[must_use]
310 pub(crate) const fn canonical_rank(&self) -> u8 {
311 rank::canonical_rank(self)
312 }
313
314 #[must_use]
316 pub(crate) fn canonical_cmp(left: &Self, right: &Self) -> Ordering {
317 compare::canonical_cmp(left, right)
318 }
319
320 #[must_use]
322 pub(crate) fn canonical_cmp_key(left: &Self, right: &Self) -> Ordering {
323 compare::canonical_cmp_key(left, right)
324 }
325
326 #[must_use]
331 pub const fn as_text(&self) -> Option<&str> {
332 if let Self::Text(s) = self {
333 Some(s.as_str())
334 } else {
335 None
336 }
337 }
338
339 #[must_use]
340 pub const fn as_list(&self) -> Option<&[Self]> {
341 if let Self::List(xs) = self {
342 Some(xs.as_slice())
343 } else {
344 None
345 }
346 }
347
348 #[must_use]
349 pub const fn as_map(&self) -> Option<&[(Self, Self)]> {
350 if let Self::Map(entries) = self {
351 Some(entries.as_slice())
352 } else {
353 None
354 }
355 }
356}
357
358macro_rules! impl_from_for {
359 ( $( $type:ty => $variant:ident ),* $(,)? ) => {
360 $(
361 impl From<$type> for Value {
362 fn from(v: $type) -> Self {
363 Self::$variant(v.into())
364 }
365 }
366 )*
367 };
368}
369
370impl_from_for! {
371 Account => Account,
372 Date => Date,
373 Decimal => Decimal,
374 Duration => Duration,
375 bool => Bool,
376 i8 => Int64,
377 i16 => Int64,
378 i32 => Int64,
379 i64 => Int64,
380 i128 => Int128,
381 IntBig => IntBig,
382 Principal => Principal,
383 Subaccount => Subaccount,
384 &str => Text,
385 String => Text,
386 Timestamp => Timestamp,
387 u8 => Nat64,
388 u16 => Nat64,
389 u32 => Nat64,
390 u64 => Nat64,
391 u128 => Nat128,
392 NatBig => NatBig,
393 Ulid => Ulid,
394}
395
396impl From<Vec<Self>> for Value {
397 fn from(vec: Vec<Self>) -> Self {
398 Self::List(vec)
399 }
400}
401
402impl TryFrom<Vec<(Self, Self)>> for Value {
403 type Error = SchemaInvariantError;
404
405 fn try_from(entries: Vec<(Self, Self)>) -> Result<Self, Self::Error> {
406 Self::from_map(entries).map_err(Self::Error::from)
407 }
408}
409
410impl From<()> for Value {
411 fn from((): ()) -> Self {
412 Self::Unit
413 }
414}
415
416#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
422pub struct ValueEnum(CanonicalEnumValue<Value>);
423
424impl ValueEnum {
425 #[cfg(test)]
426 pub(crate) const fn test_unit(type_id: u32, variant_id: u32) -> Self {
427 Self::new(
428 EnumTypeId::new(type_id).expect("test enum type ID must be non-zero"),
429 EnumVariantId::new(variant_id).expect("test enum variant ID must be non-zero"),
430 CanonicalEnumBody::Unit,
431 )
432 }
433
434 #[cfg(test)]
435 pub(crate) fn test_payload(type_id: u32, variant_id: u32, payload: Value) -> Self {
436 Self::new(
437 EnumTypeId::new(type_id).expect("test enum type ID must be non-zero"),
438 EnumVariantId::new(variant_id).expect("test enum variant ID must be non-zero"),
439 CanonicalEnumBody::Payload(Box::new(payload)),
440 )
441 }
442
443 #[cfg(test)]
444 pub(crate) fn test_with_payload(self, payload: Value) -> Self {
445 Self::new(
446 self.type_id(),
447 self.variant_id(),
448 CanonicalEnumBody::Payload(Box::new(payload)),
449 )
450 }
451
452 #[must_use]
453 pub(crate) const fn from_canonical(value: CanonicalEnumValue<Value>) -> Self {
454 Self(value)
455 }
456
457 #[must_use]
458 pub(crate) const fn new(
459 type_id: EnumTypeId,
460 variant_id: EnumVariantId,
461 body: CanonicalEnumBody<Value>,
462 ) -> Self {
463 Self(CanonicalEnumValue::new(type_id, variant_id, body))
464 }
465
466 #[must_use]
467 pub(crate) const fn canonical(&self) -> &CanonicalEnumValue<Value> {
468 &self.0
469 }
470
471 #[must_use]
472 pub(crate) const fn type_id(&self) -> EnumTypeId {
473 self.0.type_id()
474 }
475
476 #[must_use]
477 pub(crate) const fn variant_id(&self) -> EnumVariantId {
478 self.0.variant_id()
479 }
480
481 #[must_use]
482 pub(crate) const fn body(&self) -> &CanonicalEnumBody<Value> {
483 self.0.body()
484 }
485
486 #[must_use]
487 pub(crate) fn payload(&self) -> Option<&Value> {
488 match self.body() {
489 CanonicalEnumBody::Unit => None,
490 CanonicalEnumBody::Payload(payload) => Some(payload.as_ref()),
491 }
492 }
493}
494
495impl<'de> Deserialize<'de> for ValueEnum {
496 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
497 where
498 D: Deserializer<'de>,
499 {
500 let (type_id, variant_id, payload): (u32, u32, Option<Box<Value>>) =
501 Deserialize::deserialize(deserializer)?;
502 let type_id = EnumTypeId::new(type_id)
503 .ok_or_else(|| de::Error::custom("enum type ID must be non-zero"))?;
504 let variant_id = EnumVariantId::new(variant_id)
505 .ok_or_else(|| de::Error::custom("enum variant ID must be non-zero"))?;
506 let body = payload.map_or(CanonicalEnumBody::Unit, CanonicalEnumBody::Payload);
507 Ok(Self::new(type_id, variant_id, body))
508 }
509}