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() => {
576                Ok(default())
577            }
578            result => result,
579        }
580    }
581
582    /// Converts the stored value to another supported data type.
583    ///
584    /// This method delegates to the authoritative conversion contract in
585    /// [`qubit-datatype`](https://docs.rs/qubit-datatype/latest/qubit_datatype/).
586    /// The enabled rich-type features determine which source and target
587    /// families are available. An unset value is reported as a structured
588    /// missing-value conversion error.
589    ///
590    /// Unlike [`Self::get`], this method permits conversions supported by
591    /// [`qubit_datatype::DataConverter`] and applies
592    /// [`qubit_datatype::ConversionPolicy`] and
593    /// [`qubit_datatype::ConversionLimits`].
594    ///
595    /// # Type Parameters
596    ///
597    /// * `T` - Target type supported by the shared conversion layer.
598    ///
599    /// # Returns
600    ///
601    /// The converted value.
602    ///
603    /// # Errors
604    ///
605    /// Returns a mapped conversion error when the value is unset, the
606    /// conversion is unsupported, or the source is invalid for `T`.
607    ///
608    /// # Examples
609    ///
610    /// ```rust
611    /// use qubit_value::Value;
612    ///
613    /// let value = Value::Int32(42);
614    /// assert_eq!(value.to::<i64>().unwrap(), 42);
615    /// assert_eq!(value.to::<String>().unwrap(), "42");
616    /// ```
617    #[inline(always)]
618    #[cfg(feature = "converter")]
619    #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
620    pub fn to<T>(&self) -> ValueResult<T>
621    where
622        T: DataConversionTarget,
623    {
624        self.to_with(
625            ConversionPolicy::default_ref(),
626            ConversionLimits::default_ref(),
627        )
628    }
629
630    /// Converts this value to `T`, or returns `default` when storage is unset
631    /// or conversion reports a missing value.
632    ///
633    /// Conversion failures from concrete values are preserved.
634    ///
635    /// # Type Parameters
636    ///
637    /// * `T` - Target conversion type.
638    ///
639    /// # Parameters
640    ///
641    /// * `default` - Lazily materialized value used for unset or conversion-
642    ///   missing storage.
643    ///
644    /// # Returns
645    ///
646    /// The converted value, or `default` for an unset or conversion-missing
647    /// value.
648    ///
649    /// # Errors
650    ///
651    /// Returns a mapped conversion error for concrete values that cannot be
652    /// converted to `T`.
653    #[inline]
654    #[cfg(feature = "converter")]
655    #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
656    pub fn to_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
657    where
658        T: DataConversionTarget,
659    {
660        match self.to() {
661            Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => {
662                Ok(default.into_value_default())
663            }
664            result => result,
665        }
666    }
667
668    /// Converts this value to `T`, or calls `default` when storage is unset or
669    /// conversion reports a missing value.
670    ///
671    /// # Type Parameters
672    ///
673    /// * `T` - Target conversion type.
674    /// * `F` - Deferred fallback producing `T`.
675    ///
676    /// # Parameters
677    ///
678    /// * `default` - Callback invoked only when conversion reports a missing
679    ///   value.
680    ///
681    /// # Returns
682    ///
683    /// The converted value, or the callback result for an unset or
684    /// conversion-missing value.
685    ///
686    /// # Errors
687    ///
688    /// Preserves conversion errors from concrete values without invoking the
689    /// callback.
690    #[inline]
691    #[cfg(feature = "converter")]
692    #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
693    pub fn to_or_else<T, F>(&self, default: F) -> ValueResult<T>
694    where
695        T: DataConversionTarget,
696        F: FnOnce() -> T,
697    {
698        match self.to() {
699            Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => {
700                Ok(default())
701            }
702            result => result,
703        }
704    }
705
706    /// Converts this value to `T` using the provided conversion policy and
707    /// limits.
708    ///
709    /// This method uses the shared [`qubit_datatype`] conversion layer
710    /// directly, so policy settings such as string trimming, blank string
711    /// handling, and boolean aliases are applied consistently with other
712    /// value containers.
713    ///
714    /// # Type Parameters
715    ///
716    /// * `T` - The target type to convert to.
717    ///
718    /// # Parameters
719    ///
720    /// * `policy` - Conversion policy forwarded to the shared converter.
721    /// * `limits` - Conversion limits forwarded to the shared converter.
722    ///
723    /// # Returns
724    ///
725    /// Returns the converted value on success.
726    ///
727    /// # Errors
728    ///
729    /// Returns a [`crate::ValueError`] when the value is missing, unsupported,
730    /// or invalid for `T` under the provided policy and limits.
731    #[inline(always)]
732    #[cfg(feature = "converter")]
733    #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
734    pub fn to_with<T>(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<T>
735    where
736        T: DataConversionTarget,
737    {
738        super::value_converters::convert_with_data_converter_with(self, policy, limits)
739    }
740
741    /// Converts this value to `T` while charging an existing conversion
742    /// session.
743    ///
744    /// # Type Parameters
745    ///
746    /// * `T` - Target type supported by the shared conversion layer.
747    ///
748    /// # Parameters
749    ///
750    /// * `session` - Caller-owned session providing policy, limits, and budget.
751    ///
752    /// # Returns
753    ///
754    /// The converted value.
755    ///
756    /// # Errors
757    ///
758    /// Returns a mapped conversion error when the value is missing,
759    /// unsupported, invalid, or exceeds the session budget.
760    #[inline(always)]
761    #[cfg(feature = "converter")]
762    #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
763    pub fn to_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<T>
764    where
765        T: DataConversionTarget,
766    {
767        super::value_converters::convert_with_data_converter_in(self, session)
768    }
769
770    /// Converts this value to `T` using conversion policy and limits, or
771    /// returns `default` when storage is unset or conversion reports a
772    /// missing value.
773    ///
774    /// Conversion failures from concrete values are preserved.
775    ///
776    /// # Type Parameters
777    ///
778    /// * `T` - Target conversion type.
779    ///
780    /// # Parameters
781    ///
782    /// * `default` - Lazily materialized value used for unset or conversion-
783    ///   missing storage.
784    /// * `policy` - Conversion policy forwarded to the shared converter.
785    /// * `limits` - Conversion limits forwarded to the shared converter.
786    ///
787    /// # Returns
788    ///
789    /// The converted value, or `default` for an unset or conversion-missing
790    /// value.
791    ///
792    /// # Errors
793    ///
794    /// Returns a mapped conversion error for concrete values that cannot be
795    /// converted under the provided policy and limits.
796    #[inline]
797    #[cfg(feature = "converter")]
798    #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
799    pub fn to_or_with<T>(
800        &self,
801        default: impl IntoValueDefault<T>,
802        policy: &ConversionPolicy,
803        limits: &ConversionLimits,
804    ) -> ValueResult<T>
805    where
806        T: DataConversionTarget,
807    {
808        match self.to_with(policy, limits) {
809            Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => {
810                Ok(default.into_value_default())
811            }
812            result => result,
813        }
814    }
815
816    /// Converts this value with the provided policy and limits, or calls
817    /// `default` when storage is unset or conversion reports a missing
818    /// value.
819    ///
820    /// # Type Parameters
821    ///
822    /// * `T` - Target conversion type.
823    /// * `F` - Deferred fallback producing `T`.
824    ///
825    /// # Parameters
826    ///
827    /// * `default` - Callback invoked only for a missing source value.
828    /// * `policy` - Conversion policy forwarded to the shared converter.
829    /// * `limits` - Conversion limits forwarded to the shared converter.
830    ///
831    /// # Returns
832    ///
833    /// The converted value, or the callback result for an unset or
834    /// conversion-missing value.
835    ///
836    /// # Errors
837    ///
838    /// Preserves concrete-value conversion errors without invoking the
839    /// callback.
840    #[inline]
841    #[cfg(feature = "converter")]
842    #[cfg_attr(docsrs, doc(cfg(feature = "converter")))]
843    pub fn to_or_else_with<T, F>(
844        &self,
845        default: F,
846        policy: &ConversionPolicy,
847        limits: &ConversionLimits,
848    ) -> ValueResult<T>
849    where
850        T: DataConversionTarget,
851        F: FnOnce() -> T,
852    {
853        match self.to_with(policy, limits) {
854            Err(ValueError::Missing(missing)) if missing.is_defaultable_for_conversion() => {
855                Ok(default())
856            }
857            result => result,
858        }
859    }
860
861    /// Get the data type of the value
862    ///
863    /// # Returns
864    ///
865    /// Returns the data type corresponding to this value
866    ///
867    /// # Examples
868    ///
869    /// ```rust
870    /// use qubit_datatype::DataType;
871    /// use qubit_value::Value;
872    ///
873    /// let value = Value::Int32(42);
874    /// assert_eq!(value.data_type(), DataType::Int32);
875    ///
876    /// let empty = Value::Unset(DataType::String);
877    /// assert_eq!(empty.data_type(), DataType::String);
878    /// ```
879    ///
880    /// ```compile_fail
881    /// #![deny(unused_must_use)]
882    /// use qubit_value::Value;
883    ///
884    /// Value::new(42_i32).data_type();
885    /// ```
886    #[inline(always)]
887    pub fn data_type(&self) -> DataType {
888        for_each_value_type!(value_data_type_match, self)
889    }
890
891    /// Tests whether this container has no concrete value.
892    ///
893    /// # Returns
894    ///
895    /// Returns `true` only for [`Value::Unset`]. An empty string, map, or JSON
896    /// container is still a concrete value and returns `false`.
897    ///
898    /// # Examples
899    ///
900    /// ```rust
901    /// use qubit_datatype::DataType;
902    /// use qubit_value::Value;
903    ///
904    /// let value = Value::Int32(42);
905    /// assert!(!value.is_unset());
906    ///
907    /// let empty = Value::Unset(DataType::String);
908    /// assert!(empty.is_unset());
909    /// ```
910    #[inline(always)]
911    #[must_use]
912    pub fn is_unset(&self) -> bool {
913        matches!(self.repr, ValueRepr::Unset(_))
914    }
915
916    /// Tests whether a concrete value belongs to the numeric type family.
917    ///
918    /// An unset value returns `false`, even when its declared type is numeric.
919    ///
920    /// # Returns
921    ///
922    /// `true` for concrete numeric variants; otherwise `false`.
923    #[inline]
924    #[must_use]
925    pub fn is_numeric(&self) -> bool {
926        !self.is_unset() && self.data_type().is_numeric()
927    }
928
929    /// Generic setter method
930    ///
931    /// Replaces the current value with any supported input value.
932    ///
933    /// This operation updates the stored type to `T` when needed. It does not
934    /// perform runtime type-mismatch validation against the previous variant.
935    ///
936    /// # Supported Generic Types
937    ///
938    /// `Value::set<T>(value)` currently supports the following `T`:
939    ///
940    /// - `bool`
941    /// - `char`
942    /// - `i8`, `i16`, `i32`, `i64`, `i128`
943    /// - `u8`, `u16`, `u32`, `u64`, `u128`
944    /// - `f32`, `f64`
945    /// - `String`, `&str`
946    /// - `NaiveDate`, `NaiveTime`, `NaiveDateTime`, `DateTime<Utc>`
947    /// - `BigInt`, `BigDecimal`
948    /// - `Duration`
949    /// - `Url`
950    /// - `HashMap<String, String>`
951    /// - `serde_json::Value`
952    ///
953    /// # Type Parameters
954    ///
955    /// * `T` - Input type convertible into [`Value`].
956    ///
957    /// # Parameters
958    ///
959    /// * `value` - The value to set
960    ///
961    /// # Compile-time restriction
962    ///
963    /// Unsupported input types fail to compile because they do not implement
964    /// `Into<Value>`.
965    ///
966    /// # Examples
967    ///
968    /// ```rust
969    /// use qubit_datatype::DataType;
970    /// use qubit_value::Value;
971    ///
972    /// let mut value = Value::Unset(DataType::Int32);
973    ///
974    /// // Through type inference
975    /// value.set(42i32);
976    /// assert_eq!(value.get_int32().unwrap(), 42);
977    ///
978    /// // Explicitly specify type parameter
979    /// value.set::<i32>(100);
980    /// assert_eq!(value.get_int32().unwrap(), 100);
981    ///
982    /// // String type
983    /// let mut text = Value::Unset(DataType::String);
984    /// text.set("hello".to_string());
985    /// assert_eq!(text.get_string().unwrap(), "hello");
986    /// ```
987    #[inline(always)]
988    pub fn set<T>(&mut self, value: T)
989    where
990        T: Into<Self>,
991    {
992        *self = value.into();
993    }
994
995    /// Set the data type
996    ///
997    /// If the new type differs from the current type, clears the value
998    /// and sets the new type.
999    ///
1000    /// # Parameters
1001    ///
1002    /// * `data_type` - The data type to set
1003    ///
1004    /// # Examples
1005    ///
1006    /// ```rust
1007    /// use qubit_datatype::DataType;
1008    /// use qubit_value::Value;
1009    ///
1010    /// let mut value = Value::Int32(42);
1011    /// value.set_type(DataType::String);
1012    /// assert!(value.is_unset());
1013    /// assert_eq!(value.data_type(), DataType::String);
1014    /// ```
1015    #[inline]
1016    pub fn set_type(&mut self, data_type: DataType) {
1017        if self.data_type() != data_type {
1018            *self = Value::new_unset(data_type);
1019        }
1020    }
1021
1022    /// Removes the concrete value while preserving its declared data type.
1023    #[inline]
1024    pub fn unset(&mut self) {
1025        *self = Value::new_unset(self.data_type());
1026    }
1027}
1028
1029#[cfg(all(feature = "converter", feature = "json"))]
1030#[cfg_attr(docsrs, doc(cfg(all(feature = "converter", feature = "json"))))]
1031impl Value {
1032    /// Projects this typed value to its natural JSON representation.
1033    ///
1034    /// This differs from the tagged [`crate::ValueWireV1`] representation: for
1035    /// example, `Value::Int32(42)` projects to the JSON number `42`.
1036    ///
1037    /// # Returns
1038    ///
1039    /// The natural JSON representation of this value.
1040    ///
1041    /// For natural JSON projection, `max_text_bytes` bounds each string value
1042    /// and each formatted number or rich-value text by its UTF-8 byte length,
1043    /// including strings inside materialized JSON. Object keys are excluded
1044    /// from this per-item bound but still consume applicable key and
1045    /// cumulative input, output, and payload budgets. This is neither a
1046    /// cumulative text limit nor a limit on escaped JSON encoding size.
1047    ///
1048    /// # Errors
1049    ///
1050    /// Returns a structured conversion error for values JSON cannot represent,
1051    /// including non-finite floating-point values and inexact durations.
1052    #[inline]
1053    pub fn to_json_value(&self) -> ValueResult<JsonValue> {
1054        self.to_json_value_with(
1055            ConversionPolicy::default_ref(),
1056            ConversionLimits::default_ref(),
1057        )
1058    }
1059
1060    /// Projects this typed value using explicit conversion policy and limits.
1061    ///
1062    /// # Parameters
1063    ///
1064    /// * `policy` - Controls duration units and precision-loss behavior.
1065    /// * `limits` - Bounds conversion resource consumption.
1066    ///
1067    /// # Returns
1068    ///
1069    /// The natural JSON representation of this value.
1070    ///
1071    /// # Errors
1072    ///
1073    /// Returns [`ValueError::JsonProjectionLimit`] when a projection resource
1074    /// limit is exceeded, or a structured conversion error when JSON
1075    /// projection or duration formatting violates the requested policy.
1076    #[inline(always)]
1077    pub fn to_json_value_with(
1078        &self,
1079        policy: &ConversionPolicy,
1080        limits: &ConversionLimits,
1081    ) -> ValueResult<JsonValue> {
1082        crate::json::value_to_json_value_with(self, policy, limits)
1083    }
1084}
1085
1086/// Implements one strict typed getter from the shared value table.
1087macro_rules! impl_get_value {
1088    // Copy type: directly dereference and return
1089    ($(#[$attr:meta])* copy: $method:ident, $variant:ident, $type:ty, $data_type:expr) => {
1090        $(#[$attr])*
1091        #[doc = ""]
1092        #[doc = "# Errors"]
1093        #[doc = ""]
1094        #[doc = "Returns [`ValueError::Missing`] when the value is unset with"]
1095        #[doc = "the requested type, or [`ValueError::TypeMismatch`] when the"]
1096        #[doc = "stored data type differs."]
1097        #[inline(always)]
1098        pub fn $method(&self) -> ValueResult<$type> {
1099            match &self.repr {
1100                ValueRepr::$variant(v) => Ok(*v),
1101                ValueRepr::Unset(dt) if *dt == $data_type => {
1102                    Err(ValueError::Missing($crate::ValueMissing::unset_scalar(*dt, *dt)))
1103                }
1104                ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1105                    expected: $data_type,
1106                    actual: *dt,
1107                }),
1108                _ => Err(ValueError::TypeMismatch {
1109                    expected: $data_type,
1110                    actual: self.data_type(),
1111                }),
1112            }
1113        }
1114    };
1115
1116    // Reference type: use conversion function to return reference,
1117    // fixing lifetime issues
1118    ($(#[$attr:meta])* ref: $method:ident, $variant:ident, $ret_type:ty, $data_type:expr, $conversion:expr) => {
1119        $(#[$attr])*
1120        #[doc = ""]
1121        #[doc = "# Errors"]
1122        #[doc = ""]
1123        #[doc = "Returns [`ValueError::Missing`] when the value is unset with"]
1124        #[doc = "the requested type, or [`ValueError::TypeMismatch`] when the"]
1125        #[doc = "stored data type differs."]
1126        #[inline(always)]
1127        pub fn $method(&self) -> ValueResult<$ret_type> {
1128            match &self.repr {
1129                ValueRepr::$variant(v) => {
1130                    let conv_fn: fn(&_) -> $ret_type = $conversion;
1131                    Ok(conv_fn(v))
1132                },
1133                ValueRepr::Unset(dt) if *dt == $data_type => {
1134                    Err(ValueError::Missing($crate::ValueMissing::unset_scalar(*dt, *dt)))
1135                }
1136                ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1137                    expected: $data_type,
1138                    actual: *dt,
1139                }),
1140                _ => Err(ValueError::TypeMismatch {
1141                    expected: $data_type,
1142                    actual: self.data_type(),
1143                }),
1144            }
1145        }
1146    };
1147}
1148
1149impl Value {
1150    /// Creates a `Value` from a `serde_json::Value`.
1151    ///
1152    /// # Parameters
1153    ///
1154    /// * `json` - The JSON value to wrap.
1155    ///
1156    /// # Returns
1157    ///
1158    /// A `Value::Json` wrapping the given JSON value.
1159    #[inline]
1160    #[cfg(feature = "json")]
1161    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
1162    pub fn from_json_value(json: JsonValue) -> Self {
1163        Value::Json(json)
1164    }
1165
1166    /// Creates a `Value` from any serializable value by converting it to JSON.
1167    ///
1168    /// # Type Parameters
1169    ///
1170    /// * `T` - Any type implementing `Serialize`.
1171    ///
1172    /// # Parameters
1173    ///
1174    /// * `value` - The value to serialize into JSON.
1175    ///
1176    /// # Returns
1177    ///
1178    /// A `Value::Json` containing the serialized representation.
1179    ///
1180    /// # Errors
1181    ///
1182    /// Returns [`ValueError::Conversion`] when serialization encounters a
1183    /// non-finite nested float, an integer outside the strict JSON range, or
1184    /// another unsupported Serde representation.
1185    ///
1186    /// # Resource limits
1187    ///
1188    /// This method validates the JSON representation but does not apply a
1189    /// finite resource budget. `JsonEncodeLimits` and `ConversionLimits` do
1190    /// not constrain this materialization; use `JsonValueEncoder` with a
1191    /// caller-owned `JsonValueBudget` when a bounded projection is required.
1192    #[cfg(all(feature = "converter", feature = "json"))]
1193    #[cfg_attr(docsrs, doc(cfg(all(feature = "converter", feature = "json"))))]
1194    pub fn from_serializable<T: ?Sized + Serialize>(value: &T) -> ValueResult<Self> {
1195        let json = JsonValueEncoder::new().encode(value).map_err(|error| {
1196            let reason = match error.kind() {
1197                JsonSerializationErrorKind::NonFiniteFloat => InvalidValueReason::NonFinite,
1198                JsonSerializationErrorKind::IntegerOutOfRange { .. } => {
1199                    InvalidValueReason::OutOfRange
1200                }
1201                _ => InvalidValueReason::Serialization {
1202                    format: DataFormat::Json,
1203                },
1204            };
1205            ValueError::from(DataConversionError::invalid(
1206                DataType::Json,
1207                DataType::Json,
1208                reason,
1209            ))
1210        })?;
1211        Ok(Value::Json(json))
1212    }
1213
1214    // ========================================================================
1215    // Type-checking getters (strict type matching)
1216    // ========================================================================
1217
1218    impl_get_value! {
1219        /// Get boolean value
1220        ///
1221        /// # Returns
1222        ///
1223        /// If types match, returns the boolean value; see `# Errors`.
1224        ///
1225        /// # Examples
1226        ///
1227        /// ```rust
1228        /// use qubit_value::Value;
1229        ///
1230        /// let value = Value::Bool(true);
1231        /// assert_eq!(value.get_bool().unwrap(), true);
1232        /// ```
1233        copy: get_bool, Bool, bool, DataType::Bool
1234    }
1235
1236    impl_get_value! {
1237        /// Get character value
1238        ///
1239        /// # Returns
1240        ///
1241        /// If types match, returns the character value; see `# Errors`.
1242        ///
1243        /// # Examples
1244        ///
1245        /// ```rust
1246        /// use qubit_value::Value;
1247        ///
1248        /// let value = Value::Char('A');
1249        /// assert_eq!(value.get_char().unwrap(), 'A');
1250        /// ```
1251        copy: get_char, Char, char, DataType::Char
1252    }
1253
1254    impl_get_value! {
1255        /// Get int8 value
1256        ///
1257        /// # Returns
1258        ///
1259        /// If types match, returns the int8 value; see `# Errors`.
1260        copy: get_int8, Int8, i8, DataType::Int8
1261    }
1262
1263    impl_get_value! {
1264        /// Get int16 value
1265        ///
1266        /// # Returns
1267        ///
1268        /// If types match, returns the int16 value; see `# Errors`.
1269        copy: get_int16, Int16, i16, DataType::Int16
1270    }
1271
1272    impl_get_value! {
1273        /// Get int32 value
1274        ///
1275        /// # Returns
1276        ///
1277        /// If types match, returns the int32 value; see `# Errors`.
1278        copy: get_int32, Int32, i32, DataType::Int32
1279    }
1280
1281    impl_get_value! {
1282        /// Get int64 value
1283        ///
1284        /// # Returns
1285        ///
1286        /// If types match, returns the int64 value; see `# Errors`.
1287        copy: get_int64, Int64, i64, DataType::Int64
1288    }
1289
1290    impl_get_value! {
1291        /// Get int128 value
1292        ///
1293        /// # Returns
1294        ///
1295        /// If types match, returns the int128 value; see `# Errors`.
1296        copy: get_int128, Int128, i128, DataType::Int128
1297    }
1298
1299    impl_get_value! {
1300        /// Get uint8 value
1301        ///
1302        /// # Returns
1303        ///
1304        /// If types match, returns the uint8 value; see `# Errors`.
1305        copy: get_uint8, UInt8, u8, DataType::UInt8
1306    }
1307
1308    impl_get_value! {
1309        /// Get uint16 value
1310        ///
1311        /// # Returns
1312        ///
1313        /// If types match, returns the uint16 value; see `# Errors`.
1314        copy: get_uint16, UInt16, u16, DataType::UInt16
1315    }
1316
1317    impl_get_value! {
1318        /// Get uint32 value
1319        ///
1320        /// # Returns
1321        ///
1322        /// If types match, returns the uint32 value; see `# Errors`.
1323        copy: get_uint32, UInt32, u32, DataType::UInt32
1324    }
1325
1326    impl_get_value! {
1327        /// Get uint64 value
1328        ///
1329        /// # Returns
1330        ///
1331        /// If types match, returns the uint64 value; see `# Errors`.
1332        copy: get_uint64, UInt64, u64, DataType::UInt64
1333    }
1334
1335    impl_get_value! {
1336        /// Get uint128 value
1337        ///
1338        /// # Returns
1339        ///
1340        /// If types match, returns the uint128 value; see `# Errors`.
1341        copy: get_uint128, UInt128, u128, DataType::UInt128
1342    }
1343
1344    impl_get_value! {
1345        /// Get float32 value
1346        ///
1347        /// # Returns
1348        ///
1349        /// If types match, returns the float32 value; see `# Errors`.
1350        copy: get_float32, Float32, f32, DataType::Float32
1351    }
1352
1353    impl_get_value! {
1354        /// Get float64 value
1355        ///
1356        /// # Returns
1357        ///
1358        /// If types match, returns the float64 value; see `# Errors`.
1359        copy: get_float64, Float64, f64, DataType::Float64
1360    }
1361
1362    impl_get_value! {
1363        /// Get string reference
1364        ///
1365        /// # Returns
1366        ///
1367        /// If types match, returns a reference to the string; see `# Errors`.
1368        ///
1369        /// # Examples
1370        ///
1371        /// ```rust
1372        /// use qubit_value::Value;
1373        ///
1374        /// let value = Value::String("hello".to_string());
1375        /// assert_eq!(value.get_string().unwrap(), "hello");
1376        /// ```
1377        ref: get_string, String, &str, DataType::String, |s: &String| s.as_str()
1378    }
1379
1380    #[cfg(feature = "chrono")]
1381    impl_get_value! {
1382        #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
1383        /// Get date value
1384        ///
1385        /// # Returns
1386        ///
1387        /// If types match, returns the date value; see `# Errors`.
1388        copy: get_date, Date, NaiveDate, DataType::Date
1389    }
1390
1391    #[cfg(feature = "chrono")]
1392    impl_get_value! {
1393        #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
1394        /// Get time value
1395        ///
1396        /// # Returns
1397        ///
1398        /// If types match, returns the time value; see `# Errors`.
1399        copy: get_time, Time, NaiveTime, DataType::Time
1400    }
1401
1402    #[cfg(feature = "chrono")]
1403    impl_get_value! {
1404        #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
1405        /// Get datetime value
1406        ///
1407        /// # Returns
1408        ///
1409        /// If types match, returns the datetime value; see `# Errors`.
1410        copy: get_datetime, DateTime, NaiveDateTime, DataType::DateTime
1411    }
1412
1413    #[cfg(feature = "chrono")]
1414    impl_get_value! {
1415        #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
1416        /// Get UTC instant value
1417        ///
1418        /// # Returns
1419        ///
1420        /// If types match, returns the UTC instant value; see `# Errors`.
1421        copy: get_instant, Instant, DateTime<Utc>, DataType::Instant
1422    }
1423
1424    #[cfg(feature = "big-integer")]
1425    impl_get_value! {
1426        #[cfg_attr(docsrs, doc(cfg(feature = "big-integer")))]
1427        /// Get big integer value.
1428        ///
1429        /// This method returns a cloned [`BigInt`]. Use
1430        /// [`Value::get_biginteger_ref`] to borrow the stored value without
1431        /// cloning.
1432        ///
1433        /// # Returns
1434        ///
1435        /// If types match, returns the big integer value; see `# Errors`.
1436        ///
1437        /// # Examples
1438        ///
1439        /// ```rust
1440        /// use qubit_value::Value;
1441        /// use num_bigint::BigInt;
1442        ///
1443        /// let value = Value::BigInteger(BigInt::from(123456789));
1444        /// assert_eq!(value.get_biginteger().unwrap(), BigInt::from(123456789));
1445        /// ```
1446        ref: get_biginteger, BigInteger, BigInt, DataType::BigInteger, |v: &BigInt| v.clone()
1447    }
1448
1449    #[cfg(feature = "big-decimal")]
1450    impl_get_value! {
1451        #[cfg_attr(docsrs, doc(cfg(feature = "big-decimal")))]
1452        /// Get big decimal value.
1453        ///
1454        /// This method returns a cloned [`BigDecimal`]. Use
1455        /// [`Value::get_bigdecimal_ref`] to borrow the stored value without
1456        /// cloning.
1457        ///
1458        /// # Returns
1459        ///
1460        /// If types match, returns the big decimal value; see `# Errors`.
1461        ///
1462        /// # Examples
1463        ///
1464        /// ```rust
1465        /// use std::str::FromStr;
1466        ///
1467        /// use bigdecimal::BigDecimal;
1468        /// use qubit_value::Value;
1469        ///
1470        /// let bd = BigDecimal::from_str("123.456").unwrap();
1471        /// let value = Value::BigDecimal(bd.clone());
1472        /// assert_eq!(value.get_bigdecimal().unwrap(), bd);
1473        /// ```
1474        ref: get_bigdecimal, BigDecimal, BigDecimal, DataType::BigDecimal, |v: &BigDecimal| v.clone()
1475    }
1476
1477    impl_get_value! {
1478        /// Get Duration value
1479        ///
1480        /// # Returns
1481        ///
1482        /// If types match, returns the Duration value; see `# Errors`.
1483        copy: get_duration, Duration, Duration, DataType::Duration
1484    }
1485
1486    #[cfg(feature = "url")]
1487    impl_get_value! {
1488        #[cfg_attr(docsrs, doc(cfg(feature = "url")))]
1489        /// Get URL value.
1490        ///
1491        /// This method returns a cloned [`Url`]. Use [`Value::get_url_ref`] to
1492        /// borrow the stored value without cloning.
1493        ///
1494        /// # Returns
1495        ///
1496        /// If types match, returns the URL value; see `# Errors`.
1497        ref: get_url, Url, Url, DataType::Url, Url::clone
1498    }
1499
1500    impl_get_value! {
1501        /// Get string map value.
1502        ///
1503        /// This method returns a cloned `HashMap<String, String>`. Use
1504        /// [`Value::get_string_map_ref`] to borrow the stored value without
1505        /// cloning.
1506        ///
1507        /// # Returns
1508        ///
1509        /// If types match, returns the string map value; see `# Errors`.
1510        ref: get_string_map, StringMap, HashMap<String, String>, DataType::StringMap,
1511            |v: &HashMap<String, String>| v.clone()
1512    }
1513
1514    #[cfg(feature = "json")]
1515    impl_get_value! {
1516        #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
1517        /// Get JSON value.
1518        ///
1519        /// This method returns a cloned [`serde_json::Value`]. Use
1520        /// [`Value::get_json_ref`] to borrow the stored value without cloning.
1521        ///
1522        /// # Returns
1523        ///
1524        /// If types match, returns the JSON value; see `# Errors`.
1525        ref: get_json, Json, JsonValue, DataType::Json,
1526            |v: &JsonValue| v.clone()
1527    }
1528
1529    /// Borrow the inner `BigInt` without cloning.
1530    ///
1531    /// # Returns
1532    ///
1533    /// A shared reference to the stored integer.
1534    ///
1535    /// # Errors
1536    ///
1537    /// Returns [`ValueError::Missing`] when the value is unset with
1538    /// `DataType::BigInteger`, or [`ValueError::TypeMismatch`] when the stored
1539    /// data type differs.
1540    #[cfg(feature = "big-integer")]
1541    #[inline]
1542    #[cfg_attr(docsrs, doc(cfg(feature = "big-integer")))]
1543    pub fn get_biginteger_ref(&self) -> ValueResult<&BigInt> {
1544        self.get_ref()
1545    }
1546
1547    /// Borrow the inner `BigDecimal` without cloning.
1548    ///
1549    /// # Returns
1550    ///
1551    /// A shared reference to the stored decimal.
1552    ///
1553    /// # Errors
1554    ///
1555    /// Returns [`ValueError::Missing`] when the value is unset with
1556    /// `DataType::BigDecimal`, or [`ValueError::TypeMismatch`] when the stored
1557    /// data type differs.
1558    #[cfg(feature = "big-decimal")]
1559    #[inline]
1560    #[cfg_attr(docsrs, doc(cfg(feature = "big-decimal")))]
1561    pub fn get_bigdecimal_ref(&self) -> ValueResult<&BigDecimal> {
1562        self.get_ref()
1563    }
1564
1565    /// Borrow the inner `Url` without cloning.
1566    ///
1567    /// # Returns
1568    ///
1569    /// A shared reference to the stored URL.
1570    ///
1571    /// # Errors
1572    ///
1573    /// Returns [`ValueError::Missing`] when the value is unset with
1574    /// `DataType::Url`, or [`ValueError::TypeMismatch`] when the stored data
1575    /// type differs.
1576    #[cfg(feature = "url")]
1577    #[inline]
1578    #[cfg_attr(docsrs, doc(cfg(feature = "url")))]
1579    pub fn get_url_ref(&self) -> ValueResult<&Url> {
1580        self.get_ref()
1581    }
1582
1583    /// Borrow the inner `HashMap<String, String>` without cloning.
1584    ///
1585    /// # Returns
1586    ///
1587    /// A shared reference to the stored string map.
1588    ///
1589    /// # Errors
1590    ///
1591    /// Returns [`ValueError::Missing`] when the value is unset with
1592    /// `DataType::StringMap`, or [`ValueError::TypeMismatch`] when the stored
1593    /// data type differs.
1594    #[inline(always)]
1595    pub fn get_string_map_ref(&self) -> ValueResult<&HashMap<String, String>> {
1596        self.get_ref()
1597    }
1598
1599    /// Borrow the inner JSON value without cloning.
1600    ///
1601    /// # Returns
1602    ///
1603    /// A shared reference to the stored JSON value.
1604    ///
1605    /// # Errors
1606    ///
1607    /// Returns [`ValueError::Missing`] when the value is unset with
1608    /// `DataType::Json`, or [`ValueError::TypeMismatch`] when the stored data
1609    /// type differs.
1610    #[cfg(feature = "json")]
1611    #[inline]
1612    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
1613    pub fn get_json_ref(&self) -> ValueResult<&JsonValue> {
1614        self.get_ref()
1615    }
1616
1617    /// Deserialize the inner JSON value into a target type.
1618    ///
1619    /// Only works when `self` is `Value::Json(...)`.
1620    ///
1621    /// # Type Parameters
1622    ///
1623    /// * `T` - The target type implementing `DeserializeOwned`.
1624    ///
1625    /// # Returns
1626    ///
1627    /// Returns `Ok(T)` on success.
1628    ///
1629    /// # Errors
1630    ///
1631    /// Returns [`ValueError::Missing`] when this value is
1632    /// `Value::Unset(DataType::Json)`,
1633    /// [`ValueError::TypeMismatch`] when this value has a non-JSON data type,
1634    /// or [`ValueError::Conversion`] when JSON deserialization fails.
1635    ///
1636    /// # Resource limits
1637    ///
1638    /// This method deserializes an already materialized JSON value. It does
1639    /// not apply `ConversionLimits`, a `ConversionSession`, or a JSON decode
1640    /// budget to target deserialization. Earlier bounded decoding does not
1641    /// bound allocations, recursion, or custom `Deserialize` work performed
1642    /// here. Keep input structure bounded and evaluate the target
1643    /// deserializer's resource behavior before using this method with
1644    /// untrusted data.
1645    #[cfg(all(feature = "converter", feature = "json"))]
1646    #[cfg_attr(docsrs, doc(cfg(all(feature = "converter", feature = "json"))))]
1647    pub fn deserialize_json<T: DeserializeOwned>(&self) -> ValueResult<T> {
1648        match &self.repr {
1649            ValueRepr::Json(v) => Deserialize::deserialize(v).map_err(|_| {
1650                ValueError::from(DataConversionError::invalid(
1651                    DataType::Json,
1652                    DataType::Json,
1653                    InvalidValueReason::Deserialization {
1654                        format: DataFormat::Json,
1655                    },
1656                ))
1657            }),
1658            ValueRepr::Unset(dt) if *dt == DataType::Json => {
1659                Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
1660            }
1661            ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
1662                expected: DataType::Json,
1663                actual: *dt,
1664            }),
1665            _ => Err(ValueError::TypeMismatch {
1666                expected: DataType::Json,
1667                actual: self.data_type(),
1668            }),
1669        }
1670    }
1671}
1672
1673/// Projects one stored value according to its type-table numeric strategy.
1674macro_rules! project_number_ref {
1675    (number_copy, $value:expr) => {
1676        Some(NumberRef::from(*$value))
1677    };
1678    (number_ref, $value:expr) => {
1679        Some(NumberRef::from($value))
1680    };
1681    (not_number, $value:expr) => {{
1682        let _ = $value;
1683        None
1684    }};
1685}
1686
1687/// Generates the exhaustive numeric projection from the value type table.
1688macro_rules! value_number_ref_match {
1689    ($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)*)),+ $(,)?) => {
1690        match &$value.repr {
1691            ValueRepr::Unset(_) => None,
1692            $(
1693                $(#[$cfg])*
1694                ValueRepr::$variant(value) => {
1695                    project_number_ref!($number_projection, value)
1696                }
1697            )+
1698        }
1699    };
1700}
1701
1702impl Value {
1703    /// Tests whether this value is a concrete floating-point NaN.
1704    ///
1705    /// Non-floating-point values and unset values return `false`.
1706    ///
1707    /// # Returns
1708    ///
1709    /// `true` only for concrete `Float32` or `Float64` NaN values.
1710    #[inline]
1711    #[must_use]
1712    pub fn is_nan(&self) -> bool {
1713        self.as_number_ref().is_some_and(|value| value.is_nan())
1714    }
1715
1716    /// Compares concrete numeric values across representation variants.
1717    ///
1718    /// This operation is separate from [`PartialEq`]: equality preserves enum
1719    /// representation identity, while numeric comparison compares mathematical
1720    /// values under an explicit policy.
1721    ///
1722    /// [`NumericComparisonPolicy::Approximate`] orders primitive infinities
1723    /// separately. When a finite primitive float participates, it attempts to
1724    /// project both operands to finite `f64` values; if either operand cannot
1725    /// be projected that way, comparison falls back to the exact path.
1726    /// Projected comparison is pair-dependent and not transitive across
1727    /// mixed representations. Do not use it to implement [`Ord`], sort or
1728    /// group values, or construct ordered-map or ordered-set keys. Use
1729    /// [`NumericComparisonPolicy::Exact`] for deterministic ordering.
1730    ///
1731    /// Validation is deterministic: missing operands are checked from left to
1732    /// right, followed by concrete operand types from left to right, and then
1733    /// NaN positions.
1734    ///
1735    /// Conversion limits and session budgets do not apply to this independent
1736    /// comparison. Fixed-width comparisons do not require heap allocation;
1737    /// some paths involving large numbers may clone coefficients or construct
1738    /// exact rational or decimal values, with cost depending on the operand
1739    /// representations. Constrain untrusted coefficient sizes and scales at
1740    /// the input boundary, and consider cumulative cost in batch filters.
1741    /// See the [lower-level comparison contract](https://docs.rs/qubit-datatype/latest/qubit_datatype/numeric/struct.NumberRef.html#method.compare).
1742    ///
1743    /// # Parameters
1744    ///
1745    /// * `other` - Right numeric operand.
1746    /// * `policy` - Exact or approximate numeric comparison policy.
1747    ///
1748    /// # Returns
1749    ///
1750    /// The mathematical ordering of the two concrete, non-NaN numeric
1751    /// operands.
1752    ///
1753    /// # Errors
1754    ///
1755    /// Returns [`NumericComparisonError::LeftMissing`] or
1756    /// [`NumericComparisonError::RightMissing`] when the corresponding operand
1757    /// is unset. Returns [`NumericComparisonError::LeftNotNumeric`] or
1758    /// [`NumericComparisonError::RightNotNumeric`] when the corresponding
1759    /// concrete operand is not numeric. Returns
1760    /// [`NumericComparisonError::LeftNaN`],
1761    /// [`NumericComparisonError::RightNaN`], or
1762    /// [`NumericComparisonError::BothNaN`] according to the position of NaN
1763    /// operands. Missing operands are checked left-to-right, then concrete
1764    /// operand types are checked left-to-right, and finally NaN positions are
1765    /// classified. After these checks the lower-level comparator must be able
1766    /// to order the remaining numeric operands.
1767    pub fn numeric_cmp(
1768        &self,
1769        other: &Self,
1770        policy: NumericComparisonPolicy,
1771    ) -> Result<Ordering, NumericComparisonError> {
1772        if let ValueRepr::Unset(declared) = &self.repr {
1773            return Err(NumericComparisonError::LeftMissing {
1774                declared: *declared,
1775            });
1776        }
1777        if let ValueRepr::Unset(declared) = &other.repr {
1778            return Err(NumericComparisonError::RightMissing {
1779                declared: *declared,
1780            });
1781        }
1782
1783        let left = self
1784            .as_number_ref()
1785            .ok_or_else(|| NumericComparisonError::LeftNotNumeric {
1786                actual: self.data_type(),
1787            })?;
1788        let right =
1789            other
1790                .as_number_ref()
1791                .ok_or_else(|| NumericComparisonError::RightNotNumeric {
1792                    actual: other.data_type(),
1793                })?;
1794
1795        match (left.is_nan(), right.is_nan()) {
1796            (true, true) => return Err(NumericComparisonError::BothNaN),
1797            (true, false) => return Err(NumericComparisonError::LeftNaN),
1798            (false, true) => return Err(NumericComparisonError::RightNaN),
1799            (false, false) => {}
1800        }
1801
1802        match left.compare(right, policy) {
1803            Some(ordering) => Ok(ordering),
1804            None => unreachable!("validated non-NaN numeric values must be orderable"),
1805        }
1806    }
1807
1808    /// Borrows this value as a lower-level numeric representation.
1809    ///
1810    /// # Returns
1811    ///
1812    /// A borrowed numeric representation for every concrete numeric variant,
1813    /// or `None` for unset and non-numeric variants.
1814    #[must_use = "the numeric projection should be inspected"]
1815    #[inline]
1816    fn as_number_ref(&self) -> Option<NumberRef<'_>> {
1817        for_each_value_type!(value_number_ref_match, self)
1818    }
1819}