Skip to main content

qubit_argument/argument/
argument_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//! Lossless representations of scalar argument values.
9
10use std::fmt::{
11    self,
12    Debug,
13    Display,
14    Formatter,
15};
16use std::time::Duration;
17
18/// A scalar value captured for a validation constraint or error.
19///
20/// Floating-point values are stored as raw IEEE 754 bits, so equality and
21/// hashing preserve distinctions such as signed zero and NaN payloads.
22/// Duration values retain their exact seconds and nanoseconds. Formatting
23/// reconstructs floats and preserves unit-bearing duration diagnostics.
24///
25/// This enum is non-exhaustive. Downstream matches must include a wildcard arm
26/// so future scalar representations can be added without a breaking release.
27///
28/// ```compile_fail
29/// use qubit_argument::ArgumentValue;
30///
31/// fn classify(value: ArgumentValue) -> &'static str {
32///     match value {
33///         ArgumentValue::Signed(_) => "signed",
34///         ArgumentValue::Unsigned(_) => "unsigned",
35///         ArgumentValue::Float32(_) => "f32",
36///         ArgumentValue::Float64(_) => "f64",
37///         ArgumentValue::Duration(_) => "duration",
38///     }
39/// }
40/// ```
41#[non_exhaustive]
42#[derive(Clone, Copy, PartialEq, Eq, Hash)]
43pub enum ArgumentValue {
44    /// A signed integer represented without loss.
45    Signed(i128),
46    /// An unsigned integer represented without loss.
47    Unsigned(u128),
48    /// The raw bits of a 32-bit floating-point value.
49    Float32(u32),
50    /// The raw bits of a 64-bit floating-point value.
51    Float64(u64),
52    /// An exact standard-library duration value.
53    Duration(Duration),
54}
55
56macro_rules! impl_from_signed_integer {
57    ($($source:ty),+ $(,)?) => {
58        $(
59            impl From<$source> for ArgumentValue {
60                /// Converts a signed primitive integer without losing its value.
61                #[inline]
62                fn from(value: $source) -> Self {
63                    Self::Signed(value as i128)
64                }
65            }
66        )+
67    };
68}
69
70macro_rules! impl_from_unsigned_integer {
71    ($($source:ty),+ $(,)?) => {
72        $(
73            impl From<$source> for ArgumentValue {
74                /// Converts an unsigned primitive integer without losing its value.
75                #[inline]
76                fn from(value: $source) -> Self {
77                    Self::Unsigned(value as u128)
78                }
79            }
80        )+
81    };
82}
83
84impl_from_signed_integer!(i8, i16, i32, i64, i128, isize);
85impl_from_unsigned_integer!(u8, u16, u32, u64, u128, usize);
86
87impl From<f32> for ArgumentValue {
88    /// Captures the exact IEEE 754 bit pattern of a 32-bit float.
89    #[inline]
90    fn from(value: f32) -> Self {
91        Self::Float32(value.to_bits())
92    }
93}
94
95impl From<f64> for ArgumentValue {
96    /// Captures the exact IEEE 754 bit pattern of a 64-bit float.
97    #[inline]
98    fn from(value: f64) -> Self {
99        Self::Float64(value.to_bits())
100    }
101}
102
103impl From<Duration> for ArgumentValue {
104    /// Captures an exact standard-library duration value.
105    #[inline]
106    fn from(value: Duration) -> Self {
107        Self::Duration(value)
108    }
109}
110
111impl Debug for ArgumentValue {
112    /// Formats the variant with its reconstructed scalar value.
113    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
114        match self {
115            Self::Signed(value) => {
116                formatter.debug_tuple("Signed").field(value).finish()
117            }
118            Self::Unsigned(value) => {
119                formatter.debug_tuple("Unsigned").field(value).finish()
120            }
121            Self::Float32(bits) => formatter
122                .debug_tuple("Float32")
123                .field(&f32::from_bits(*bits))
124                .finish(),
125            Self::Float64(bits) => formatter
126                .debug_tuple("Float64")
127                .field(&f64::from_bits(*bits))
128                .finish(),
129            Self::Duration(value) => {
130                formatter.debug_tuple("Duration").field(value).finish()
131            }
132        }
133    }
134}
135
136impl Display for ArgumentValue {
137    /// Formats the represented scalar value without a variant label.
138    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
139        match self {
140            Self::Signed(value) => Display::fmt(value, formatter),
141            Self::Unsigned(value) => Display::fmt(value, formatter),
142            Self::Float32(bits) => {
143                Display::fmt(&f32::from_bits(*bits), formatter)
144            }
145            Self::Float64(bits) => {
146                Display::fmt(&f64::from_bits(*bits), formatter)
147            }
148            Self::Duration(value) => Debug::fmt(value, formatter),
149        }
150    }
151}