twine_core/constraint/non_zero.rs
1use std::cmp::Ordering;
2
3use num_traits::Zero;
4
5use super::{Constrained, Constraint, ConstraintError};
6
7/// Marker type enforcing that a value is non-zero (not equal to zero).
8///
9/// Use this type with [`Constrained<T, NonZero>`] to encode a non-zero
10/// constraint at the type level.
11///
12/// You can construct a value constrained to be non-zero using either the
13/// generic [`Constrained::new`] method or the convenient [`NonZero::new`]
14/// associated function.
15///
16/// # Examples
17///
18/// ```
19/// use twine_core::constraint::{Constrained, NonZero};
20///
21/// // Generic constructor:
22/// let x = Constrained::<_, NonZero>::new(1).unwrap();
23/// assert_eq!(x.into_inner(), 1);
24///
25/// // Associated constructor:
26/// let y = NonZero::new(-5.0).unwrap();
27/// assert_eq!(y.into_inner(), -5.0);
28///
29/// // Error cases:
30/// assert!(NonZero::new(0).is_err());
31/// assert!(NonZero::new(0.0).is_err());
32/// assert!(NonZero::new(f64::NAN).is_err());
33/// ```
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct NonZero;
36
37impl NonZero {
38 /// Constructs a [`Constrained<T, NonZero>`] if the value is not zero.
39 ///
40 /// # Errors
41 ///
42 /// Returns an error if the value is zero or not a number (`NaN`).
43 pub fn new<T: PartialOrd + Zero>(value: T) -> Result<Constrained<T, NonZero>, ConstraintError> {
44 Constrained::<T, NonZero>::new(value)
45 }
46}
47
48impl<T: PartialOrd + Zero> Constraint<T> for NonZero {
49 fn check(value: &T) -> Result<(), ConstraintError> {
50 match value.partial_cmp(&T::zero()) {
51 Some(Ordering::Greater | Ordering::Less) => Ok(()),
52 Some(Ordering::Equal) => Err(ConstraintError::Zero),
53 None => Err(ConstraintError::NotANumber),
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn integers() {
64 let one = Constrained::<_, NonZero>::new(1).unwrap();
65 assert_eq!(one.into_inner(), 1);
66
67 let neg_one = NonZero::new(-1).unwrap();
68 assert_eq!(neg_one.as_ref(), &-1);
69
70 assert!(NonZero::new(0).is_err());
71 }
72
73 #[test]
74 fn floats() {
75 assert!(Constrained::<f64, NonZero>::new(2.0).is_ok());
76 assert!(NonZero::new(-3.5).is_ok());
77 assert!(NonZero::new(0.0).is_err());
78 assert!(NonZero::new(f64::NAN).is_err());
79 }
80}