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