use std::{cmp::Ordering, marker::PhantomData, ops::Add};
use num_traits::Zero;
use super::{Constrained, Constraint, ConstraintError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct StrictlyPositive;
impl StrictlyPositive {
pub fn new<T: PartialOrd + Zero>(
value: T,
) -> Result<Constrained<T, StrictlyPositive>, ConstraintError> {
Constrained::<T, StrictlyPositive>::new(value)
}
}
impl<T: PartialOrd + Zero> Constraint<T> for StrictlyPositive {
fn check(value: &T) -> Result<(), ConstraintError> {
match value.partial_cmp(&T::zero()) {
Some(Ordering::Greater) => Ok(()),
Some(Ordering::Equal) => Err(ConstraintError::Zero),
Some(Ordering::Less) => Err(ConstraintError::Negative),
None => Err(ConstraintError::NotANumber),
}
}
}
impl<T> Add for Constrained<T, StrictlyPositive>
where
T: Add<Output = T> + PartialOrd + Zero,
{
type Output = Self;
fn add(self, rhs: Self) -> Self {
let value = self.value + rhs.value;
debug_assert!(
value > T::zero(),
"Addition produced a non-positive value, violating StrictlyPositive bound invariant"
);
Self {
value,
_marker: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use uom::si::{f64::MassRate, mass_rate::kilogram_per_second};
#[test]
fn integers() {
let x = Constrained::<i32, StrictlyPositive>::new(1).unwrap();
assert_eq!(x.into_inner(), 1);
let y = StrictlyPositive::new(42).unwrap();
assert_eq!(y.as_ref(), &42);
assert!(StrictlyPositive::new(0).is_err());
assert!(StrictlyPositive::new(-2).is_err());
}
#[test]
fn floats() {
assert!(Constrained::<f64, StrictlyPositive>::new(1.0).is_ok());
assert!(StrictlyPositive::new(0.1).is_ok());
assert!(StrictlyPositive::new(0.0).is_err());
assert!(StrictlyPositive::new(-5.0).is_err());
assert!(StrictlyPositive::new(f64::NAN).is_err());
}
#[test]
fn mass_rates() {
let mass_rate = MassRate::new::<kilogram_per_second>(5.0);
assert!(StrictlyPositive::new(mass_rate).is_ok());
let mass_rate = MassRate::new::<kilogram_per_second>(0.0);
assert!(StrictlyPositive::new(mass_rate).is_err());
let mass_rate = MassRate::new::<kilogram_per_second>(-2.0);
assert!(StrictlyPositive::new(mass_rate).is_err());
}
}