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