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