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;
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}