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