twine_models/support/constraint/unit_interval/
upper_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 UnitIntervalUpperOpen;
55
56impl UnitIntervalUpperOpen {
57 pub fn new<T: UnitBounds>(
67 value: T,
68 ) -> Result<Constrained<T, UnitIntervalUpperOpen>, ConstraintError> {
69 Constrained::<T, UnitIntervalUpperOpen>::new(value)
70 }
71
72 #[must_use]
74 pub fn zero<T: UnitBounds>() -> Constrained<T, UnitIntervalUpperOpen> {
75 Constrained::<T, UnitIntervalUpperOpen> {
76 value: T::zero(),
77 _marker: PhantomData,
78 }
79 }
80}
81
82impl<T: UnitBounds> Constraint<T> for UnitIntervalUpperOpen {
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), _) => Err(ConstraintError::BelowMinimum),
87 (_, Some(Ordering::Greater | Ordering::Equal)) => 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, UnitIntervalUpperOpen>::new(0.0).is_ok());
103 assert!(Constrained::<f64, UnitIntervalUpperOpen>::new(0.9).is_ok());
104 assert!(UnitIntervalUpperOpen::new(0.5).is_ok());
105
106 let z = UnitIntervalUpperOpen::zero::<f64>();
107 assert_eq!(z.into_inner(), 0.0);
108 }
109
110 #[test]
111 fn floats_out_of_range() {
112 assert!(matches!(
113 UnitIntervalUpperOpen::new(-1.0),
114 Err(ConstraintError::BelowMinimum)
115 ));
116 assert!(matches!(
117 UnitIntervalUpperOpen::new(1.0),
118 Err(ConstraintError::AboveMaximum)
119 ));
120 assert!(matches!(
121 UnitIntervalUpperOpen::new(2.0),
122 Err(ConstraintError::AboveMaximum)
123 ));
124 }
125
126 #[test]
127 fn floats_nan_is_not_a_number() {
128 assert!(matches!(
129 UnitIntervalUpperOpen::new(f64::NAN),
130 Err(ConstraintError::NotANumber)
131 ));
132 }
133
134 #[test]
135 #[allow(clippy::float_cmp)]
136 fn uom_ratio_valid() {
137 assert!(Constrained::<Ratio, UnitIntervalUpperOpen>::new(Ratio::new::<ratio>(0.0)).is_ok());
138 assert!(
139 Constrained::<Ratio, UnitIntervalUpperOpen>::new(Ratio::new::<ratio>(0.99)).is_ok()
140 );
141 assert!(UnitIntervalUpperOpen::new(Ratio::new::<ratio>(0.5)).is_ok());
142
143 let z = UnitIntervalUpperOpen::zero::<Ratio>();
144 assert_eq!(z.into_inner().get::<ratio>(), 0.0);
145 }
146
147 #[test]
148 fn uom_ratio_out_of_range() {
149 assert!(matches!(
150 UnitIntervalUpperOpen::new(Ratio::new::<ratio>(-0.1)),
151 Err(ConstraintError::BelowMinimum)
152 ));
153 assert!(matches!(
154 UnitIntervalUpperOpen::new(Ratio::new::<ratio>(1.0)),
155 Err(ConstraintError::AboveMaximum)
156 ));
157 }
158}