Skip to main content

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