Skip to main content

qubit_common/lang/argument/
numeric.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Numeric Argument Validation
10//!
11//! Provides validation functionality for numeric type arguments.
12//!
13//! # Author
14//!
15//! Haixing Hu
16
17use super::error::{
18    ArgumentError,
19    ArgumentResult,
20};
21use std::fmt::Display;
22
23/// Internal trait that restricts `NumericArgument` to actual numeric types.
24trait NumericValue: PartialOrd + Display + Copy {
25    /// Returns the additive identity (zero) of the numeric type.
26    fn zero() -> Self;
27
28    /// Returns true when the value is NaN.
29    ///
30    /// Non-floating-point types always return false.
31    fn is_nan(self) -> bool {
32        false
33    }
34}
35
36macro_rules! impl_numeric_value_for_int {
37    ($($t:ty),+ $(,)?) => {
38        $(
39            impl NumericValue for $t {
40                #[inline]
41                fn zero() -> Self {
42                    0
43                }
44            }
45        )+
46    };
47}
48
49impl_numeric_value_for_int!(i8, i16, i32, i64, i128, isize);
50impl_numeric_value_for_int!(u8, u16, u32, u64, u128, usize);
51
52impl NumericValue for f32 {
53    #[inline]
54    fn zero() -> Self {
55        0.0
56    }
57
58    #[inline]
59    fn is_nan(self) -> bool {
60        self.is_nan()
61    }
62}
63
64impl NumericValue for f64 {
65    #[inline]
66    fn zero() -> Self {
67        0.0
68    }
69
70    #[inline]
71    fn is_nan(self) -> bool {
72        self.is_nan()
73    }
74}
75
76#[inline]
77fn reject_nan<T: NumericValue>(name: &str, value: T) -> ArgumentResult<()> {
78    if value.is_nan() {
79        return Err(ArgumentError::new(format!(
80            "Parameter '{}' must not be NaN",
81            name
82        )));
83    }
84    Ok(())
85}
86
87/// Numeric argument validation trait
88///
89/// Provides validation methods for all sortable numeric types, supporting method chaining.
90///
91/// # Features
92///
93/// - Zero-cost abstraction: Same performance as manual checks after compilation
94/// - Method chaining: Can perform multiple validations in sequence
95/// - Type safety: Leverages Rust's type system to ensure correctness
96/// - Clear errors: Provides friendly error messages
97///
98/// # Use Cases
99///
100/// - Validating function parameter validity
101/// - Configuration value range checking
102/// - User input numeric validation
103///
104/// # Examples
105///
106/// Basic usage (returns `ArgumentResult`):
107///
108/// ```rust
109/// use qubit_common::lang::argument::{NumericArgument, ArgumentResult};
110///
111/// fn set_volume(volume: i32) -> ArgumentResult<()> {
112///     let volume = volume.require_in_closed_range("volume", 0, 100)?;
113///     println!("Volume: {}", volume);
114///     Ok(())
115/// }
116/// ```
117///
118/// Converting to other error types:
119///
120/// ```rust
121/// use qubit_common::lang::argument::NumericArgument;
122///
123/// fn set_volume(volume: i32) -> Result<(), String> {
124///     let volume = volume
125///         .require_non_negative("volume")
126///         .and_then(|v| v.require_in_closed_range("volume", 0, 100))
127///         .map_err(|e| e.to_string())?;
128///     println!("Volume: {}", volume);
129///     Ok(())
130/// }
131/// ```
132///
133/// # Author
134///
135/// Haixing Hu
136///
137pub trait NumericArgument: Sized {
138    /// Validate that value is zero
139    ///
140    /// # Parameters
141    ///
142    /// * `name` - Parameter name for error message generation
143    ///
144    /// # Returns
145    ///
146    /// Returns `Ok(self)` if value is zero, otherwise returns an error
147    ///
148    /// # Examples
149    ///
150    /// ```rust
151    /// use qubit_common::lang::argument::NumericArgument;
152    ///
153    /// let value: i32 = 0;
154    /// assert!(value.require_zero("value").is_ok());
155    ///
156    /// let non_zero: i32 = 5;
157    /// assert!(non_zero.require_zero("value").is_err());
158    /// ```
159    fn require_zero(self, name: &str) -> ArgumentResult<Self>;
160
161    /// Validate that value is non-zero
162    ///
163    /// # Parameters
164    ///
165    /// * `name` - Parameter name
166    ///
167    /// # Returns
168    ///
169    /// Returns `Ok(self)` if value is non-zero, otherwise returns an error
170    ///
171    /// # Examples
172    ///
173    /// ```rust
174    /// use qubit_common::lang::argument::NumericArgument;
175    ///
176    /// let value: i32 = 10;
177    /// assert!(value.require_non_zero("value").is_ok());
178    ///
179    /// let zero: i32 = 0;
180    /// assert!(zero.require_non_zero("value").is_err());
181    /// ```
182    fn require_non_zero(self, name: &str) -> ArgumentResult<Self>;
183
184    /// Validate that value is positive
185    ///
186    /// # Parameters
187    ///
188    /// * `name` - Parameter name
189    ///
190    /// # Returns
191    ///
192    /// Returns `Ok(self)` if value is greater than zero, otherwise returns an error
193    ///
194    /// # Examples
195    ///
196    /// ```rust
197    /// use qubit_common::lang::argument::NumericArgument;
198    ///
199    /// let value: i32 = 10;
200    /// assert!(value.require_positive("value").is_ok());
201    ///
202    /// let zero: i32 = 0;
203    /// assert!(zero.require_positive("value").is_err());
204    /// ```
205    fn require_positive(self, name: &str) -> ArgumentResult<Self>;
206
207    /// Validate that value is non-negative
208    ///
209    /// # Parameters
210    ///
211    /// * `name` - Parameter name
212    ///
213    /// # Returns
214    ///
215    /// Returns `Ok(self)` if value is non-negative, otherwise returns an error
216    ///
217    /// # Examples
218    ///
219    /// ```rust
220    /// use qubit_common::lang::argument::NumericArgument;
221    ///
222    /// let value: i32 = 0;
223    /// assert!(value.require_non_negative("value").is_ok());
224    ///
225    /// let negative: i32 = -5;
226    /// assert!(negative.require_non_negative("value").is_err());
227    /// ```
228    fn require_non_negative(self, name: &str) -> ArgumentResult<Self>;
229
230    /// Validate that value is negative
231    ///
232    /// # Parameters
233    ///
234    /// * `name` - Parameter name
235    ///
236    /// # Returns
237    ///
238    /// Returns `Ok(self)` if value is less than zero, otherwise returns an error
239    ///
240    /// # Examples
241    ///
242    /// ```rust
243    /// use qubit_common::lang::argument::NumericArgument;
244    ///
245    /// let value: i32 = -5;
246    /// assert!(value.require_negative("value").is_ok());
247    ///
248    /// let positive: i32 = 5;
249    /// assert!(positive.require_negative("value").is_err());
250    /// ```
251    fn require_negative(self, name: &str) -> ArgumentResult<Self>;
252
253    /// Validate that value is non-positive
254    ///
255    /// # Parameters
256    ///
257    /// * `name` - Parameter name
258    ///
259    /// # Returns
260    ///
261    /// Returns `Ok(self)` if value is less than or equal to zero, otherwise returns an error
262    ///
263    /// # Examples
264    ///
265    /// ```rust
266    /// use qubit_common::lang::argument::NumericArgument;
267    ///
268    /// let value: i32 = 0;
269    /// assert!(value.require_non_positive("value").is_ok());
270    ///
271    /// let negative: i32 = -5;
272    /// assert!(negative.require_non_positive("value").is_ok());
273    ///
274    /// let positive: i32 = 5;
275    /// assert!(positive.require_non_positive("value").is_err());
276    /// ```
277    fn require_non_positive(self, name: &str) -> ArgumentResult<Self>;
278
279    /// Validate that value is within closed interval
280    ///
281    /// # Parameters
282    ///
283    /// * `name` - Parameter name
284    /// * `min` - Minimum value (inclusive)
285    /// * `max` - Maximum value (inclusive)
286    ///
287    /// # Returns
288    ///
289    /// Returns `Ok(self)` if value is within [min, max] range, otherwise returns an error
290    ///
291    /// # Examples
292    ///
293    /// ```rust
294    /// use qubit_common::lang::argument::NumericArgument;
295    ///
296    /// let value = 50;
297    /// assert!(value.require_in_closed_range("value", 0, 100).is_ok());
298    ///
299    /// let out_of_range = 150;
300    /// assert!(out_of_range.require_in_closed_range("value", 0, 100).is_err());
301    /// ```
302    fn require_in_closed_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self>;
303
304    /// Validate that value is within open interval
305    ///
306    /// # Parameters
307    ///
308    /// * `name` - Parameter name
309    /// * `min` - Minimum value (exclusive)
310    /// * `max` - Maximum value (exclusive)
311    ///
312    /// # Returns
313    ///
314    /// Returns `Ok(self)` if value is within (min, max) range, otherwise returns an error
315    ///
316    /// # Examples
317    ///
318    /// ```rust
319    /// use qubit_common::lang::argument::NumericArgument;
320    ///
321    /// let value = 50;
322    /// assert!(value.require_in_open_range("value", 0, 100).is_ok());
323    ///
324    /// let boundary = 0;
325    /// assert!(boundary.require_in_open_range("value", 0, 100).is_err());
326    /// ```
327    fn require_in_open_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self>;
328
329    /// Validate that value is within left-open right-closed interval
330    ///
331    /// # Parameters
332    ///
333    /// * `name` - Parameter name
334    /// * `min` - Minimum value (exclusive)
335    /// * `max` - Maximum value (inclusive)
336    ///
337    /// # Returns
338    ///
339    /// Returns `Ok(self)` if value is within (min, max] range, otherwise returns an error
340    ///
341    /// # Examples
342    ///
343    /// ```rust
344    /// use qubit_common::lang::argument::NumericArgument;
345    ///
346    /// let value = 100;
347    /// assert!(value.require_in_left_open_range("value", 0, 100).is_ok());
348    ///
349    /// let min_boundary = 0;
350    /// assert!(min_boundary.require_in_left_open_range("value", 0, 100).is_err());
351    /// ```
352    fn require_in_left_open_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self>;
353
354    /// Validate that value is within left-closed right-open interval
355    ///
356    /// # Parameters
357    ///
358    /// * `name` - Parameter name
359    /// * `min` - Minimum value (inclusive)
360    /// * `max` - Maximum value (exclusive)
361    ///
362    /// # Returns
363    ///
364    /// Returns `Ok(self)` if value is within [min, max) range, otherwise returns an error
365    ///
366    /// # Examples
367    ///
368    /// ```rust
369    /// use qubit_common::lang::argument::NumericArgument;
370    ///
371    /// let value = 0;
372    /// assert!(value.require_in_right_open_range("value", 0, 100).is_ok());
373    ///
374    /// let max_boundary = 100;
375    /// assert!(max_boundary.require_in_right_open_range("value", 0, 100).is_err());
376    /// ```
377    fn require_in_right_open_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self>;
378
379    /// Validate that value is less than specified value
380    ///
381    /// # Parameters
382    ///
383    /// * `name` - Parameter name
384    /// * `max` - Maximum value (exclusive)
385    ///
386    /// # Returns
387    ///
388    /// Returns `Ok(self)` if value is less than max, otherwise returns an error
389    ///
390    /// # Examples
391    ///
392    /// ```rust
393    /// use qubit_common::lang::argument::NumericArgument;
394    ///
395    /// let value = 50;
396    /// assert!(value.require_less("value", 100).is_ok());
397    ///
398    /// let boundary = 100;
399    /// assert!(boundary.require_less("value", 100).is_err());
400    /// ```
401    fn require_less(self, name: &str, max: Self) -> ArgumentResult<Self>;
402
403    /// Validate that value is less than or equal to specified value
404    ///
405    /// # Parameters
406    ///
407    /// * `name` - Parameter name
408    /// * `max` - Maximum value (inclusive)
409    ///
410    /// # Returns
411    ///
412    /// Returns `Ok(self)` if value is less than or equal to max, otherwise returns an error
413    ///
414    /// # Examples
415    ///
416    /// ```rust
417    /// use qubit_common::lang::argument::NumericArgument;
418    ///
419    /// let value = 100;
420    /// assert!(value.require_less_equal("value", 100).is_ok());
421    ///
422    /// let over = 101;
423    /// assert!(over.require_less_equal("value", 100).is_err());
424    /// ```
425    fn require_less_equal(self, name: &str, max: Self) -> ArgumentResult<Self>;
426
427    /// Validate that value is greater than specified value
428    ///
429    /// # Parameters
430    ///
431    /// * `name` - Parameter name
432    /// * `min` - Minimum value (exclusive)
433    ///
434    /// # Returns
435    ///
436    /// Returns `Ok(self)` if value is greater than min, otherwise returns an error
437    ///
438    /// # Examples
439    ///
440    /// ```rust
441    /// use qubit_common::lang::argument::NumericArgument;
442    ///
443    /// let value = 50;
444    /// assert!(value.require_greater("value", 0).is_ok());
445    ///
446    /// let boundary = 0;
447    /// assert!(boundary.require_greater("value", 0).is_err());
448    /// ```
449    fn require_greater(self, name: &str, min: Self) -> ArgumentResult<Self>;
450
451    /// Validate that value is greater than or equal to specified value
452    ///
453    /// # Parameters
454    ///
455    /// * `name` - Parameter name
456    /// * `min` - Minimum value (inclusive)
457    ///
458    /// # Returns
459    ///
460    /// Returns `Ok(self)` if value is greater than or equal to min, otherwise returns an error
461    ///
462    /// # Examples
463    ///
464    /// ```rust
465    /// use qubit_common::lang::argument::NumericArgument;
466    ///
467    /// let value = 0;
468    /// assert!(value.require_greater_equal("value", 0).is_ok());
469    ///
470    /// let under = -1;
471    /// assert!(under.require_greater_equal("value", 0).is_err());
472    /// ```
473    fn require_greater_equal(self, name: &str, min: Self) -> ArgumentResult<Self>;
474}
475
476/// Implement numeric argument validation for all ordered displayable types
477///
478/// Automatically provides validation functionality for all standard numeric
479/// types: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize,
480/// f32, and f64.
481impl<T> NumericArgument for T
482where
483    T: NumericValue,
484{
485    #[inline]
486    fn require_zero(self, name: &str) -> ArgumentResult<Self> {
487        reject_nan(name, self)?;
488        if self != T::zero() {
489            return Err(ArgumentError::new(format!(
490                "Parameter '{}' must be zero but was: {}",
491                name, self
492            )));
493        }
494        Ok(self)
495    }
496
497    #[inline]
498    fn require_non_zero(self, name: &str) -> ArgumentResult<Self> {
499        reject_nan(name, self)?;
500        if self == T::zero() {
501            return Err(ArgumentError::new(format!(
502                "Parameter '{}' cannot be zero",
503                name
504            )));
505        }
506        Ok(self)
507    }
508
509    #[inline]
510    fn require_positive(self, name: &str) -> ArgumentResult<Self> {
511        reject_nan(name, self)?;
512        if self <= T::zero() {
513            return Err(ArgumentError::new(format!(
514                "Parameter '{}' must be positive but was: {}",
515                name, self
516            )));
517        }
518        Ok(self)
519    }
520
521    #[inline]
522    fn require_non_negative(self, name: &str) -> ArgumentResult<Self> {
523        reject_nan(name, self)?;
524        if self < T::zero() {
525            return Err(ArgumentError::new(format!(
526                "Parameter '{}' must be non-negative but was: {}",
527                name, self
528            )));
529        }
530        Ok(self)
531    }
532
533    #[inline]
534    fn require_negative(self, name: &str) -> ArgumentResult<Self> {
535        reject_nan(name, self)?;
536        if self >= T::zero() {
537            return Err(ArgumentError::new(format!(
538                "Parameter '{}' must be negative but was: {}",
539                name, self
540            )));
541        }
542        Ok(self)
543    }
544
545    #[inline]
546    fn require_non_positive(self, name: &str) -> ArgumentResult<Self> {
547        reject_nan(name, self)?;
548        if self > T::zero() {
549            return Err(ArgumentError::new(format!(
550                "Parameter '{}' must be non-positive but was: {}",
551                name, self
552            )));
553        }
554        Ok(self)
555    }
556
557    #[inline]
558    fn require_in_closed_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self> {
559        reject_nan(name, self)?;
560        reject_nan("min", min)?;
561        reject_nan("max", max)?;
562        if min > max {
563            return Err(ArgumentError::new(format!(
564                "Parameter '{}' has invalid range: min {} is greater than max {}",
565                name, min, max
566            )));
567        }
568        if self < min || self > max {
569            return Err(ArgumentError::new(format!(
570                "Parameter '{}' must be in range [{}, {}] but was: {}",
571                name, min, max, self
572            )));
573        }
574        Ok(self)
575    }
576
577    #[inline]
578    fn require_in_open_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self> {
579        reject_nan(name, self)?;
580        reject_nan("min", min)?;
581        reject_nan("max", max)?;
582        if min >= max {
583            return Err(ArgumentError::new(format!(
584                "Parameter '{}' has invalid range: min {} must be less than max {}",
585                name, min, max
586            )));
587        }
588        if self <= min || self >= max {
589            return Err(ArgumentError::new(format!(
590                "Parameter '{}' must be in range ({}, {}) but was: {}",
591                name, min, max, self
592            )));
593        }
594        Ok(self)
595    }
596
597    #[inline]
598    fn require_in_left_open_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self> {
599        reject_nan(name, self)?;
600        reject_nan("min", min)?;
601        reject_nan("max", max)?;
602        if min > max {
603            return Err(ArgumentError::new(format!(
604                "Parameter '{}' has invalid range: min {} is greater than max {}",
605                name, min, max
606            )));
607        }
608        if self <= min || self > max {
609            return Err(ArgumentError::new(format!(
610                "Parameter '{}' must be in range ({}, {}] but was: {}",
611                name, min, max, self
612            )));
613        }
614        Ok(self)
615    }
616
617    #[inline]
618    fn require_in_right_open_range(self, name: &str, min: Self, max: Self) -> ArgumentResult<Self> {
619        reject_nan(name, self)?;
620        reject_nan("min", min)?;
621        reject_nan("max", max)?;
622        if min > max {
623            return Err(ArgumentError::new(format!(
624                "Parameter '{}' has invalid range: min {} is greater than max {}",
625                name, min, max
626            )));
627        }
628        if self < min || self >= max {
629            return Err(ArgumentError::new(format!(
630                "Parameter '{}' must be in range [{}, {}) but was: {}",
631                name, min, max, self
632            )));
633        }
634        Ok(self)
635    }
636
637    #[inline]
638    fn require_less(self, name: &str, max: Self) -> ArgumentResult<Self> {
639        reject_nan(name, self)?;
640        reject_nan("max", max)?;
641        if self >= max {
642            return Err(ArgumentError::new(format!(
643                "Parameter '{}' must be less than {} but was: {}",
644                name, max, self
645            )));
646        }
647        Ok(self)
648    }
649
650    #[inline]
651    fn require_less_equal(self, name: &str, max: Self) -> ArgumentResult<Self> {
652        reject_nan(name, self)?;
653        reject_nan("max", max)?;
654        if self > max {
655            return Err(ArgumentError::new(format!(
656                "Parameter '{}' must be less than or equal to {} but was: {}",
657                name, max, self
658            )));
659        }
660        Ok(self)
661    }
662
663    #[inline]
664    fn require_greater(self, name: &str, min: Self) -> ArgumentResult<Self> {
665        reject_nan(name, self)?;
666        reject_nan("min", min)?;
667        if self <= min {
668            return Err(ArgumentError::new(format!(
669                "Parameter '{}' must be greater than {} but was: {}",
670                name, min, self
671            )));
672        }
673        Ok(self)
674    }
675
676    #[inline]
677    fn require_greater_equal(self, name: &str, min: Self) -> ArgumentResult<Self> {
678        reject_nan(name, self)?;
679        reject_nan("min", min)?;
680        if self < min {
681            return Err(ArgumentError::new(format!(
682                "Parameter '{}' must be greater than or equal to {} but was: {}",
683                name, min, self
684            )));
685        }
686        Ok(self)
687    }
688}
689
690/// Comparison argument validation
691///
692/// Provides comparison validation functionality between two arguments.
693///
694/// # Examples
695///
696/// ```rust
697/// use qubit_common::lang::argument::require_equal;
698///
699/// let result = require_equal("width", 100, "height", 100);
700/// assert!(result.is_ok());
701///
702/// let result = require_equal("width", 100, "height", 200);
703/// assert!(result.is_err());
704/// ```
705///
706/// # Author
707///
708/// Haixing Hu
709#[inline]
710pub fn require_equal<T>(name1: &str, value1: T, name2: &str, value2: T) -> ArgumentResult<()>
711where
712    T: PartialEq + Display,
713{
714    if value1 != value2 {
715        return Err(ArgumentError::new(format!(
716            "Parameter '{}' ({}) must equal parameter '{}' ({})",
717            name1, value1, name2, value2
718        )));
719    }
720    Ok(())
721}
722
723/// Validate that two arguments are not equal
724///
725/// # Parameters
726///
727/// * `name1` - First parameter name
728/// * `value1` - First parameter value
729/// * `name2` - Second parameter name
730/// * `value2` - Second parameter value
731///
732/// # Examples
733///
734/// ```rust
735/// use qubit_common::lang::argument::require_not_equal;
736///
737/// let result = require_not_equal("min", 0, "max", 100);
738/// assert!(result.is_ok());
739/// ```
740///
741/// # Author
742///
743/// Haixing Hu
744#[inline]
745pub fn require_not_equal<T>(name1: &str, value1: T, name2: &str, value2: T) -> ArgumentResult<()>
746where
747    T: PartialEq + Display,
748{
749    if value1 == value2 {
750        return Err(ArgumentError::new(format!(
751            "Parameters '{}' and '{}' cannot be equal (both are: {})",
752            name1, name2, value1
753        )));
754    }
755    Ok(())
756}