1use core::fmt::{Debug, Display};
6
7pub fn assert_eq<T: Copy + Eq>(actual: &T, expected: &T) -> Result<T, Mismatch<T>> {
31 if expected.eq(&actual) {
32 Ok(*expected)
33 } else {
34 Err(Mismatch {
35 expected: *expected,
36 actual: *actual,
37 })
38 }
39}
40
41pub fn assert_in<T: Ord + Copy>(value: &T, range: &core::ops::Range<T>) -> Result<T, Outside<T>> {
66 if value > &range.start && value <= &range.end {
67 Ok(*value)
68 } else {
69 Err(Outside {
71 range: range.clone(),
72 value: *value,
73 })
74 }
75}
76
77#[derive(PartialEq, Eq, Clone, Copy)]
80pub struct Mismatch<T: Copy + Eq> {
81 pub(crate) expected: T,
83 pub(crate) actual: T,
85}
86
87impl<T: Debug + Copy + Eq> Debug for Mismatch<T> {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("Mismatch")
90 .field("expected", &self.expected)
91 .field("actual", &self.actual)
92 .finish()
93 }
94}
95
96impl<T: Display + Copy + Eq> Display for Mismatch<T> {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 write!(f, "Expected {}, but got {}", self.expected, self.actual)
99 }
100}
101
102#[derive(Clone)]
105pub struct Outside<T: Ord + Copy> {
106 pub(crate) range: core::ops::Range<T>,
108 pub(crate) value: T,
110}
111
112impl<T: Ord + Copy + Debug> Debug for Outside<T> {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 f.debug_struct("Outside")
115 .field("range", &self.range)
116 .field("value", &self.value)
117 .finish()
118 }
119}
120
121impl<T: Ord + Copy + Display> Display for Outside<T> {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 if self.value >= self.range.end {
124 write!(f, "Value {} exceeds maximum {}", self.value, self.range.end)
125 } else if self.value < self.range.start {
126 write!(f, "Value {} below minimum {}", self.value, self.range.start)
127 } else {
128 panic!("An invalid instance of outside was created. Aborting")
129 }
130 }
131}
132
133impl<T: PartialEq + Ord + Copy> PartialEq for Outside<T> {
134 fn eq(&self, other: &Self) -> bool {
135 self.range == other.range && self.value == other.value
136 }
137}
138
139#[derive(PartialEq, Eq, Clone)]
141pub struct Unknown<'a, T: Eq>{
142 pub(crate) knowns: Option<&'a [T]>,
144 pub(crate) value: T,
146}
147
148impl<'a, T: Eq + Copy> Copy for Unknown<'a, T> {}
149
150impl<T: Eq + Debug> Debug for Unknown<'_, T> {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 f.debug_struct("Unknown")
153 .field("knowns", &self.knowns)
154 .field("value", &self.value)
155 .finish()
156 }
157}
158
159impl<T: Eq + Display> Display for Unknown<'_, T> {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 write!(f, "The value {} is not known", self.value)?;
162 if let Some(knowns) = self.knowns {
163 write!(f, "Because it's not one of [{}]", join(&knowns, ", ")?)
164 } else {
165 f.write_str(".")
166 }
167 }
168}
169
170pub fn join<T: Display>(items: &[T], separator: &'static str) -> Result<String, core::fmt::Error> {
171 use core::fmt::Write;
172
173 let first_element = items[0].to_string();
174 let mut buffer = String::with_capacity(
175 (items.len() - 1) * (separator.len() + first_element.len()) + first_element.len(),
176 );
177 for idx in 1..items.len() {
178 buffer.push_str(separator);
179 buffer.write_str(&items[idx].to_string())?;
180 }
181 Ok(buffer)
182}
183
184pub fn assert_known_enum<'a, T: Eq>(knowns: &'a [T], value: T) -> Result<T, Unknown<'a, T>> {
185 if knowns.contains(&value) {
186 Ok(value)
187 } else {
188 Err(Unknown {
189 knowns: Some(knowns),
190 value,
191 })
192 }
193}
194
195pub fn assert_known<'a, T: Eq>(knowns: &'a [T], value: T) -> Result<T, Unknown<'_, T>> {
196 if knowns.contains(&value) {
197 Ok(value)
198 } else {
199 Err(Unknown {
200 knowns: None,
201 value,
202 })
203 }
204}
205
206#[cfg(test)]
207pub mod tests {
208 use crate::{Mismatch, Outside, Unknown};
209
210 #[test]
211 fn usage_of_assert_eq() {
212 assert_eq!(crate::assert_eq(&32_u32, &32), Ok(32));
213 assert_eq!(
214 crate::assert_eq(&32_u32, &33),
215 Err(Mismatch {
216 expected: 32,
217 actual: 33
218 })
219 );
220 }
221
222 #[test]
223 fn usage_of_outside() {
224 assert_eq!(crate::assert_in(&2, &(1..5)), Ok(2));
225 assert_eq!(crate::assert_in(&5, &(1..5)), Ok(5));
226 assert_eq!(
227 crate::assert_in(&6, &(1..5)),
228 Err(Outside {
229 range: 1..5,
230 value: 6
231 })
232 );
233 assert_eq!(
234 crate::assert_in(&0, &(1..5)),
235 Err(Outside {
236 range: 1..5,
237 value: 0
238 })
239 );
240 }
241
242 #[test]
243 fn usage_of_unknown() {
244 let knowns = vec![1, 2, 4, 6, 7, 20_u32];
245 assert_eq!(crate::assert_known_enum(&knowns, 2), Ok(2));
246 assert_eq!(
247 crate::assert_known_enum(&knowns, 3),
248 Err(Unknown {
249 knowns: Some(&knowns),
250 value: 3
251 })
252 );
253 assert_eq!(crate::assert_known(&knowns, 2), Ok(2));
254 assert_eq!(
255 crate::assert_known(&knowns, 3),
256 Err(Unknown {
257 knowns: None,
258 value: 3
259 })
260 );
261 }
262}