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::{
11    ArgumentError,
12    ArgumentErrorKind,
13    ArgumentResult,
14    ArgumentValue,
15    internal::Sealed,
16};
17
18/// Validates properties specific to floating-point arguments.
19///
20/// This trait is implemented only for `f32` and `f64`. Successful validation
21/// returns the original value with its exact bit pattern unchanged.
22///
23/// The trait is sealed: downstream crates can use its methods but cannot add
24/// implementations for other types.
25///
26/// ```compile_fail
27/// use qubit_argument::{ArgumentResult, FloatArgument};
28///
29/// struct CustomFloat;
30///
31/// impl FloatArgument for CustomFloat {
32///     fn require_finite(self, _path: &str) -> ArgumentResult<Self> {
33///         Ok(self)
34///     }
35/// }
36/// ```
37pub trait FloatArgument: Sealed + Sized {
38    /// Requires this floating-point value to be finite.
39    ///
40    /// Success returns the original value. NaN returns
41    /// [`ArgumentErrorKind::NotANumber`]; positive or negative infinity returns
42    /// [`ArgumentErrorKind::NotFinite`] with the exact floating-point value at
43    /// `path`.
44    fn require_finite(self, path: &str) -> ArgumentResult<Self>;
45}
46
47macro_rules! impl_float_argument {
48    ($($float_type:ty),+ $(,)?) => {
49        $(
50            impl FloatArgument for $float_type {
51                /// Requires a finite value and preserves its exact bit pattern.
52                #[inline]
53                fn require_finite(self, path: &str) -> ArgumentResult<Self> {
54                    if self.is_nan() {
55                        Err(ArgumentError::new(
56                            path,
57                            ArgumentErrorKind::NotANumber,
58                        ))
59                    } else if self.is_finite() {
60                        Ok(self)
61                    } else {
62                        Err(ArgumentError::new(
63                            path,
64                            ArgumentErrorKind::NotFinite {
65                                actual: ArgumentValue::from(self),
66                            },
67                        ))
68                    }
69                }
70            }
71        )+
72    };
73}
74
75impl_float_argument!(f32, f64);