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}