qubit_value/value/value.rs
1// =============================================================================
2// Copyright (c) 2025 - 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! # Single Value Container
9//!
10//! Provides type-safe storage and access functionality for single values.
11// qubit-style: allow multiple-public-types
12
13use std::cmp::Ordering;
14use std::collections::HashMap;
15use std::fmt;
16#[cfg(feature = "json")]
17use std::hash::Hash;
18#[cfg(feature = "json")]
19use std::hash::Hasher;
20use std::time::Duration;
21
22#[cfg(feature = "big-decimal")]
23use bigdecimal::BigDecimal;
24#[cfg(feature = "chrono")]
25use chrono::DateTime;
26#[cfg(feature = "chrono")]
27use chrono::NaiveDate;
28#[cfg(feature = "chrono")]
29use chrono::NaiveDateTime;
30#[cfg(feature = "chrono")]
31use chrono::NaiveTime;
32#[cfg(feature = "chrono")]
33use chrono::Utc;
34#[cfg(feature = "big-integer")]
35use num_bigint::BigInt;
36#[cfg(feature = "json")]
37use qubit_budget::MeasuredBudgetError;
38#[cfg(feature = "json")]
39use qubit_budget::ResourceQuantity;
40#[cfg(feature = "json")]
41use qubit_budget::json::JsonValueBudget;
42#[cfg(feature = "converter")]
43use qubit_datatype::ConversionLimits;
44#[cfg(feature = "converter")]
45use qubit_datatype::ConversionPolicy;
46#[cfg(feature = "converter")]
47use qubit_datatype::ConversionSession;
48#[cfg(all(feature = "converter", feature = "json"))]
49use qubit_datatype::DataConversionError;
50#[cfg(feature = "converter")]
51use qubit_datatype::DataConversionTarget;
52#[cfg(all(feature = "converter", feature = "json"))]
53use qubit_datatype::DataFormat;
54use qubit_datatype::DataType;
55#[cfg(all(feature = "converter", feature = "json"))]
56use qubit_datatype::InvalidValueReason;
57use qubit_datatype::NumberRef;
58use qubit_datatype::NumericComparisonPolicy;
59#[cfg(all(feature = "converter", feature = "json"))]
60use qubit_json::encode::JsonSerializationErrorKind;
61#[cfg(all(feature = "converter", feature = "json"))]
62use qubit_json::value::JsonValueEncoder;
63#[cfg(all(feature = "converter", feature = "json"))]
64use serde::Deserialize;
65#[cfg(all(feature = "converter", feature = "json"))]
66use serde::Serialize;
67#[cfg(all(feature = "converter", feature = "json"))]
68use serde::de::DeserializeOwned;
69#[cfg(feature = "url")]
70use url::Url;
71
72use super::internal::ValueRepr;
73use super::value_ref::ValueRef;
74use crate::IntoValueDefault;
75use crate::NumericComparisonError;
76use crate::ValueError;
77use crate::ValueMissing;
78#[cfg(feature = "json")]
79use crate::identity::hash_json;
80#[cfg(feature = "json")]
81use crate::identity::preflight_json;
82#[cfg(feature = "json")]
83use crate::value::value_identity::hash_value_payload_with_json_budget;
84use crate::value_error::ValueResult;
85
86/// Single typed runtime value with private storage representation.
87///
88/// Construction and access are expressed through methods and conversions. The
89/// concrete enum representation is private so storage optimizations do not
90/// become part of the public API.
91///
92/// # Examples
93///
94/// ```
95/// use qubit_value::Value;
96///
97/// let value = Value::from(42_i32);
98/// assert_eq!(value.get_int32().unwrap(), 42);
99/// ```
100#[must_use]
101#[derive(Clone)]
102pub struct Value {
103 /// Private typed storage backing the stable public accessor API.
104 pub(crate) repr: ValueRepr,
105}
106
107impl fmt::Debug for Value {
108 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
109 self.view().fmt(formatter)
110 }
111}
112
113/// Implements named scalar constructors from the shared value table.
114macro_rules! impl_value_constructors {
115 (
116 ;
117 $(
118 (
119 [$($cfg:meta),*],
120 $variant:ident,
121 $type:ty,
122 $data_type:expr,
123 $materialization:ident,
124 $json_class:ident,
125 $number_projection:ident,
126 $value_doc:literal,
127 $multi_doc:literal
128 $(, $_wire:tt)*
129 )
130 ),+ $(,)?
131 ) => {
132 impl Value {
133 /// Creates an unset value with an explicit declared type.
134 ///
135 /// # Parameters
136 ///
137 /// * `data_type` - Runtime type retained while the value is unset.
138 ///
139 /// # Returns
140 ///
141 /// An unset scalar retaining `data_type`.
142 #[allow(non_snake_case)]
143 #[inline(always)]
144 pub const fn Unset(data_type: DataType) -> Self {
145 Self::new_unset(data_type)
146 }
147
148 /// Creates an unset value with an explicit declared type.
149 ///
150 /// # Parameters
151 ///
152 /// * `data_type` - Runtime type retained while the value is unset.
153 ///
154 /// # Returns
155 ///
156 /// An unset scalar retaining `data_type`.
157 #[inline(always)]
158 pub const fn new_unset(data_type: DataType) -> Self {
159 Self { repr: ValueRepr::Unset(data_type) }
160 }
161
162 $(
163 #[doc = concat!("Creates a ", $value_doc, ".")]
164 ///
165 /// # Parameters
166 ///
167 /// * `value` - Concrete payload stored by the returned scalar.
168 ///
169 /// # Returns
170 ///
171 /// A typed scalar containing `value`.
172 $(#[$cfg])*
173 #[allow(non_snake_case)]
174 #[inline(always)]
175 pub fn $variant(value: $type) -> Self {
176 Self { repr: ValueRepr::$variant(value_storage_new!($variant, value)) }
177 }
178 )+
179 }
180 };
181}
182
183/// Borrows owned storage through the shared type table.
184macro_rules! owned_view_match {
185 ($value:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal $(, $_wire:tt)*)),+ $(,)?) => {
186 match &$value.repr {
187 ValueRepr::Unset(data_type) => ValueRef::Unset(*data_type),
188 $($(#[$cfg])* ValueRepr::$variant(value) => ValueRef::$variant(
189 value_view_payload!($variant, $number_projection, value_storage_ref!($variant, value))
190 ),)+
191 }
192 };
193}
194
195for_each_value_type!(impl_value_constructors);
196
197impl Value {
198 /// Strictly borrows a stored scalar without allocating.
199 #[must_use = "the borrowed strict value result should be handled"]
200 #[inline(always)]
201 pub fn get_ref<'a, T: ?Sized>(&'a self) -> ValueResult<&'a T>
202 where
203 &'a T: TryFrom<&'a Self, Error = ValueError>,
204 {
205 <&'a T>::try_from(self)
206 }
207
208 /// Hashes this value while applying `budget` to a JSON payload.
209 ///
210 /// # Type Parameters
211 ///
212 /// * `H` - Hasher receiving the semantic value identity.
213 /// * `R` - Resource identifier used by the JSON budget.
214 /// * `Q` - Quantity type used by the JSON budget.
215 ///
216 /// # Parameters
217 ///
218 /// * `state` - Hasher that receives the same identity representation as
219 /// [`Hash::hash`].
220 /// * `budget` - Mutable JSON traversal budget, used only when this value
221 /// contains a JSON payload.
222 ///
223 /// # Returns
224 ///
225 /// `Ok(())` after the complete semantic identity is hashed.
226 ///
227 /// # Errors
228 ///
229 /// Returns [`qubit_budget::MeasuredBudgetError`] when the JSON payload
230 /// exceeds a configured limit. On error, neither `state` nor the committed
231 /// portion of `budget` is modified. A hasher panic also drops the
232 /// staged budget transaction.
233 ///
234 /// # Examples
235 ///
236 /// ```
237 /// use std::collections::hash_map::DefaultHasher;
238 ///
239 /// use qubit_budget::{ResourceLimit, StructureLimits};
240 /// use qubit_budget::json::{JsonResource, JsonValueBudget, JsonValueLimits};
241 /// use qubit_value::Value;
242 ///
243 /// let value = Value::Json(serde_json::json!([null]));
244 /// let structure = StructureLimits::<JsonResource, usize>::builder().nodes_limit(
245 /// ResourceLimit::new(JsonResource::Nodes, 1_usize),
246 /// ).build();
247 /// let mut budget = JsonValueBudget::new(
248 /// JsonValueLimits::builder().structure_limits(structure).build(),
249 /// );
250 /// let mut hasher = DefaultHasher::new();
251 ///
252 /// assert!(value.hash_with_json_budget(&mut hasher, &mut budget).is_err());
253 /// drop(hasher);
254 /// // The rejected value did not consume committed budget state.
255 /// ```
256 #[cfg(feature = "json")]
257 pub fn hash_with_json_budget<H, R, Q>(
258 &self,
259 state: &mut H,
260 budget: &mut JsonValueBudget<R, Q>,
261 ) -> Result<(), MeasuredBudgetError<R, Q>>
262 where
263 H: Hasher,
264 R: Clone,
265 Q: ResourceQuantity,
266 {
267 match &self.repr {
268 ValueRepr::Json(value) => {
269 let mut transaction = budget.transaction();
270 preflight_json(value, &mut transaction)?;
271 std::mem::discriminant(&self.repr).hash(state);
272 hash_json(value, state);
273 transaction.commit()
274 }
275 _ => {
276 std::mem::discriminant(&self.repr).hash(state);
277 hash_value_payload_with_json_budget(&self.repr, state, budget)
278 }
279 }
280 }
281
282 /// Borrows the stable semantic view of this value.
283 ///
284 /// # Returns
285 ///
286 /// A non-owning view that hides private storage representation details.
287 #[must_use = "the borrowed value view should be used"]
288 #[inline(always)]
289 pub fn view(&self) -> ValueRef<'_> {
290 for_each_value_type!(owned_view_match, self)
291 }
292}
293
294/// Maps private scalar storage variants to their runtime data types.
295macro_rules! value_data_type_match {
296 ($value:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal $(, $_wire:tt)*)),+ $(,)?) => {
297 match &$value.repr {
298 ValueRepr::Unset(data_type) => *data_type,
299 $($(#[$cfg])* ValueRepr::$variant(_) => $data_type,)+
300 }
301 };
302}
303
304// ============================================================================
305// Getter method generation macro
306// ============================================================================
307
308/// Unified getter generation macro
309///
310/// Supports two modes:
311/// 1. `copy:` - For types implementing the Copy trait, directly returns the
312/// value
313/// 2. `ref:` - For non-Copy types, returns a reference
314///
315/// # Documentation Comment Support
316///
317/// The macro automatically extracts preceding documentation comments, so
318/// you can add `///` comments before macro invocations.
319impl Value {
320 /// Generic constructor method
321 ///
322 /// Creates a `Value` from any supported type, avoiding direct use of
323 /// enum variants.
324 ///
325 /// # Supported Generic Types
326 ///
327 /// `Value::new<T>(value)` currently supports the following `T`:
328 ///
329 /// - `bool`
330 /// - `char`
331 /// - `i8`, `i16`, `i32`, `i64`, `i128`
332 /// - `u8`, `u16`, `u32`, `u64`, `u128`
333 /// - `f32`, `f64`
334 /// - `String`, `&str`
335 /// - `NaiveDate`, `NaiveTime`, `NaiveDateTime`, `DateTime<Utc>`
336 /// - `BigInt`, `BigDecimal`
337 /// - `Duration`
338 /// - `Url`
339 /// - `HashMap<String, String>`
340 /// - `serde_json::Value`
341 ///
342 /// # Type Parameters
343 ///
344 /// * `T` - The type of the value to wrap
345 ///
346 /// # Parameters
347 ///
348 /// * `value` - Value to wrap.
349 ///
350 /// # Returns
351 ///
352 /// Returns a `Value` wrapping the given value
353 ///
354 /// # Examples
355 ///
356 /// ```rust
357 /// use qubit_value::Value;
358 ///
359 /// // Basic types
360 /// let v = Value::new(42i32);
361 /// assert_eq!(v.get_int32().unwrap(), 42);
362 ///
363 /// let v = Value::new(true);
364 /// assert_eq!(v.get_bool().unwrap(), true);
365 ///
366 /// // String
367 /// let v = Value::new("hello".to_string());
368 /// assert_eq!(v.get_string().unwrap(), "hello");
369 /// ```
370 #[inline(always)]
371 pub fn new<T>(value: T) -> Self
372 where
373 T: Into<Self>,
374 {
375 value.into()
376 }
377
378 /// Generic getter method.
379 ///
380 /// Performs a strict typed read of the stored value as `T`.
381 ///
382 /// `get<T>()` performs strict type matching. It does not do cross-type
383 /// conversion.
384 ///
385 /// For example, `Value::Int32(42).get::<i64>()` fails, while
386 /// `Value::Int32(42).to::<i64>()` succeeds.
387 ///
388 /// # Supported Generic Types
389 ///
390 /// `Value::get<T>()` currently supports the following `T`:
391 ///
392 /// - `bool`
393 /// - `char`
394 /// - `i8`, `i16`, `i32`, `i64`, `i128`
395 /// - `u8`, `u16`, `u32`, `u64`, `u128`
396 /// - `f32`, `f64`
397 /// - `String`
398 /// - `NaiveDate`, `NaiveTime`, `NaiveDateTime`, `DateTime<Utc>`
399 /// - `BigInt`, `BigDecimal`
400 /// - `Duration`
401 /// - `Url`
402 /// - `HashMap<String, String>`
403 /// - `serde_json::Value`
404 ///
405 /// # Type Parameters
406 ///
407 /// * `T` - The target type to retrieve
408 ///
409 /// # Returns
410 ///
411 /// Returns the stored value when its type matches `T`.
412 ///
413 /// # Errors
414 ///
415 /// Returns [`ValueError::Missing`] when the value is unset with the
416 /// requested type, or [`ValueError::TypeMismatch`] when the stored type
417 /// differs from `T`.
418 ///
419 /// # Examples
420 ///
421 /// ```rust
422 /// use qubit_value::Value;
423 ///
424 /// let value = Value::Int32(42);
425 ///
426 /// // Through type inference
427 /// let num: i32 = value.get().unwrap();
428 /// assert_eq!(num, 42);
429 ///
430 /// // Explicitly specify type parameter
431 /// let num = value.get::<i32>().unwrap();
432 /// assert_eq!(num, 42);
433 ///
434 /// // Different type
435 /// let text = Value::String("hello".to_string());
436 /// let s: String = text.get().unwrap();
437 /// assert_eq!(s, "hello");
438 ///
439 /// // Boolean value
440 /// let flag = Value::Bool(true);
441 /// let b: bool = flag.get().unwrap();
442 /// assert_eq!(b, true);
443 /// ```
444 #[must_use = "the strict value read result should be handled"]
445 #[inline(always)]
446 pub fn get<T>(&self) -> ValueResult<T>
447 where
448 for<'a> T: TryFrom<&'a Self, Error = ValueError>,
449 {
450 T::try_from(self)
451 }
452
453 /// Generic getter method with a default value.
454 ///
455 /// Returns the supplied default only when this value is unset. Type
456 /// mismatches and conversion errors are still returned as errors.
457 ///
458 /// # Type Parameters
459 ///
460 /// * `T` - Target type for the strict read and default value.
461 ///
462 /// # Parameters
463 ///
464 /// * `default` - Lazily materialized value used only when `self` is unset.
465 ///
466 /// # Returns
467 ///
468 /// The stored value, or `default` when the value is unset.
469 ///
470 /// # Errors
471 ///
472 /// Returns [`ValueError::TypeMismatch`] when the stored type differs from
473 /// `T`.
474 #[must_use = "the strict value read result should be handled"]
475 #[inline(always)]
476 pub fn get_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
477 where
478 for<'a> T: TryFrom<&'a Self, Error = ValueError>,
479 {
480 match self.get() {
481 Err(ValueError::Missing(missing)) if missing.is_defaultable_for_strict_read() => {
482 Ok(default.into_value_default())
483 }
484 result => result,
485 }
486 }
487
488 /// Strictly reads this value or calls `default` only when it is unset.
489 ///
490 /// # Type Parameters
491 ///
492 /// * `T` - Target type for the strict read and fallback value.
493 /// * `F` - Deferred fallback producing `T`.
494 ///
495 /// # Parameters
496 ///
497 /// * `default` - Callback invoked only when this value is unset.
498 ///
499 /// # Returns
500 ///
501 /// The stored value, or the callback result for an unset value.
502 ///
503 /// # Errors
504 ///
505 /// Returns [`ValueError::TypeMismatch`] when the stored type differs from
506 /// `T`; the callback is not invoked in that case.
507 #[must_use = "the strict value read result should be handled"]
508 #[inline(always)]
509 pub fn get_or_else<T, F>(&self, default: F) -> ValueResult<T>
510 where
511 for<'a> T: TryFrom<&'a Self, Error = ValueError>,
512 F: FnOnce() -> T,
513 {
514 match self.get() {
515 Err(ValueError::Missing(missing)) if missing.is_defaultable_for_strict_read() => Ok(default()),
516 result => result,
517 }
518 }
519
520 /// Converts the stored value to another supported data type.
521 ///
522 /// This method delegates to the authoritative conversion contract in
523 /// [`qubit-datatype`](https://docs.rs/qubit-datatype/latest/qubit_datatype/).
524 /// The enabled rich-type features determine which source and target
525 /// families are available. An unset value is reported as a structured
526 /// missing-value conversion error.
527 ///
528 /// Unlike [`Self::get`], this method permits conversions supported by
529 /// [`qubit_datatype::DataConverter`] and applies
530 /// [`qubit_datatype::ConversionPolicy`] and
531 /// [`qubit_datatype::ConversionLimits`].
532 ///
533 /// # Type Parameters
534 ///
535 /// * `T` - Target type supported by the shared conversion layer.
536 ///
537 /// # Returns
538 ///
539 /// The converted value.
540 ///
541 /// # Errors
542 ///
543 /// Returns a mapped conversion error when the value is unset, the
544 /// conversion is unsupported, or the source is invalid for `T`.
545 ///
546 /// # Examples
547 ///
548 /// ```rust
549 /// use qubit_value::Value;
550 ///
551 /// let value = Value::Int32(42);
552 /// assert_eq!(value.to::<i64>().unwrap(), 42);
553 /// assert_eq!(value.to::<String>().unwrap(), "42");
554 /// ```
555 #[inline(always)]
556 #[cfg(feature = "converter")]
557 pub fn to<T>(&self) -> ValueResult<T>
558 where
559 T: DataConversionTarget,
560 {
561 self.to_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
562 }
563
564 /// Converts this value to `T`, or returns `default` when storage is unset
565 /// or conversion reports a missing value.
566 ///
567 /// Conversion failures from concrete values are preserved.
568 ///
569 /// # Type Parameters
570 ///
571 /// * `T` - Target conversion type.
572 ///
573 /// # Parameters
574 ///
575 /// * `default` - Lazily materialized value used for unset or conversion-
576 /// missing storage.
577 ///
578 /// # Returns
579 ///
580 /// The converted value, or `default` for an unset or conversion-missing
581 /// value.
582 ///
583 /// # Errors
584 ///
585 /// Returns a mapped conversion error for concrete values that cannot be
586 /// converted to `T`.
587 #[inline]
588 #[cfg(feature = "converter")]
589 pub fn to_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
590 where
591 T: DataConversionTarget,
592 {
593 match self.to() {
594 Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => {
595 Ok(default.into_value_default())
596 }
597 result => result,
598 }
599 }
600
601 /// Converts this value to `T`, or calls `default` when storage is unset or
602 /// conversion reports a missing value.
603 ///
604 /// # Type Parameters
605 ///
606 /// * `T` - Target conversion type.
607 /// * `F` - Deferred fallback producing `T`.
608 ///
609 /// # Parameters
610 ///
611 /// * `default` - Callback invoked only when conversion reports a missing
612 /// value.
613 ///
614 /// # Returns
615 ///
616 /// The converted value, or the callback result for an unset or
617 /// conversion-missing value.
618 ///
619 /// # Errors
620 ///
621 /// Preserves conversion errors from concrete values without invoking the
622 /// callback.
623 #[inline]
624 #[cfg(feature = "converter")]
625 pub fn to_or_else<T, F>(&self, default: F) -> ValueResult<T>
626 where
627 T: DataConversionTarget,
628 F: FnOnce() -> T,
629 {
630 match self.to() {
631 Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => Ok(default()),
632 result => result,
633 }
634 }
635
636 /// Converts this value to `T` using the provided conversion policy and
637 /// limits.
638 ///
639 /// This method uses the shared [`qubit_datatype`] conversion layer
640 /// directly, so policy settings such as string trimming, blank string
641 /// handling, and boolean aliases are applied consistently with other
642 /// value containers.
643 ///
644 /// # Type Parameters
645 ///
646 /// * `T` - The target type to convert to.
647 ///
648 /// # Parameters
649 ///
650 /// * `policy` - Conversion policy forwarded to the shared converter.
651 /// * `limits` - Conversion limits forwarded to the shared converter.
652 ///
653 /// # Returns
654 ///
655 /// Returns the converted value on success.
656 ///
657 /// # Errors
658 ///
659 /// Returns a [`crate::ValueError`] when the value is missing, unsupported,
660 /// or invalid for `T` under the provided policy and limits.
661 #[inline(always)]
662 #[cfg(feature = "converter")]
663 pub fn to_with<T>(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<T>
664 where
665 T: DataConversionTarget,
666 {
667 super::value_converters::convert_with_data_converter_with(self, policy, limits)
668 }
669
670 /// Converts this value to `T` while charging an existing conversion
671 /// session.
672 ///
673 /// # Type Parameters
674 ///
675 /// * `T` - Target type supported by the shared conversion layer.
676 ///
677 /// # Parameters
678 ///
679 /// * `session` - Caller-owned session providing policy, limits, and budget.
680 ///
681 /// # Returns
682 ///
683 /// The converted value.
684 ///
685 /// # Errors
686 ///
687 /// Returns a mapped conversion error when the value is missing,
688 /// unsupported, invalid, or exceeds the session budget.
689 #[inline(always)]
690 #[cfg(feature = "converter")]
691 pub fn to_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<T>
692 where
693 T: DataConversionTarget,
694 {
695 super::value_converters::convert_with_data_converter_in(self, session)
696 }
697
698 /// Converts this value to `T` using conversion policy and limits, or
699 /// returns `default` when storage is unset or conversion reports a
700 /// missing value.
701 ///
702 /// Conversion failures from concrete values are preserved.
703 ///
704 /// # Type Parameters
705 ///
706 /// * `T` - Target conversion type.
707 ///
708 /// # Parameters
709 ///
710 /// * `default` - Lazily materialized value used for unset or conversion-
711 /// missing storage.
712 /// * `policy` - Conversion policy forwarded to the shared converter.
713 /// * `limits` - Conversion limits forwarded to the shared converter.
714 ///
715 /// # Returns
716 ///
717 /// The converted value, or `default` for an unset or conversion-missing
718 /// value.
719 ///
720 /// # Errors
721 ///
722 /// Returns a mapped conversion error for concrete values that cannot be
723 /// converted under the provided policy and limits.
724 #[inline]
725 #[cfg(feature = "converter")]
726 pub fn to_or_with<T>(
727 &self,
728 default: impl IntoValueDefault<T>,
729 policy: &ConversionPolicy,
730 limits: &ConversionLimits,
731 ) -> ValueResult<T>
732 where
733 T: DataConversionTarget,
734 {
735 match self.to_with(policy, limits) {
736 Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => {
737 Ok(default.into_value_default())
738 }
739 result => result,
740 }
741 }
742
743 /// Converts this value with the provided policy and limits, or calls
744 /// `default` when storage is unset or conversion reports a missing
745 /// value.
746 ///
747 /// # Type Parameters
748 ///
749 /// * `T` - Target conversion type.
750 /// * `F` - Deferred fallback producing `T`.
751 ///
752 /// # Parameters
753 ///
754 /// * `default` - Callback invoked only for a missing source value.
755 /// * `policy` - Conversion policy forwarded to the shared converter.
756 /// * `limits` - Conversion limits forwarded to the shared converter.
757 ///
758 /// # Returns
759 ///
760 /// The converted value, or the callback result for an unset or
761 /// conversion-missing value.
762 ///
763 /// # Errors
764 ///
765 /// Preserves concrete-value conversion errors without invoking the
766 /// callback.
767 #[inline]
768 #[cfg(feature = "converter")]
769 pub fn to_or_else_with<T, F>(
770 &self,
771 default: F,
772 policy: &ConversionPolicy,
773 limits: &ConversionLimits,
774 ) -> ValueResult<T>
775 where
776 T: DataConversionTarget,
777 F: FnOnce() -> T,
778 {
779 match self.to_with(policy, limits) {
780 Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => Ok(default()),
781 result => result,
782 }
783 }
784
785 /// Generic setter method
786 ///
787 /// Replaces the current value with any supported input value.
788 ///
789 /// This operation updates the stored type to `T` when needed. It does not
790 /// perform runtime type-mismatch validation against the previous variant.
791 ///
792 /// # Supported Generic Types
793 ///
794 /// `Value::set<T>(value)` currently supports the following `T`:
795 ///
796 /// - `bool`
797 /// - `char`
798 /// - `i8`, `i16`, `i32`, `i64`, `i128`
799 /// - `u8`, `u16`, `u32`, `u64`, `u128`
800 /// - `f32`, `f64`
801 /// - `String`, `&str`
802 /// - `NaiveDate`, `NaiveTime`, `NaiveDateTime`, `DateTime<Utc>`
803 /// - `BigInt`, `BigDecimal`
804 /// - `Duration`
805 /// - `Url`
806 /// - `HashMap<String, String>`
807 /// - `serde_json::Value`
808 ///
809 /// # Type Parameters
810 ///
811 /// * `T` - Input type convertible into [`Value`].
812 ///
813 /// # Parameters
814 ///
815 /// * `value` - The value to set
816 ///
817 /// # Compile-time restriction
818 ///
819 /// Unsupported input types fail to compile because they do not implement
820 /// `Into<Value>`.
821 ///
822 /// # Examples
823 ///
824 /// ```rust
825 /// use qubit_datatype::DataType;
826 /// use qubit_value::Value;
827 ///
828 /// let mut value = Value::Unset(DataType::Int32);
829 ///
830 /// // Through type inference
831 /// value.set(42i32);
832 /// assert_eq!(value.get_int32().unwrap(), 42);
833 ///
834 /// // Explicitly specify type parameter
835 /// value.set::<i32>(100);
836 /// assert_eq!(value.get_int32().unwrap(), 100);
837 ///
838 /// // String type
839 /// let mut text = Value::Unset(DataType::String);
840 /// text.set("hello".to_string());
841 /// assert_eq!(text.get_string().unwrap(), "hello");
842 /// ```
843 #[inline(always)]
844 pub fn set<T>(&mut self, value: T)
845 where
846 T: Into<Self>,
847 {
848 *self = value.into();
849 }
850
851 /// Get the data type of the value
852 ///
853 /// # Returns
854 ///
855 /// Returns the data type corresponding to this value
856 ///
857 /// # Examples
858 ///
859 /// ```rust
860 /// use qubit_datatype::DataType;
861 /// use qubit_value::Value;
862 ///
863 /// let value = Value::Int32(42);
864 /// assert_eq!(value.data_type(), DataType::Int32);
865 ///
866 /// let empty = Value::Unset(DataType::String);
867 /// assert_eq!(empty.data_type(), DataType::String);
868 /// ```
869 ///
870 /// ```compile_fail
871 /// #![deny(unused_must_use)]
872 /// use qubit_value::Value;
873 ///
874 /// Value::new(42_i32).data_type();
875 /// ```
876 #[must_use = "the runtime data type should be used"]
877 #[inline(always)]
878 pub fn data_type(&self) -> DataType {
879 for_each_value_type!(value_data_type_match, self)
880 }
881
882 /// Tests whether this container has no concrete value.
883 ///
884 /// # Returns
885 ///
886 /// Returns `true` only for [`Value::Unset`]. An empty string, map, or JSON
887 /// container is still a concrete value and returns `false`.
888 ///
889 /// # Examples
890 ///
891 /// ```rust
892 /// use qubit_datatype::DataType;
893 /// use qubit_value::Value;
894 ///
895 /// let value = Value::Int32(42);
896 /// assert!(!value.is_unset());
897 ///
898 /// let empty = Value::Unset(DataType::String);
899 /// assert!(empty.is_unset());
900 /// ```
901 #[inline(always)]
902 #[must_use]
903 pub fn is_unset(&self) -> bool {
904 matches!(self.repr, ValueRepr::Unset(_))
905 }
906
907 /// Tests whether a concrete value belongs to the numeric type family.
908 ///
909 /// An unset value returns `false`, even when its declared type is numeric.
910 ///
911 /// # Returns
912 ///
913 /// `true` for concrete numeric variants; otherwise `false`.
914 #[inline(always)]
915 #[must_use]
916 pub fn is_numeric(&self) -> bool {
917 !self.is_unset() && self.data_type().is_numeric()
918 }
919
920 /// Removes the concrete value while preserving its declared data type.
921 #[inline(always)]
922 pub fn unset(&mut self) {
923 *self = Value::new_unset(self.data_type());
924 }
925
926 /// Set the data type
927 ///
928 /// If the new type differs from the current type, clears the value
929 /// and sets the new type.
930 ///
931 /// # Parameters
932 ///
933 /// * `data_type` - The data type to set
934 ///
935 /// # Examples
936 ///
937 /// ```rust
938 /// use qubit_datatype::DataType;
939 /// use qubit_value::Value;
940 ///
941 /// let mut value = Value::Int32(42);
942 /// value.set_type(DataType::String);
943 /// assert!(value.is_unset());
944 /// assert_eq!(value.data_type(), DataType::String);
945 /// ```
946 #[inline(always)]
947 pub fn set_type(&mut self, data_type: DataType) {
948 if self.data_type() != data_type {
949 *self = Value::new_unset(data_type);
950 }
951 }
952}
953
954#[cfg(all(feature = "converter", feature = "json"))]
955impl Value {
956 /// Projects this typed value to its natural JSON representation.
957 ///
958 /// This differs from the tagged [`crate::ValueWireV1`] representation: for
959 /// example, `Value::Int32(42)` projects to the JSON number `42`.
960 ///
961 /// # Returns
962 ///
963 /// The natural JSON representation of this value.
964 ///
965 /// # Errors
966 ///
967 /// Returns a structured conversion error for values JSON cannot represent,
968 /// including non-finite floating-point values and inexact durations.
969 #[inline(always)]
970 pub fn to_json_value(&self) -> ValueResult<serde_json::Value> {
971 self.to_json_value_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
972 }
973
974 /// Projects this typed value using explicit conversion policy and limits.
975 ///
976 /// # Parameters
977 ///
978 /// * `policy` - Controls duration units and precision-loss behavior.
979 /// * `limits` - Bounds conversion resource consumption.
980 ///
981 /// # Returns
982 ///
983 /// The natural JSON representation of this value.
984 ///
985 /// # Errors
986 ///
987 /// Returns a structured conversion error when JSON projection or duration
988 /// formatting violates the requested policy or limits.
989 #[inline(always)]
990 pub fn to_json_value_with(
991 &self,
992 policy: &ConversionPolicy,
993 limits: &ConversionLimits,
994 ) -> ValueResult<serde_json::Value> {
995 crate::json::value_to_json_value_with(self, policy, limits)
996 }
997}
998
999/// Implements one strict typed getter from the shared value table.
1000macro_rules! impl_get_value {
1001 // Copy type: directly dereference and return
1002 ($(#[$attr:meta])* copy: $method:ident, $variant:ident, $type:ty, $data_type:expr) => {
1003 $(#[$attr])*
1004 #[doc = ""]
1005 #[doc = "# Errors"]
1006 #[doc = ""]
1007 #[doc = "Returns [`ValueError::Missing`] when the value is unset with"]
1008 #[doc = "the requested type, or [`ValueError::TypeMismatch`] when the"]
1009 #[doc = "stored data type differs."]
1010 #[must_use = "the strict value read result should be handled"]
1011 #[inline(always)]
1012 pub fn $method(&self) -> ValueResult<$type> {
1013 match &self.repr {
1014 ValueRepr::$variant(v) => Ok(*v),
1015 ValueRepr::Unset(dt) if *dt == $data_type => {
1016 Err(ValueError::Missing($crate::ValueMissing::unset_scalar(*dt, *dt)))
1017 }
1018 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1019 expected: $data_type,
1020 actual: *dt,
1021 }),
1022 _ => Err(ValueError::TypeMismatch {
1023 expected: $data_type,
1024 actual: self.data_type(),
1025 }),
1026 }
1027 }
1028 };
1029
1030 // Reference type: use conversion function to return reference,
1031 // fixing lifetime issues
1032 ($(#[$attr:meta])* ref: $method:ident, $variant:ident, $ret_type:ty, $data_type:expr, $conversion:expr) => {
1033 $(#[$attr])*
1034 #[doc = ""]
1035 #[doc = "# Errors"]
1036 #[doc = ""]
1037 #[doc = "Returns [`ValueError::Missing`] when the value is unset with"]
1038 #[doc = "the requested type, or [`ValueError::TypeMismatch`] when the"]
1039 #[doc = "stored data type differs."]
1040 #[must_use = "the strict value read result should be handled"]
1041 #[inline(always)]
1042 pub fn $method(&self) -> ValueResult<$ret_type> {
1043 match &self.repr {
1044 ValueRepr::$variant(v) => {
1045 let conv_fn: fn(&_) -> $ret_type = $conversion;
1046 Ok(conv_fn(v))
1047 },
1048 ValueRepr::Unset(dt) if *dt == $data_type => {
1049 Err(ValueError::Missing($crate::ValueMissing::unset_scalar(*dt, *dt)))
1050 }
1051 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1052 expected: $data_type,
1053 actual: *dt,
1054 }),
1055 _ => Err(ValueError::TypeMismatch {
1056 expected: $data_type,
1057 actual: self.data_type(),
1058 }),
1059 }
1060 }
1061 };
1062}
1063
1064impl Value {
1065 /// Creates a `Value` from a `serde_json::Value`.
1066 ///
1067 /// # Parameters
1068 ///
1069 /// * `json` - The JSON value to wrap.
1070 ///
1071 /// # Returns
1072 ///
1073 /// A `Value::Json` wrapping the given JSON value.
1074 #[inline(always)]
1075 #[cfg(feature = "json")]
1076 pub fn from_json_value(json: serde_json::Value) -> Self {
1077 Value::Json(json)
1078 }
1079
1080 /// Creates a `Value` from any serializable value by converting it to JSON.
1081 ///
1082 /// # Type Parameters
1083 ///
1084 /// * `T` - Any type implementing `Serialize`.
1085 ///
1086 /// # Parameters
1087 ///
1088 /// * `value` - The value to serialize into JSON.
1089 ///
1090 /// # Returns
1091 ///
1092 /// A `Value::Json` containing the serialized representation.
1093 ///
1094 /// # Errors
1095 ///
1096 /// Returns [`ValueError::Conversion`] with
1097 /// A non-finite reason is returned when any nested float is non-finite, an
1098 /// out-of-range reason when an integer exceeds the strict JSON range, or a
1099 /// serialization reason for every other unsupported Serde representation.
1100 #[cfg(all(feature = "converter", feature = "json"))]
1101 pub fn from_serializable<T: ?Sized + Serialize>(value: &T) -> ValueResult<Self> {
1102 let json = JsonValueEncoder::new().encode(value).map_err(|error| {
1103 let reason = match error.kind() {
1104 JsonSerializationErrorKind::NonFiniteFloat => InvalidValueReason::NonFinite,
1105 JsonSerializationErrorKind::IntegerOutOfRange { .. } => InvalidValueReason::OutOfRange,
1106 _ => InvalidValueReason::Serialization {
1107 format: DataFormat::Json,
1108 },
1109 };
1110 ValueError::from(DataConversionError::invalid(DataType::Json, DataType::Json, reason))
1111 })?;
1112 Ok(Value::Json(json))
1113 }
1114
1115 // ========================================================================
1116 // Type-checking getters (strict type matching)
1117 // ========================================================================
1118
1119 impl_get_value! {
1120 /// Get boolean value
1121 ///
1122 /// # Returns
1123 ///
1124 /// If types match, returns the boolean value; see `# Errors`.
1125 ///
1126 /// # Examples
1127 ///
1128 /// ```rust
1129 /// use qubit_value::Value;
1130 ///
1131 /// let value = Value::Bool(true);
1132 /// assert_eq!(value.get_bool().unwrap(), true);
1133 /// ```
1134 copy: get_bool, Bool, bool, DataType::Bool
1135 }
1136
1137 impl_get_value! {
1138 /// Get character value
1139 ///
1140 /// # Returns
1141 ///
1142 /// If types match, returns the character value; see `# Errors`.
1143 ///
1144 /// # Examples
1145 ///
1146 /// ```rust
1147 /// use qubit_value::Value;
1148 ///
1149 /// let value = Value::Char('A');
1150 /// assert_eq!(value.get_char().unwrap(), 'A');
1151 /// ```
1152 copy: get_char, Char, char, DataType::Char
1153 }
1154
1155 impl_get_value! {
1156 /// Get int8 value
1157 ///
1158 /// # Returns
1159 ///
1160 /// If types match, returns the int8 value; see `# Errors`.
1161 copy: get_int8, Int8, i8, DataType::Int8
1162 }
1163
1164 impl_get_value! {
1165 /// Get int16 value
1166 ///
1167 /// # Returns
1168 ///
1169 /// If types match, returns the int16 value; see `# Errors`.
1170 copy: get_int16, Int16, i16, DataType::Int16
1171 }
1172
1173 impl_get_value! {
1174 /// Get int32 value
1175 ///
1176 /// # Returns
1177 ///
1178 /// If types match, returns the int32 value; see `# Errors`.
1179 copy: get_int32, Int32, i32, DataType::Int32
1180 }
1181
1182 impl_get_value! {
1183 /// Get int64 value
1184 ///
1185 /// # Returns
1186 ///
1187 /// If types match, returns the int64 value; see `# Errors`.
1188 copy: get_int64, Int64, i64, DataType::Int64
1189 }
1190
1191 impl_get_value! {
1192 /// Get int128 value
1193 ///
1194 /// # Returns
1195 ///
1196 /// If types match, returns the int128 value; see `# Errors`.
1197 copy: get_int128, Int128, i128, DataType::Int128
1198 }
1199
1200 impl_get_value! {
1201 /// Get uint8 value
1202 ///
1203 /// # Returns
1204 ///
1205 /// If types match, returns the uint8 value; see `# Errors`.
1206 copy: get_uint8, UInt8, u8, DataType::UInt8
1207 }
1208
1209 impl_get_value! {
1210 /// Get uint16 value
1211 ///
1212 /// # Returns
1213 ///
1214 /// If types match, returns the uint16 value; see `# Errors`.
1215 copy: get_uint16, UInt16, u16, DataType::UInt16
1216 }
1217
1218 impl_get_value! {
1219 /// Get uint32 value
1220 ///
1221 /// # Returns
1222 ///
1223 /// If types match, returns the uint32 value; see `# Errors`.
1224 copy: get_uint32, UInt32, u32, DataType::UInt32
1225 }
1226
1227 impl_get_value! {
1228 /// Get uint64 value
1229 ///
1230 /// # Returns
1231 ///
1232 /// If types match, returns the uint64 value; see `# Errors`.
1233 copy: get_uint64, UInt64, u64, DataType::UInt64
1234 }
1235
1236 impl_get_value! {
1237 /// Get uint128 value
1238 ///
1239 /// # Returns
1240 ///
1241 /// If types match, returns the uint128 value; see `# Errors`.
1242 copy: get_uint128, UInt128, u128, DataType::UInt128
1243 }
1244
1245 impl_get_value! {
1246 /// Get float32 value
1247 ///
1248 /// # Returns
1249 ///
1250 /// If types match, returns the float32 value; see `# Errors`.
1251 copy: get_float32, Float32, f32, DataType::Float32
1252 }
1253
1254 impl_get_value! {
1255 /// Get float64 value
1256 ///
1257 /// # Returns
1258 ///
1259 /// If types match, returns the float64 value; see `# Errors`.
1260 copy: get_float64, Float64, f64, DataType::Float64
1261 }
1262
1263 impl_get_value! {
1264 /// Get string reference
1265 ///
1266 /// # Returns
1267 ///
1268 /// If types match, returns a reference to the string; see `# Errors`.
1269 ///
1270 /// # Examples
1271 ///
1272 /// ```rust
1273 /// use qubit_value::Value;
1274 ///
1275 /// let value = Value::String("hello".to_string());
1276 /// assert_eq!(value.get_string().unwrap(), "hello");
1277 /// ```
1278 ref: get_string, String, &str, DataType::String, |s: &String| s.as_str()
1279 }
1280
1281 #[cfg(feature = "chrono")]
1282 impl_get_value! {
1283 /// Get date value
1284 ///
1285 /// # Returns
1286 ///
1287 /// If types match, returns the date value; see `# Errors`.
1288 copy: get_date, Date, NaiveDate, DataType::Date
1289 }
1290
1291 #[cfg(feature = "chrono")]
1292 impl_get_value! {
1293 /// Get time value
1294 ///
1295 /// # Returns
1296 ///
1297 /// If types match, returns the time value; see `# Errors`.
1298 copy: get_time, Time, NaiveTime, DataType::Time
1299 }
1300
1301 #[cfg(feature = "chrono")]
1302 impl_get_value! {
1303 /// Get datetime value
1304 ///
1305 /// # Returns
1306 ///
1307 /// If types match, returns the datetime value; see `# Errors`.
1308 copy: get_datetime, DateTime, NaiveDateTime, DataType::DateTime
1309 }
1310
1311 #[cfg(feature = "chrono")]
1312 impl_get_value! {
1313 /// Get UTC instant value
1314 ///
1315 /// # Returns
1316 ///
1317 /// If types match, returns the UTC instant value; see `# Errors`.
1318 copy: get_instant, Instant, DateTime<Utc>, DataType::Instant
1319 }
1320
1321 #[cfg(feature = "big-integer")]
1322 impl_get_value! {
1323 /// Get big integer value.
1324 ///
1325 /// This method returns a cloned [`BigInt`]. Use
1326 /// [`Value::get_biginteger_ref`] to borrow the stored value without
1327 /// cloning.
1328 ///
1329 /// # Returns
1330 ///
1331 /// If types match, returns the big integer value; see `# Errors`.
1332 ///
1333 /// # Examples
1334 ///
1335 /// ```rust
1336 /// use qubit_value::Value;
1337 /// use num_bigint::BigInt;
1338 ///
1339 /// let value = Value::BigInteger(BigInt::from(123456789));
1340 /// assert_eq!(value.get_biginteger().unwrap(), BigInt::from(123456789));
1341 /// ```
1342 ref: get_biginteger, BigInteger, BigInt, DataType::BigInteger, |v: &BigInt| v.clone()
1343 }
1344
1345 #[cfg(feature = "big-decimal")]
1346 impl_get_value! {
1347 /// Get big decimal value.
1348 ///
1349 /// This method returns a cloned [`BigDecimal`]. Use
1350 /// [`Value::get_bigdecimal_ref`] to borrow the stored value without
1351 /// cloning.
1352 ///
1353 /// # Returns
1354 ///
1355 /// If types match, returns the big decimal value; see `# Errors`.
1356 ///
1357 /// # Examples
1358 ///
1359 /// ```rust
1360 /// use std::str::FromStr;
1361 ///
1362 /// use bigdecimal::BigDecimal;
1363 /// use qubit_value::Value;
1364 ///
1365 /// let bd = BigDecimal::from_str("123.456").unwrap();
1366 /// let value = Value::BigDecimal(bd.clone());
1367 /// assert_eq!(value.get_bigdecimal().unwrap(), bd);
1368 /// ```
1369 ref: get_bigdecimal, BigDecimal, BigDecimal, DataType::BigDecimal, |v: &BigDecimal| v.clone()
1370 }
1371
1372 impl_get_value! {
1373 /// Get Duration value
1374 ///
1375 /// # Returns
1376 ///
1377 /// If types match, returns the Duration value; see `# Errors`.
1378 copy: get_duration, Duration, Duration, DataType::Duration
1379 }
1380
1381 #[cfg(feature = "url")]
1382 impl_get_value! {
1383 /// Get URL value.
1384 ///
1385 /// This method returns a cloned [`Url`]. Use [`Value::get_url_ref`] to
1386 /// borrow the stored value without cloning.
1387 ///
1388 /// # Returns
1389 ///
1390 /// If types match, returns the URL value; see `# Errors`.
1391 ref: get_url, Url, Url, DataType::Url, Url::clone
1392 }
1393
1394 impl_get_value! {
1395 /// Get string map value.
1396 ///
1397 /// This method returns a cloned `HashMap<String, String>`. Use
1398 /// [`Value::get_string_map_ref`] to borrow the stored value without
1399 /// cloning.
1400 ///
1401 /// # Returns
1402 ///
1403 /// If types match, returns the string map value; see `# Errors`.
1404 ref: get_string_map, StringMap, HashMap<String, String>, DataType::StringMap,
1405 |v: &HashMap<String, String>| v.clone()
1406 }
1407
1408 #[cfg(feature = "json")]
1409 impl_get_value! {
1410 /// Get JSON value.
1411 ///
1412 /// This method returns a cloned [`serde_json::Value`]. Use
1413 /// [`Value::get_json_ref`] to borrow the stored value without cloning.
1414 ///
1415 /// # Returns
1416 ///
1417 /// If types match, returns the JSON value; see `# Errors`.
1418 ref: get_json, Json, serde_json::Value, DataType::Json,
1419 |v: &serde_json::Value| v.clone()
1420 }
1421
1422 /// Borrow the inner `BigInt` without cloning.
1423 ///
1424 /// # Returns
1425 ///
1426 /// A shared reference to the stored integer.
1427 ///
1428 /// # Errors
1429 ///
1430 /// Returns [`ValueError::Missing`] when the value is unset with
1431 /// `DataType::BigInteger`, or [`ValueError::TypeMismatch`] when the stored
1432 /// data type differs.
1433 #[cfg(feature = "big-integer")]
1434 #[must_use = "the strict value read result should be handled"]
1435 #[inline(always)]
1436 pub fn get_biginteger_ref(&self) -> ValueResult<&BigInt> {
1437 match &self.repr {
1438 ValueRepr::BigInteger(v) => Ok(v),
1439 ValueRepr::Unset(dt) if *dt == DataType::BigInteger => {
1440 Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
1441 }
1442 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1443 expected: DataType::BigInteger,
1444 actual: *dt,
1445 }),
1446 _ => Err(ValueError::TypeMismatch {
1447 expected: DataType::BigInteger,
1448 actual: self.data_type(),
1449 }),
1450 }
1451 }
1452
1453 /// Borrow the inner `BigDecimal` without cloning.
1454 ///
1455 /// # Returns
1456 ///
1457 /// A shared reference to the stored decimal.
1458 ///
1459 /// # Errors
1460 ///
1461 /// Returns [`ValueError::Missing`] when the value is unset with
1462 /// `DataType::BigDecimal`, or [`ValueError::TypeMismatch`] when the stored
1463 /// data type differs.
1464 #[cfg(feature = "big-decimal")]
1465 #[must_use = "the strict value read result should be handled"]
1466 #[inline(always)]
1467 pub fn get_bigdecimal_ref(&self) -> ValueResult<&BigDecimal> {
1468 match &self.repr {
1469 ValueRepr::BigDecimal(v) => Ok(v),
1470 ValueRepr::Unset(dt) if *dt == DataType::BigDecimal => {
1471 Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
1472 }
1473 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1474 expected: DataType::BigDecimal,
1475 actual: *dt,
1476 }),
1477 _ => Err(ValueError::TypeMismatch {
1478 expected: DataType::BigDecimal,
1479 actual: self.data_type(),
1480 }),
1481 }
1482 }
1483
1484 /// Borrow the inner `Url` without cloning.
1485 ///
1486 /// # Returns
1487 ///
1488 /// A shared reference to the stored URL.
1489 ///
1490 /// # Errors
1491 ///
1492 /// Returns [`ValueError::Missing`] when the value is unset with
1493 /// `DataType::Url`, or [`ValueError::TypeMismatch`] when the stored data
1494 /// type differs.
1495 #[cfg(feature = "url")]
1496 #[must_use = "the strict value read result should be handled"]
1497 #[inline(always)]
1498 pub fn get_url_ref(&self) -> ValueResult<&Url> {
1499 match &self.repr {
1500 ValueRepr::Url(v) => Ok(v.as_ref()),
1501 ValueRepr::Unset(dt) if *dt == DataType::Url => {
1502 Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
1503 }
1504 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1505 expected: DataType::Url,
1506 actual: *dt,
1507 }),
1508 _ => Err(ValueError::TypeMismatch {
1509 expected: DataType::Url,
1510 actual: self.data_type(),
1511 }),
1512 }
1513 }
1514
1515 /// Borrow the inner `HashMap<String, String>` without cloning.
1516 ///
1517 /// # Returns
1518 ///
1519 /// A shared reference to the stored string map.
1520 ///
1521 /// # Errors
1522 ///
1523 /// Returns [`ValueError::Missing`] when the value is unset with
1524 /// `DataType::StringMap`, or [`ValueError::TypeMismatch`] when the stored
1525 /// data type differs.
1526 #[must_use = "the strict value read result should be handled"]
1527 #[inline(always)]
1528 pub fn get_string_map_ref(&self) -> ValueResult<&HashMap<String, String>> {
1529 match &self.repr {
1530 ValueRepr::StringMap(v) => Ok(v),
1531 ValueRepr::Unset(dt) if *dt == DataType::StringMap => {
1532 Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
1533 }
1534 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1535 expected: DataType::StringMap,
1536 actual: *dt,
1537 }),
1538 _ => Err(ValueError::TypeMismatch {
1539 expected: DataType::StringMap,
1540 actual: self.data_type(),
1541 }),
1542 }
1543 }
1544
1545 /// Borrow the inner JSON value without cloning.
1546 ///
1547 /// # Returns
1548 ///
1549 /// A shared reference to the stored JSON value.
1550 ///
1551 /// # Errors
1552 ///
1553 /// Returns [`ValueError::Missing`] when the value is unset with
1554 /// `DataType::Json`, or [`ValueError::TypeMismatch`] when the stored data
1555 /// type differs.
1556 #[cfg(feature = "json")]
1557 #[must_use = "the strict value read result should be handled"]
1558 #[inline(always)]
1559 pub fn get_json_ref(&self) -> ValueResult<&serde_json::Value> {
1560 match &self.repr {
1561 ValueRepr::Json(v) => Ok(v),
1562 ValueRepr::Unset(dt) if *dt == DataType::Json => {
1563 Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
1564 }
1565 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1566 expected: DataType::Json,
1567 actual: *dt,
1568 }),
1569 _ => Err(ValueError::TypeMismatch {
1570 expected: DataType::Json,
1571 actual: self.data_type(),
1572 }),
1573 }
1574 }
1575
1576 /// Deserialize the inner JSON value into a target type.
1577 ///
1578 /// Only works when `self` is `Value::Json(...)`.
1579 ///
1580 /// # Type Parameters
1581 ///
1582 /// * `T` - The target type implementing `DeserializeOwned`.
1583 ///
1584 /// # Returns
1585 ///
1586 /// Returns `Ok(T)` on success.
1587 ///
1588 /// # Errors
1589 ///
1590 /// Returns [`ValueError::Missing`] when this value is
1591 /// `Value::Unset(DataType::Json)`,
1592 /// [`ValueError::TypeMismatch`] when this value has a non-JSON data type,
1593 /// or [`ValueError::Conversion`] when JSON deserialization fails.
1594 #[cfg(all(feature = "converter", feature = "json"))]
1595 pub fn deserialize_json<T: DeserializeOwned>(&self) -> ValueResult<T> {
1596 match &self.repr {
1597 ValueRepr::Json(v) => Deserialize::deserialize(v).map_err(|_| {
1598 ValueError::from(DataConversionError::invalid(
1599 DataType::Json,
1600 DataType::Json,
1601 InvalidValueReason::Deserialization {
1602 format: DataFormat::Json,
1603 },
1604 ))
1605 }),
1606 ValueRepr::Unset(dt) if *dt == DataType::Json => {
1607 Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
1608 }
1609 ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1610 expected: DataType::Json,
1611 actual: *dt,
1612 }),
1613 _ => Err(ValueError::TypeMismatch {
1614 expected: DataType::Json,
1615 actual: self.data_type(),
1616 }),
1617 }
1618 }
1619}
1620
1621/// Projects one stored value according to its type-table numeric strategy.
1622macro_rules! project_number_ref {
1623 (number_copy, $value:expr) => {
1624 Some(NumberRef::from(*$value))
1625 };
1626 (number_ref, $value:expr) => {
1627 Some(NumberRef::from($value))
1628 };
1629 (not_number, $value:expr) => {{
1630 let _ = $value;
1631 None
1632 }};
1633}
1634
1635/// Generates the exhaustive numeric projection from the value type table.
1636macro_rules! value_number_ref_match {
1637 ($value:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal $(, $_wire:tt)*)),+ $(,)?) => {
1638 match &$value.repr {
1639 ValueRepr::Unset(_) => None,
1640 $(
1641 $(#[$cfg])*
1642 ValueRepr::$variant(value) => {
1643 project_number_ref!($number_projection, value)
1644 }
1645 )+
1646 }
1647 };
1648}
1649
1650impl Value {
1651 /// Tests whether this value is a concrete floating-point NaN.
1652 ///
1653 /// Non-floating-point values and unset values return `false`.
1654 ///
1655 /// # Returns
1656 ///
1657 /// `true` only for concrete `Float32` or `Float64` NaN values.
1658 #[inline(always)]
1659 #[must_use]
1660 pub fn is_nan(&self) -> bool {
1661 self.as_number_ref().is_some_and(|value| value.is_nan())
1662 }
1663
1664 /// Compares concrete numeric values across representation variants.
1665 ///
1666 /// This operation is separate from [`PartialEq`]: equality preserves enum
1667 /// representation identity, while numeric comparison compares mathematical
1668 /// values under an explicit policy.
1669 ///
1670 /// [`NumericComparisonPolicy::Approximate`] orders primitive infinities
1671 /// separately. When a finite primitive float participates, it attempts to
1672 /// project both operands to finite `f64` values; if either operand cannot
1673 /// be projected that way, comparison falls back to the exact path.
1674 /// Projected comparison is pair-dependent and not transitive across
1675 /// mixed representations. Do not use it to implement [`Ord`], sort or
1676 /// group values, or construct ordered-map or ordered-set keys. Use
1677 /// [`NumericComparisonPolicy::Exact`] for deterministic ordering.
1678 ///
1679 /// Validation is deterministic: missing operands are checked from left to
1680 /// right, followed by concrete operand types from left to right, and then
1681 /// NaN positions.
1682 ///
1683 /// # Parameters
1684 ///
1685 /// * `other` - Right numeric operand.
1686 /// * `policy` - Exact or approximate numeric comparison policy.
1687 ///
1688 /// # Returns
1689 ///
1690 /// The mathematical ordering of the two concrete, non-NaN numeric
1691 /// operands.
1692 ///
1693 /// # Errors
1694 ///
1695 /// Returns [`NumericComparisonError::LeftMissing`] or
1696 /// [`NumericComparisonError::RightMissing`] when the corresponding operand
1697 /// is unset. Returns [`NumericComparisonError::LeftNotNumeric`] or
1698 /// [`NumericComparisonError::RightNotNumeric`] when the corresponding
1699 /// concrete operand is not numeric. Returns
1700 /// [`NumericComparisonError::LeftNaN`],
1701 /// [`NumericComparisonError::RightNaN`], or
1702 /// [`NumericComparisonError::BothNaN`] according to the position of NaN
1703 /// operands. Missing operands are checked left-to-right, then concrete
1704 /// operand types are checked left-to-right, and finally NaN positions are
1705 /// classified. After these checks the lower-level comparator must be able
1706 /// to order the remaining numeric operands.
1707 pub fn numeric_cmp(
1708 &self,
1709 other: &Self,
1710 policy: NumericComparisonPolicy,
1711 ) -> Result<Ordering, NumericComparisonError> {
1712 if let ValueRepr::Unset(declared) = &self.repr {
1713 return Err(NumericComparisonError::LeftMissing { declared: *declared });
1714 }
1715 if let ValueRepr::Unset(declared) = &other.repr {
1716 return Err(NumericComparisonError::RightMissing { declared: *declared });
1717 }
1718
1719 let left = self
1720 .as_number_ref()
1721 .ok_or_else(|| NumericComparisonError::LeftNotNumeric {
1722 actual: self.data_type(),
1723 })?;
1724 let right = other
1725 .as_number_ref()
1726 .ok_or_else(|| NumericComparisonError::RightNotNumeric {
1727 actual: other.data_type(),
1728 })?;
1729
1730 match (left.is_nan(), right.is_nan()) {
1731 (true, true) => return Err(NumericComparisonError::BothNaN),
1732 (true, false) => return Err(NumericComparisonError::LeftNaN),
1733 (false, true) => return Err(NumericComparisonError::RightNaN),
1734 (false, false) => {}
1735 }
1736
1737 match left.compare(right, policy) {
1738 Some(ordering) => Ok(ordering),
1739 None => unreachable!("validated non-NaN numeric values must be orderable"),
1740 }
1741 }
1742
1743 /// Borrows this value as a lower-level numeric representation.
1744 ///
1745 /// # Returns
1746 ///
1747 /// A borrowed numeric representation for every concrete numeric variant,
1748 /// or `None` for unset and non-numeric variants.
1749 #[must_use]
1750 fn as_number_ref(&self) -> Option<NumberRef<'_>> {
1751 for_each_value_type!(value_number_ref_match, self)
1752 }
1753}