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 qubit_datatype::DataType;
14#[cfg(feature = "converter")]
15use qubit_datatype::{
16    DataConversionOptions,
17    DataConversionTarget,
18};
19use std::fmt;
20
21use crate::value_error::ValueResult;
22use crate::{
23    IntoValueDefault,
24    ValueError,
25};
26
27use super::value_ref::ValueRef;
28
29/// Defines the private storage representation for the public single-value
30/// container from the shared value-type table.
31macro_rules! define_value_enum {
32    (
33        ;
34        $(
35            (
36                [$($cfg:meta),*],
37                $variant:ident,
38                $type:ty,
39                $data_type:expr,
40                $materialization:ident,
41                $json_class:ident,
42                $number_projection:ident,
43                $value_doc:literal,
44                $multi_doc:literal
45            )
46        ),+ $(,)?
47    ) => {
48        /// Internal single-value representation.
49        ///
50        /// Uses an enum to represent different types of values, providing
51        /// type-safe value storage and access.
52        ///
53        /// This representation is private; downstream code uses [`Value`]
54        /// constructors and [`ValueRef`] semantic views instead of matching
55        /// storage details.
56        ///
57        /// # Behavior
58        ///
59        /// - Stores one value from the closed [`DataType`] family.
60        /// - Provides strict getters and, with `converter`, option-controlled
61        ///   conversion methods.
62        /// - Distinguishes an unset container from concrete inner values.
63        /// - The URL variant uses boxed storage internally to keep the enum
64        ///   compact; use [`Value::new`] and typed getters instead of relying
65        ///   on the storage representation of individual variants.
66        ///
67        /// # Equality and hashing
68        ///
69        /// Equality preserves enum-variant identity. Signed zero is canonicalized,
70        /// every NaN payload within one float width is equal, and unordered payloads
71        /// hash structurally. Standard hash output is suitable for in-memory keys but
72        /// is not a stable persistent fingerprint.
73        ///
74        /// # Examples
75        ///
76        /// ```rust
77        /// use qubit_value::Value;
78        ///
79        /// let value = Value::Int32(42);
80        /// assert_eq!(value.get_int32().unwrap(), 42);
81        ///
82        /// let number: i32 = value.get().unwrap();
83        /// assert_eq!(number, 42);
84        ///
85        /// let text = Value::String("hello".to_string());
86        /// assert_eq!(text.get_string().unwrap(), "hello");
87        /// ```
88        #[derive(Debug, Clone)]
89        pub(crate) enum ValueRepr {
90            /// Unset value with a declared data type.
91            Unset(
92                /// Declared data type retained while the value is unset.
93                DataType,
94            ),
95            $(
96                $(#[$cfg])*
97                #[doc = $value_doc]
98                $variant(
99                    #[doc = concat!("Stored ", $value_doc, " payload.")]
100                    value_storage_type!($variant, $type),
101                ),
102            )+
103        }
104    };
105}
106
107for_each_value_type!(define_value_enum);
108
109/// Single typed runtime value with private storage representation.
110///
111/// Construction and access are expressed through methods and conversions. The
112/// concrete enum representation is private so storage optimizations do not
113/// become part of the public API.
114#[must_use]
115#[derive(Clone)]
116pub struct Value {
117    pub(crate) repr: ValueRepr,
118}
119
120impl fmt::Debug for Value {
121    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
122        self.view().fmt(formatter)
123    }
124}
125
126macro_rules! impl_value_constructors {
127    (
128        ;
129        $(
130            (
131                [$($cfg:meta),*],
132                $variant:ident,
133                $type:ty,
134                $data_type:expr,
135                $materialization:ident,
136                $json_class:ident,
137                $number_projection:ident,
138                $value_doc:literal,
139                $multi_doc:literal
140            )
141        ),+ $(,)?
142    ) => {
143        impl Value {
144            /// Creates an unset value with an explicit declared type.
145            #[allow(non_snake_case)]
146            #[inline(always)]
147            pub const fn Unset(data_type: DataType) -> Self {
148                Self::new_unset(data_type)
149            }
150
151            /// Creates an unset value with an explicit declared type.
152            #[inline(always)]
153            pub const fn new_unset(data_type: DataType) -> Self {
154                Self { repr: ValueRepr::Unset(data_type) }
155            }
156
157            $(
158                $(#[$cfg])*
159                #[allow(non_snake_case)]
160                #[doc = concat!("Creates a ", $value_doc, ".")]
161                #[inline(always)]
162                pub fn $variant(value: $type) -> Self {
163                    Self { repr: ValueRepr::$variant(value_storage_new!($variant, value)) }
164                }
165            )+
166        }
167    };
168}
169
170for_each_value_type!(impl_value_constructors);
171
172impl Value {
173    /// Borrows the stable semantic view of this value.
174    #[inline(always)]
175    pub fn view(&self) -> ValueRef<'_> {
176        match &self.repr {
177            ValueRepr::Unset(data_type) => ValueRef::Unset(*data_type),
178            ValueRepr::Bool(value) => ValueRef::Bool(*value),
179            ValueRepr::Char(value) => ValueRef::Char(*value),
180            ValueRepr::Int8(value) => ValueRef::Int8(*value),
181            ValueRepr::Int16(value) => ValueRef::Int16(*value),
182            ValueRepr::Int32(value) => ValueRef::Int32(*value),
183            ValueRepr::Int64(value) => ValueRef::Int64(*value),
184            ValueRepr::Int128(value) => ValueRef::Int128(*value),
185            ValueRepr::UInt8(value) => ValueRef::UInt8(*value),
186            ValueRepr::UInt16(value) => ValueRef::UInt16(*value),
187            ValueRepr::UInt32(value) => ValueRef::UInt32(*value),
188            ValueRepr::UInt64(value) => ValueRef::UInt64(*value),
189            ValueRepr::UInt128(value) => ValueRef::UInt128(*value),
190            ValueRepr::Float32(value) => ValueRef::Float32(*value),
191            ValueRepr::Float64(value) => ValueRef::Float64(*value),
192            #[cfg(feature = "big-integer")]
193            ValueRepr::BigInteger(value) => ValueRef::BigInteger(value),
194            #[cfg(feature = "big-decimal")]
195            ValueRepr::BigDecimal(value) => ValueRef::BigDecimal(value),
196            ValueRepr::String(value) => ValueRef::String(value),
197            #[cfg(feature = "chrono")]
198            ValueRepr::Date(value) => ValueRef::Date(value),
199            #[cfg(feature = "chrono")]
200            ValueRepr::Time(value) => ValueRef::Time(value),
201            #[cfg(feature = "chrono")]
202            ValueRepr::DateTime(value) => ValueRef::DateTime(value),
203            #[cfg(feature = "chrono")]
204            ValueRepr::Instant(value) => ValueRef::Instant(value),
205            ValueRepr::Duration(value) => ValueRef::Duration(value),
206            #[cfg(feature = "url")]
207            ValueRepr::Url(value) => ValueRef::Url(value.as_ref()),
208            ValueRepr::StringMap(value) => ValueRef::StringMap(value),
209            #[cfg(feature = "json")]
210            ValueRepr::Json(value) => ValueRef::Json(value),
211        }
212    }
213}
214
215macro_rules! value_data_type_match {
216    ($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)),+ $(,)?) => {
217        match &$value.repr {
218            ValueRepr::Unset(data_type) => *data_type,
219            $($(#[$cfg])* ValueRepr::$variant(_) => $data_type,)+
220        }
221    };
222}
223
224// ============================================================================
225// Getter method generation macro
226// ============================================================================
227
228/// Unified getter generation macro
229///
230/// Supports two modes:
231/// 1. `copy:` - For types implementing the Copy trait, directly returns the
232///    value
233/// 2. `ref:` - For non-Copy types, returns a reference
234///
235/// # Documentation Comment Support
236///
237/// The macro automatically extracts preceding documentation comments, so
238/// you can add `///` comments before macro invocations.
239impl Value {
240    /// Generic constructor method
241    ///
242    /// Creates a `Value` from any supported type, avoiding direct use of
243    /// enum variants.
244    ///
245    /// # Supported Generic Types
246    ///
247    /// `Value::new<T>(value)` currently supports the following `T`:
248    ///
249    /// - `bool`
250    /// - `char`
251    /// - `i8`, `i16`, `i32`, `i64`, `i128`
252    /// - `u8`, `u16`, `u32`, `u64`, `u128`
253    /// - `f32`, `f64`
254    /// - `String`, `&str`
255    /// - `NaiveDate`, `NaiveTime`, `NaiveDateTime`, `DateTime<Utc>`
256    /// - `BigInt`, `BigDecimal`
257    /// - `Duration`
258    /// - `Url`
259    /// - `HashMap<String, String>`
260    /// - `serde_json::Value`
261    ///
262    /// # Type Parameters
263    ///
264    /// * `T` - The type of the value to wrap
265    ///
266    /// # Parameters
267    ///
268    /// * `value` - Value to wrap.
269    ///
270    /// # Returns
271    ///
272    /// Returns a `Value` wrapping the given value
273    ///
274    /// # Examples
275    ///
276    /// ```rust
277    /// use qubit_value::Value;
278    ///
279    /// // Basic types
280    /// let v = Value::new(42i32);
281    /// assert_eq!(v.get_int32().unwrap(), 42);
282    ///
283    /// let v = Value::new(true);
284    /// assert_eq!(v.get_bool().unwrap(), true);
285    ///
286    /// // String
287    /// let v = Value::new("hello".to_string());
288    /// assert_eq!(v.get_string().unwrap(), "hello");
289    /// ```
290    #[inline(always)]
291    pub fn new<T>(value: T) -> Self
292    where
293        T: Into<Self>,
294    {
295        value.into()
296    }
297
298    /// Generic getter method.
299    ///
300    /// Performs a strict typed read of the stored value as `T`.
301    ///
302    /// `get<T>()` performs strict type matching. It does not do cross-type
303    /// conversion.
304    ///
305    /// For example, `Value::Int32(42).get::<i64>()` fails, while
306    /// `Value::Int32(42).to::<i64>()` succeeds.
307    ///
308    /// # Supported Generic Types
309    ///
310    /// `Value::get<T>()` currently supports the following `T`:
311    ///
312    /// - `bool`
313    /// - `char`
314    /// - `i8`, `i16`, `i32`, `i64`, `i128`
315    /// - `u8`, `u16`, `u32`, `u64`, `u128`
316    /// - `f32`, `f64`
317    /// - `String`
318    /// - `NaiveDate`, `NaiveTime`, `NaiveDateTime`, `DateTime<Utc>`
319    /// - `BigInt`, `BigDecimal`
320    /// - `Duration`
321    /// - `Url`
322    /// - `HashMap<String, String>`
323    /// - `serde_json::Value`
324    ///
325    /// # Type Parameters
326    ///
327    /// * `T` - The target type to retrieve
328    ///
329    /// # Returns
330    ///
331    /// Returns the stored value when its type matches `T`.
332    ///
333    /// # Errors
334    ///
335    /// Returns [`ValueError::Missing`] when the value is unset with the
336    /// requested type, or [`ValueError::TypeMismatch`] when the stored type
337    /// differs from `T`.
338    ///
339    /// # Examples
340    ///
341    /// ```rust
342    /// use qubit_value::Value;
343    ///
344    /// let value = Value::Int32(42);
345    ///
346    /// // Through type inference
347    /// let num: i32 = value.get().unwrap();
348    /// assert_eq!(num, 42);
349    ///
350    /// // Explicitly specify type parameter
351    /// let num = value.get::<i32>().unwrap();
352    /// assert_eq!(num, 42);
353    ///
354    /// // Different type
355    /// let text = Value::String("hello".to_string());
356    /// let s: String = text.get().unwrap();
357    /// assert_eq!(s, "hello");
358    ///
359    /// // Boolean value
360    /// let flag = Value::Bool(true);
361    /// let b: bool = flag.get().unwrap();
362    /// assert_eq!(b, true);
363    /// ```
364    #[inline(always)]
365    pub fn get<T>(&self) -> ValueResult<T>
366    where
367        for<'a> T: TryFrom<&'a Self, Error = ValueError>,
368    {
369        T::try_from(self)
370    }
371
372    /// Generic getter method with a default value.
373    ///
374    /// Returns the supplied default only when this value is unset. Type
375    /// mismatches and conversion errors are still returned as errors.
376    ///
377    /// # Type Parameters
378    ///
379    /// * `T` - Target type for the strict read and default value.
380    ///
381    /// # Parameters
382    ///
383    /// * `default` - Lazily materialized value used only when `self` is unset.
384    ///
385    /// # Returns
386    ///
387    /// The stored value, or `default` when the value is unset.
388    ///
389    /// # Errors
390    ///
391    /// Returns [`ValueError::TypeMismatch`] when the stored type differs from
392    /// `T`.
393    #[inline]
394    pub fn get_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
395    where
396        for<'a> T: TryFrom<&'a Self, Error = ValueError>,
397    {
398        match self.get() {
399            Err(ValueError::Missing(missing)) if missing.is_unset() => {
400                Ok(default.into_value_default())
401            }
402            result => result,
403        }
404    }
405
406    /// Strictly reads this value or calls `default` only when it is unset.
407    ///
408    /// # Type Parameters
409    ///
410    /// * `T` - Target type for the strict read and fallback value.
411    /// * `F` - Deferred fallback producing `T`.
412    ///
413    /// # Parameters
414    ///
415    /// * `default` - Callback invoked only when this value is unset.
416    ///
417    /// # Returns
418    ///
419    /// The stored value, or the callback result for an unset value.
420    ///
421    /// # Errors
422    ///
423    /// Returns [`ValueError::TypeMismatch`] when the stored type differs from
424    /// `T`; the callback is not invoked in that case.
425    #[inline]
426    pub fn get_or_else<T, F>(&self, default: F) -> ValueResult<T>
427    where
428        for<'a> T: TryFrom<&'a Self, Error = ValueError>,
429        F: FnOnce() -> T,
430    {
431        match self.get() {
432            Err(ValueError::Missing(missing)) if missing.is_unset() => {
433                Ok(default())
434            }
435            result => result,
436        }
437    }
438
439    /// Converts the stored value to another supported data type.
440    ///
441    /// This method delegates to the authoritative conversion contract in
442    /// [`qubit-datatype`](https://docs.rs/qubit-datatype/latest/qubit_datatype/).
443    /// The enabled rich-type features determine which source and target
444    /// families are available. An unset value is reported as a structured
445    /// missing-value conversion error.
446    ///
447    /// Unlike [`Self::get`], this method permits conversions supported by
448    /// [`qubit_datatype::DataConverter`] and applies
449    /// [`qubit_datatype::DataConversionOptions`].
450    ///
451    /// # Errors
452    ///
453    /// Returns a mapped conversion error when the value is unset, the
454    /// conversion is unsupported, or the source is invalid for `T`.
455    ///
456    /// # Returns
457    ///
458    /// The converted value.
459    ///
460    /// # Examples
461    ///
462    /// ```rust
463    /// use qubit_value::Value;
464    ///
465    /// let value = Value::Int32(42);
466    /// assert_eq!(value.to::<i64>().unwrap(), 42);
467    /// assert_eq!(value.to::<String>().unwrap(), "42");
468    /// ```
469    #[inline(always)]
470    #[cfg(feature = "converter")]
471    pub fn to<T>(&self) -> ValueResult<T>
472    where
473        T: DataConversionTarget,
474    {
475        self.to_with(DataConversionOptions::default_ref())
476    }
477
478    /// Converts this value to `T`, or returns `default` when storage is unset
479    /// or conversion reports a missing value.
480    ///
481    /// Conversion failures from concrete values are preserved.
482    ///
483    /// # Type Parameters
484    ///
485    /// * `T` - Target conversion type.
486    ///
487    /// # Parameters
488    ///
489    /// * `default` - Lazily materialized value used for unset or conversion-
490    ///   missing storage.
491    ///
492    /// # Returns
493    ///
494    /// The converted value, or `default` for an unset or conversion-missing
495    /// value.
496    ///
497    /// # Errors
498    ///
499    /// Returns a mapped conversion error for concrete values that cannot be
500    /// converted to `T`.
501    #[inline]
502    #[cfg(feature = "converter")]
503    pub fn to_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
504    where
505        T: DataConversionTarget,
506    {
507        match self.to() {
508            Err(ValueError::Missing(missing))
509                if missing.is_defaultable_for_conversion() =>
510            {
511                Ok(default.into_value_default())
512            }
513            result => result,
514        }
515    }
516
517    /// Converts this value to `T`, or calls `default` when storage is unset or
518    /// conversion reports a missing value.
519    ///
520    /// # Type Parameters
521    ///
522    /// * `T` - Target conversion type.
523    /// * `F` - Deferred fallback producing `T`.
524    ///
525    /// # Parameters
526    ///
527    /// * `default` - Callback invoked only when conversion reports a missing
528    ///   value.
529    ///
530    /// # Returns
531    ///
532    /// The converted value, or the callback result for an unset or
533    /// conversion-missing value.
534    ///
535    /// # Errors
536    ///
537    /// Preserves conversion errors from concrete values without invoking the
538    /// callback.
539    #[inline]
540    #[cfg(feature = "converter")]
541    pub fn to_or_else<T, F>(&self, default: F) -> ValueResult<T>
542    where
543        T: DataConversionTarget,
544        F: FnOnce() -> T,
545    {
546        match self.to() {
547            Err(ValueError::Missing(missing))
548                if missing.is_defaultable_for_conversion() =>
549            {
550                Ok(default())
551            }
552            result => result,
553        }
554    }
555
556    /// Converts this value to `T` using the provided conversion options.
557    ///
558    /// This method uses the shared [`qubit_datatype`] conversion layer
559    /// directly, so options such as string trimming, blank string handling,
560    /// and boolean aliases are applied consistently with other value
561    /// containers.
562    ///
563    /// # Type Parameters
564    ///
565    /// * `T` - The target type to convert to.
566    ///
567    /// # Parameters
568    ///
569    /// * `options` - Conversion options forwarded to the shared converter.
570    ///
571    /// # Returns
572    ///
573    /// Returns the converted value on success.
574    ///
575    /// # Errors
576    ///
577    /// Returns a [`crate::ValueError`] when the value is missing, unsupported,
578    /// or invalid for `T` under the provided options.
579    #[inline(always)]
580    #[cfg(feature = "converter")]
581    pub fn to_with<T>(&self, options: &DataConversionOptions) -> ValueResult<T>
582    where
583        T: DataConversionTarget,
584    {
585        super::value_converters::convert_with_data_converter_with(self, options)
586    }
587
588    /// Converts this value to `T` using conversion options, or returns
589    /// `default` when storage is unset or conversion reports a missing value.
590    ///
591    /// Conversion failures from concrete values are preserved.
592    ///
593    /// # Type Parameters
594    ///
595    /// * `T` - Target conversion type.
596    ///
597    /// # Parameters
598    ///
599    /// * `default` - Lazily materialized value used for unset or conversion-
600    ///   missing storage.
601    /// * `options` - Conversion options forwarded to the shared converter.
602    ///
603    /// # Returns
604    ///
605    /// The converted value, or `default` for an unset or conversion-missing
606    /// value.
607    ///
608    /// # Errors
609    ///
610    /// Returns a mapped conversion error for concrete values that cannot be
611    /// converted under `options`.
612    #[inline]
613    #[cfg(feature = "converter")]
614    pub fn to_or_with<T>(
615        &self,
616        default: impl IntoValueDefault<T>,
617        options: &DataConversionOptions,
618    ) -> ValueResult<T>
619    where
620        T: DataConversionTarget,
621    {
622        match self.to_with(options) {
623            Err(ValueError::Missing(missing))
624                if missing.is_defaultable_for_conversion() =>
625            {
626                Ok(default.into_value_default())
627            }
628            result => result,
629        }
630    }
631
632    /// Converts this value with `options`, or calls `default` when storage is
633    /// unset or conversion reports a missing value.
634    ///
635    /// # Type Parameters
636    ///
637    /// * `T` - Target conversion type.
638    /// * `F` - Deferred fallback producing `T`.
639    ///
640    /// # Parameters
641    ///
642    /// * `default` - Callback invoked only for a missing source value.
643    /// * `options` - Conversion options forwarded to the shared converter.
644    ///
645    /// # Returns
646    ///
647    /// The converted value, or the callback result for an unset or
648    /// conversion-missing value.
649    ///
650    /// # Errors
651    ///
652    /// Preserves concrete-value conversion errors without invoking the
653    /// callback.
654    #[inline]
655    #[cfg(feature = "converter")]
656    pub fn to_or_else_with<T, F>(
657        &self,
658        default: F,
659        options: &DataConversionOptions,
660    ) -> ValueResult<T>
661    where
662        T: DataConversionTarget,
663        F: FnOnce() -> T,
664    {
665        match self.to_with(options) {
666            Err(ValueError::Missing(missing))
667                if missing.is_defaultable_for_conversion() =>
668            {
669                Ok(default())
670            }
671            result => result,
672        }
673    }
674
675    /// Generic setter method
676    ///
677    /// Replaces the current value with any supported input value.
678    ///
679    /// This operation updates the stored type to `T` when needed. It does not
680    /// perform runtime type-mismatch validation against the previous variant.
681    ///
682    /// # Supported Generic Types
683    ///
684    /// `Value::set<T>(value)` currently supports the following `T`:
685    ///
686    /// - `bool`
687    /// - `char`
688    /// - `i8`, `i16`, `i32`, `i64`, `i128`
689    /// - `u8`, `u16`, `u32`, `u64`, `u128`
690    /// - `f32`, `f64`
691    /// - `String`, `&str`
692    /// - `NaiveDate`, `NaiveTime`, `NaiveDateTime`, `DateTime<Utc>`
693    /// - `BigInt`, `BigDecimal`
694    /// - `Duration`
695    /// - `Url`
696    /// - `HashMap<String, String>`
697    /// - `serde_json::Value`
698    ///
699    /// # Type Parameters
700    ///
701    /// * `T` - Input type convertible into [`Value`].
702    ///
703    /// # Parameters
704    ///
705    /// * `value` - The value to set
706    ///
707    /// # Compile-time restriction
708    ///
709    /// Unsupported input types fail to compile because they do not implement
710    /// `Into<Value>`.
711    ///
712    /// # Examples
713    ///
714    /// ```rust
715    /// use qubit_datatype::DataType;
716    /// use qubit_value::Value;
717    ///
718    /// let mut value = Value::Unset(DataType::Int32);
719    ///
720    /// // Through type inference
721    /// value.set(42i32);
722    /// assert_eq!(value.get_int32().unwrap(), 42);
723    ///
724    /// // Explicitly specify type parameter
725    /// value.set::<i32>(100);
726    /// assert_eq!(value.get_int32().unwrap(), 100);
727    ///
728    /// // String type
729    /// let mut text = Value::Unset(DataType::String);
730    /// text.set("hello".to_string());
731    /// assert_eq!(text.get_string().unwrap(), "hello");
732    /// ```
733    #[inline(always)]
734    pub fn set<T>(&mut self, value: T)
735    where
736        T: Into<Self>,
737    {
738        *self = value.into();
739    }
740
741    /// Get the data type of the value
742    ///
743    /// # Returns
744    ///
745    /// Returns the data type corresponding to this value
746    ///
747    /// # Examples
748    ///
749    /// ```rust
750    /// use qubit_datatype::DataType;
751    /// use qubit_value::Value;
752    ///
753    /// let value = Value::Int32(42);
754    /// assert_eq!(value.data_type(), DataType::Int32);
755    ///
756    /// let empty = Value::Unset(DataType::String);
757    /// assert_eq!(empty.data_type(), DataType::String);
758    /// ```
759    ///
760    /// ```compile_fail
761    /// #![deny(unused_must_use)]
762    /// use qubit_value::Value;
763    ///
764    /// Value::new(42_i32).data_type();
765    /// ```
766    #[inline(always)]
767    pub fn data_type(&self) -> DataType {
768        for_each_value_type!(value_data_type_match, self)
769    }
770
771    /// Tests whether this container has no concrete value.
772    ///
773    /// # Returns
774    ///
775    /// Returns `true` only for [`Value::Unset`]. An empty string, map, or JSON
776    /// container is still a concrete value and returns `false`.
777    ///
778    /// # Examples
779    ///
780    /// ```rust
781    /// use qubit_datatype::DataType;
782    /// use qubit_value::Value;
783    ///
784    /// let value = Value::Int32(42);
785    /// assert!(!value.is_unset());
786    ///
787    /// let empty = Value::Unset(DataType::String);
788    /// assert!(empty.is_unset());
789    /// ```
790    #[inline(always)]
791    #[must_use]
792    pub fn is_unset(&self) -> bool {
793        matches!(self.repr, ValueRepr::Unset(_))
794    }
795
796    /// Tests whether a concrete value belongs to the numeric type family.
797    ///
798    /// An unset value returns `false`, even when its declared type is numeric.
799    ///
800    /// # Returns
801    ///
802    /// `true` for concrete numeric variants; otherwise `false`.
803    #[inline(always)]
804    #[must_use]
805    pub fn is_numeric(&self) -> bool {
806        !self.is_unset() && self.data_type().is_numeric()
807    }
808
809    /// Removes the concrete value while preserving its declared data type.
810    #[inline(always)]
811    pub fn unset(&mut self) {
812        *self = Value::new_unset(self.data_type());
813    }
814
815    /// Set the data type
816    ///
817    /// If the new type differs from the current type, clears the value
818    /// and sets the new type.
819    ///
820    /// # Parameters
821    ///
822    /// * `data_type` - The data type to set
823    ///
824    /// # Examples
825    ///
826    /// ```rust
827    /// use qubit_datatype::DataType;
828    /// use qubit_value::Value;
829    ///
830    /// let mut value = Value::Int32(42);
831    /// value.set_type(DataType::String);
832    /// assert!(value.is_unset());
833    /// assert_eq!(value.data_type(), DataType::String);
834    /// ```
835    #[inline]
836    pub fn set_type(&mut self, data_type: DataType) {
837        if self.data_type() != data_type {
838            *self = Value::new_unset(data_type);
839        }
840    }
841}