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 UnitIntervalLowerOpen;
impl UnitIntervalLowerOpen {
pub fn new<T: UnitBounds>(
value: T,
) -> Result<Constrained<T, UnitIntervalLowerOpen>, ConstraintError> {
Constrained::<T, UnitIntervalLowerOpen>::new(value)
}
#[must_use]
pub fn one<T: UnitBounds>() -> Constrained<T, UnitIntervalLowerOpen> {
Constrained::<T, UnitIntervalLowerOpen> {
value: T::one(),
_marker: PhantomData,
}
}
}
impl<T: UnitBounds> Constraint<T> for UnitIntervalLowerOpen {
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 | Ordering::Equal), _) => Err(ConstraintError::BelowMinimum),
(_, Some(Ordering::Greater)) => 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, UnitIntervalLowerOpen>::new(0.1).is_ok());
assert!(Constrained::<f64, UnitIntervalLowerOpen>::new(1.0).is_ok());
assert!(UnitIntervalLowerOpen::new(0.75).is_ok());
let o = UnitIntervalLowerOpen::one::<f64>();
assert_eq!(o.into_inner(), 1.0);
}
#[test]
fn floats_out_of_range() {
assert!(matches!(
UnitIntervalLowerOpen::new(0.0),
Err(ConstraintError::BelowMinimum)
));
assert!(matches!(
UnitIntervalLowerOpen::new(-1.0),
Err(ConstraintError::BelowMinimum)
));
assert!(matches!(
UnitIntervalLowerOpen::new(1.000_000_1),
Err(ConstraintError::AboveMaximum)
));
}
#[test]
fn floats_nan_is_not_a_number() {
assert!(matches!(
UnitIntervalLowerOpen::new(f64::NAN),
Err(ConstraintError::NotANumber)
));
}
#[test]
#[allow(clippy::float_cmp)]
fn uom_ratio_valid() {
assert!(
Constrained::<Ratio, UnitIntervalLowerOpen>::new(Ratio::new::<ratio>(0.01)).is_ok()
);
assert!(Constrained::<Ratio, UnitIntervalLowerOpen>::new(Ratio::new::<ratio>(1.0)).is_ok());
assert!(UnitIntervalLowerOpen::new(Ratio::new::<ratio>(0.5)).is_ok());
let o = UnitIntervalLowerOpen::one::<Ratio>();
assert_eq!(o.into_inner().get::<ratio>(), 1.0);
}
#[test]
fn uom_ratio_out_of_range() {
assert!(matches!(
UnitIntervalLowerOpen::new(Ratio::new::<ratio>(0.0)),
Err(ConstraintError::BelowMinimum)
));
assert!(matches!(
UnitIntervalLowerOpen::new(Ratio::new::<ratio>(1.1)),
Err(ConstraintError::AboveMaximum)
));
}
}