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}