qubit-argument 0.4.6

Structured, ownership-preserving argument validation for Rust applications
Documentation
// =============================================================================
//    Copyright (c) 2025 - 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Ownership-preserving validation for floating-point arguments.

use crate::argument::ArgumentError;
use crate::argument::ArgumentErrorKind;
use crate::argument::ArgumentResult;
use crate::argument::ArgumentValue;
use crate::argument::internal::Sealed;

/// Validates properties specific to floating-point arguments.
///
/// This trait is implemented only for `f32` and `f64`. Successful validation
/// returns the original value with its exact bit pattern unchanged.
///
/// The trait is sealed: downstream crates can use its methods but cannot add
/// implementations for other types.
///
/// # Examples
///
/// ```
/// use qubit_argument::FloatArgument;
///
/// let value = 1.5_f64.require_finite("ratio").unwrap();
/// assert_eq!(value, 1.5);
/// ```
///
/// ```compile_fail
/// use qubit_argument::ArgumentResult;
/// use qubit_argument::FloatArgument;
///
/// struct CustomFloat;
///
/// impl FloatArgument for CustomFloat {
///     fn require_finite(self, _path: &str) -> ArgumentResult<Self> {
///         Ok(self)
///     }
/// }
/// ```
pub trait FloatArgument: Sealed + Sized {
    /// Requires this floating-point value to be finite.
    ///
    /// Success returns the original value. NaN returns
    /// [`ArgumentErrorKind::NotANumber`]; positive or negative infinity returns
    /// [`ArgumentErrorKind::NotFinite`] with the exact floating-point value at
    /// `path`.
    ///
    /// # Parameters
    ///
    /// * `path` — the argument path to include in any validation error.
    ///
    /// # Returns
    ///
    /// The original value when it is finite.
    ///
    /// # Errors
    ///
    /// Returns [`ArgumentErrorKind::NotANumber`] for NaN, or
    /// [`ArgumentErrorKind::NotFinite`] for positive or negative infinity.
    fn require_finite(self, path: &str) -> ArgumentResult<Self>;
}

macro_rules! impl_float_argument {
    ($($float_type:ty),+ $(,)?) => {
        $(
            impl FloatArgument for $float_type {
                /// Requires a finite value and preserves its exact bit pattern.
                #[inline]
                fn require_finite(self, path: &str) -> ArgumentResult<Self> {
                    if self.is_nan() {
                        Err(ArgumentError::new(
                            path,
                            ArgumentErrorKind::NotANumber,
                        ))
                    } else if self.is_finite() {
                        Ok(self)
                    } else {
                        Err(ArgumentError::new(
                            path,
                            ArgumentErrorKind::NotFinite {
                                actual: ArgumentValue::from(self),
                            },
                        ))
                    }
                }
            }
        )+
    };
}

impl_float_argument!(f32, f64);