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///
42/// ```compile_fail
43/// #![deny(unused_must_use)]
44/// use qubit_argument::ArgumentValue;
45///
46/// ArgumentValue::Signed(1);
47/// ```
48#[non_exhaustive]
49#[must_use]
50#[derive(Clone, Copy, PartialEq, Eq, Hash)]
51pub enum ArgumentValue {
52 /// A signed integer represented without loss.
53 Signed(i128),
54 /// An unsigned integer represented without loss.
55 Unsigned(u128),
56 /// The raw bits of a 32-bit floating-point value.
57 Float32(u32),
58 /// The raw bits of a 64-bit floating-point value.
59 Float64(u64),
60 /// An exact standard-library duration value.
61 Duration(Duration),
62}
63
64macro_rules! impl_from_signed_integer {
65 ($($source:ty),+ $(,)?) => {
66 $(
67 impl From<$source> for ArgumentValue {
68 /// Converts a signed primitive integer without losing its value.
69 #[inline(always)]
70 fn from(value: $source) -> Self {
71 Self::Signed(value as i128)
72 }
73 }
74 )+
75 };
76}
77
78macro_rules! impl_from_unsigned_integer {
79 ($($source:ty),+ $(,)?) => {
80 $(
81 impl From<$source> for ArgumentValue {
82 /// Converts an unsigned primitive integer without losing its value.
83 #[inline(always)]
84 fn from(value: $source) -> Self {
85 Self::Unsigned(value as u128)
86 }
87 }
88 )+
89 };
90}
91
92impl_from_signed_integer!(i8, i16, i32, i64, i128, isize);
93impl_from_unsigned_integer!(u8, u16, u32, u64, u128, usize);
94
95impl From<f32> for ArgumentValue {
96 /// Captures the exact IEEE 754 bit pattern of a 32-bit float.
97 #[inline(always)]
98 fn from(value: f32) -> Self {
99 Self::Float32(value.to_bits())
100 }
101}
102
103impl From<f64> for ArgumentValue {
104 /// Captures the exact IEEE 754 bit pattern of a 64-bit float.
105 #[inline(always)]
106 fn from(value: f64) -> Self {
107 Self::Float64(value.to_bits())
108 }
109}
110
111impl From<Duration> for ArgumentValue {
112 /// Captures an exact standard-library duration value.
113 #[inline(always)]
114 fn from(value: Duration) -> Self {
115 Self::Duration(value)
116 }
117}
118
119impl Debug for ArgumentValue {
120 /// Formats the variant with its reconstructed scalar value.
121 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
122 match self {
123 Self::Signed(value) => {
124 formatter.debug_tuple("Signed").field(value).finish()
125 }
126 Self::Unsigned(value) => {
127 formatter.debug_tuple("Unsigned").field(value).finish()
128 }
129 Self::Float32(bits) => formatter
130 .debug_tuple("Float32")
131 .field(&f32::from_bits(*bits))
132 .finish(),
133 Self::Float64(bits) => formatter
134 .debug_tuple("Float64")
135 .field(&f64::from_bits(*bits))
136 .finish(),
137 Self::Duration(value) => {
138 formatter.debug_tuple("Duration").field(value).finish()
139 }
140 }
141 }
142}
143
144impl Display for ArgumentValue {
145 /// Formats the represented scalar value without a variant label.
146 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
147 match self {
148 Self::Signed(value) => Display::fmt(value, formatter),
149 Self::Unsigned(value) => Display::fmt(value, formatter),
150 Self::Float32(bits) => {
151 Display::fmt(&f32::from_bits(*bits), formatter)
152 }
153 Self::Float64(bits) => {
154 Display::fmt(&f64::from_bits(*bits), formatter)
155 }
156 Self::Duration(value) => Debug::fmt(value, formatter),
157 }
158 }
159}