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