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