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}