use std::{cmp::Ordering, marker::PhantomData};
use crate::support::constraint::{Constrained, Constraint, ConstraintError};
use crate::support::constraint::UnitBounds;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct UnitIntervalUpperOpen;
impl UnitIntervalUpperOpen {
pub fn new<T: UnitBounds>(
value: T,
) -> Result<Constrained<T, UnitIntervalUpperOpen>, ConstraintError> {
Constrained::<T, UnitIntervalUpperOpen>::new(value)
}
#[must_use]
pub fn zero<T: UnitBounds>() -> Constrained<T, UnitIntervalUpperOpen> {
Constrained::<T, UnitIntervalUpperOpen> {
value: T::zero(),
_marker: PhantomData,
}
}
}
impl<T: UnitBounds> Constraint<T> for UnitIntervalUpperOpen {
fn check(value: &T) -> Result<(), ConstraintError> {
match (value.partial_cmp(&T::zero()), value.partial_cmp(&T::one())) {
(None, _) | (_, None) => Err(ConstraintError::NotANumber),
(Some(Ordering::Less), _) => Err(ConstraintError::BelowMinimum),
(_, Some(Ordering::Greater | Ordering::Equal)) => Err(ConstraintError::AboveMaximum),
_ => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
use crate::support::constraint::*;
use uom::si::{f64::Ratio, ratio::ratio};
#[test]
#[allow(clippy::float_cmp)]
fn floats_valid() {
assert!(Constrained::<f64, UnitIntervalUpperOpen>::new(0.0).is_ok());
assert!(Constrained::<f64, UnitIntervalUpperOpen>::new(0.9).is_ok());
assert!(UnitIntervalUpperOpen::new(0.5).is_ok());
let z = UnitIntervalUpperOpen::zero::<f64>();
assert_eq!(z.into_inner(), 0.0);
}
#[test]
fn floats_out_of_range() {
assert!(matches!(
UnitIntervalUpperOpen::new(-1.0),
Err(ConstraintError::BelowMinimum)
));
assert!(matches!(
UnitIntervalUpperOpen::new(1.0),
Err(ConstraintError::AboveMaximum)
));
assert!(matches!(
UnitIntervalUpperOpen::new(2.0),
Err(ConstraintError::AboveMaximum)
));
}
#[test]
fn floats_nan_is_not_a_number() {
assert!(matches!(
UnitIntervalUpperOpen::new(f64::NAN),
Err(ConstraintError::NotANumber)
));
}
#[test]
#[allow(clippy::float_cmp)]
fn uom_ratio_valid() {
assert!(Constrained::<Ratio, UnitIntervalUpperOpen>::new(Ratio::new::<ratio>(0.0)).is_ok());
assert!(
Constrained::<Ratio, UnitIntervalUpperOpen>::new(Ratio::new::<ratio>(0.99)).is_ok()
);
assert!(UnitIntervalUpperOpen::new(Ratio::new::<ratio>(0.5)).is_ok());
let z = UnitIntervalUpperOpen::zero::<Ratio>();
assert_eq!(z.into_inner().get::<ratio>(), 0.0);
}
#[test]
fn uom_ratio_out_of_range() {
assert!(matches!(
UnitIntervalUpperOpen::new(Ratio::new::<ratio>(-0.1)),
Err(ConstraintError::BelowMinimum)
));
assert!(matches!(
UnitIntervalUpperOpen::new(Ratio::new::<ratio>(1.0)),
Err(ConstraintError::AboveMaximum)
));
}
}