Skip to main content

qubit_argument/argument/
float_argument.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//! Ownership-preserving validation for floating-point arguments.
9
10use crate::argument::ArgumentError;
11use crate::argument::ArgumentErrorKind;
12use crate::argument::ArgumentResult;
13use crate::argument::ArgumentValue;
14use crate::argument::internal::Sealed;
15
16/// Validates properties specific to floating-point arguments.
17///
18/// This trait is implemented only for `f32` and `f64`. Successful validation
19/// returns the original value with its exact bit pattern unchanged.
20///
21/// The trait is sealed: downstream crates can use its methods but cannot add
22/// implementations for other types.
23///
24/// # Examples
25///
26/// ```
27/// use qubit_argument::FloatArgument;
28///
29/// let value = 1.5_f64.require_finite("ratio").unwrap();
30/// assert_eq!(value, 1.5);
31/// ```
32///
33/// ```compile_fail
34/// use qubit_argument::ArgumentResult;
35/// use qubit_argument::FloatArgument;
36///
37/// struct CustomFloat;
38///
39/// impl FloatArgument for CustomFloat {
40///     fn require_finite(self, _path: &str) -> ArgumentResult<Self> {
41///         Ok(self)
42///     }
43/// }
44/// ```
45pub trait FloatArgument: Sealed + Sized {
46    /// Requires this floating-point value to be finite.
47    ///
48    /// Success returns the original value. NaN returns
49    /// [`ArgumentErrorKind::NotANumber`]; positive or negative infinity returns
50    /// [`ArgumentErrorKind::NotFinite`] with the exact floating-point value at
51    /// `path`.
52    ///
53    /// # Parameters
54    ///
55    /// * `path` — the argument path to include in any validation error.
56    ///
57    /// # Returns
58    ///
59    /// The original value when it is finite.
60    ///
61    /// # Errors
62    ///
63    /// Returns [`ArgumentErrorKind::NotANumber`] for NaN, or
64    /// [`ArgumentErrorKind::NotFinite`] for positive or negative infinity.
65    fn require_finite(self, path: &str) -> ArgumentResult<Self>;
66}
67
68macro_rules! impl_float_argument {
69    ($($float_type:ty),+ $(,)?) => {
70        $(
71            impl FloatArgument for $float_type {
72                /// Requires a finite value and preserves its exact bit pattern.
73                #[inline]
74                fn require_finite(self, path: &str) -> ArgumentResult<Self> {
75                    if self.is_nan() {
76                        Err(ArgumentError::new(
77                            path,
78                            ArgumentErrorKind::NotANumber,
79                        ))
80                    } else if self.is_finite() {
81                        Ok(self)
82                    } else {
83                        Err(ArgumentError::new(
84                            path,
85                            ArgumentErrorKind::NotFinite {
86                                actual: ArgumentValue::from(self),
87                            },
88                        ))
89                    }
90                }
91            }
92        )+
93    };
94}
95
96impl_float_argument!(f32, f64);