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