twine_models/support/constraint/unit_interval/
lower_open.rs1use std::{cmp::Ordering, marker::PhantomData};
2
3use crate::support::constraint::{Constrained, Constraint, ConstraintError};
4
5use crate::support::constraint::UnitBounds;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
54pub struct UnitIntervalLowerOpen;
55
56impl UnitIntervalLowerOpen {
57 pub fn new<T: UnitBounds>(
67 value: T,
68 ) -> Result<Constrained<T, UnitIntervalLowerOpen>, ConstraintError> {
69 Constrained::<T, UnitIntervalLowerOpen>::new(value)
70 }
71
72 #[must_use]
74 pub fn one<T: UnitBounds>() -> Constrained<T, UnitIntervalLowerOpen> {
75 Constrained::<T, UnitIntervalLowerOpen> {
76 value: T::one(),
77 _marker: PhantomData,
78 }
79 }
80}
81
82impl<T: UnitBounds> Constraint<T> for UnitIntervalLowerOpen {
83 fn check(value: &T) -> Result<(), ConstraintError> {
84 match (value.partial_cmp(&T::zero()), value.partial_cmp(&T::one())) {
85 (None, _) | (_, None) => Err(ConstraintError::NotANumber),
86 (Some(Ordering::Less | Ordering::Equal), _) => Err(ConstraintError::BelowMinimum),
87 (_, Some(Ordering::Greater)) => Err(ConstraintError::AboveMaximum),
88 _ => Ok(()),
89 }
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use crate::support::constraint::*;
96
97 use uom::si::{f64::Ratio, ratio::ratio};
98
99 #[test]
100 #[allow(clippy::float_cmp)]
101 fn floats_valid() {
102 assert!(Constrained::<f64, UnitIntervalLowerOpen>::new(0.1).is_ok());
103 assert!(Constrained::<f64, UnitIntervalLowerOpen>::new(1.0).is_ok());
104 assert!(UnitIntervalLowerOpen::new(0.75).is_ok());
105
106 let o = UnitIntervalLowerOpen::one::<f64>();
107 assert_eq!(o.into_inner(), 1.0);
108 }
109
110 #[test]
111 fn floats_out_of_range() {
112 assert!(matches!(
113 UnitIntervalLowerOpen::new(0.0),
114 Err(ConstraintError::BelowMinimum)
115 ));
116 assert!(matches!(
117 UnitIntervalLowerOpen::new(-1.0),
118 Err(ConstraintError::BelowMinimum)
119 ));
120 assert!(matches!(
121 UnitIntervalLowerOpen::new(1.000_000_1),
122 Err(ConstraintError::AboveMaximum)
123 ));
124 }
125
126 #[test]
127 fn floats_nan_is_not_a_number() {
128 assert!(matches!(
129 UnitIntervalLowerOpen::new(f64::NAN),
130 Err(ConstraintError::NotANumber)
131 ));
132 }
133
134 #[test]
135 #[allow(clippy::float_cmp)]
136 fn uom_ratio_valid() {
137 assert!(
138 Constrained::<Ratio, UnitIntervalLowerOpen>::new(Ratio::new::<ratio>(0.01)).is_ok()
139 );
140 assert!(Constrained::<Ratio, UnitIntervalLowerOpen>::new(Ratio::new::<ratio>(1.0)).is_ok());
141 assert!(UnitIntervalLowerOpen::new(Ratio::new::<ratio>(0.5)).is_ok());
142
143 let o = UnitIntervalLowerOpen::one::<Ratio>();
144 assert_eq!(o.into_inner().get::<ratio>(), 1.0);
145 }
146
147 #[test]
148 fn uom_ratio_out_of_range() {
149 assert!(matches!(
150 UnitIntervalLowerOpen::new(Ratio::new::<ratio>(0.0)),
151 Err(ConstraintError::BelowMinimum)
152 ));
153 assert!(matches!(
154 UnitIntervalLowerOpen::new(Ratio::new::<ratio>(1.1)),
155 Err(ConstraintError::AboveMaximum)
156 ));
157 }
158}