1use core::fmt::{Display, Formatter, Result as FmtResult};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum SurfaceError {
19 XBelow {
21 coordinate: u16,
23 bound: u16,
25 },
26 XAbove {
28 coordinate: u16,
30 bound: u16,
32 },
33 YBelow {
35 coordinate: u16,
37 bound: u16,
39 },
40 YAbove {
42 coordinate: u16,
44 bound: u16,
46 },
47}
48
49impl SurfaceError {
50 #[must_use]
66 pub const fn coordinate(&self) -> u16 {
67 match *self {
68 Self::XBelow { coordinate, .. }
69 | Self::XAbove { coordinate, .. }
70 | Self::YBelow { coordinate, .. }
71 | Self::YAbove { coordinate, .. } => coordinate,
72 }
73 }
74
75 #[must_use]
80 pub const fn bound(&self) -> u16 {
81 match *self {
82 Self::XBelow { bound, .. }
83 | Self::XAbove { bound, .. }
84 | Self::YBelow { bound, .. }
85 | Self::YAbove { bound, .. } => bound,
86 }
87 }
88}
89
90impl Display for SurfaceError {
91 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
92 match *self {
93 Self::XBelow { coordinate, bound } => {
94 write!(
95 f,
96 "x coordinate {coordinate} is below the x axis minimum {bound}"
97 )
98 }
99 Self::XAbove { coordinate, bound } => {
100 write!(
101 f,
102 "x coordinate {coordinate} is above the x axis maximum {bound}"
103 )
104 }
105 Self::YBelow { coordinate, bound } => {
106 write!(
107 f,
108 "y coordinate {coordinate} is below the y axis minimum {bound}"
109 )
110 }
111 Self::YAbove { coordinate, bound } => {
112 write!(
113 f,
114 "y coordinate {coordinate} is above the y axis maximum {bound}"
115 )
116 }
117 }
118 }
119}
120
121impl core::error::Error for SurfaceError {}
122
123#[cfg(test)]
124mod tests {
125 use super::SurfaceError;
126 use core::fmt::Write;
127
128 struct Sink {
133 buffer: [u8; 96],
134 used: usize,
135 }
136
137 impl Sink {
138 const fn new() -> Self {
139 Self {
140 buffer: [0; 96],
141 used: 0,
142 }
143 }
144
145 fn as_str(&self) -> &str {
146 core::str::from_utf8(&self.buffer[..self.used]).expect("sink holds valid utf-8")
147 }
148 }
149
150 impl Write for Sink {
151 fn write_str(&mut self, s: &str) -> core::fmt::Result {
152 let bytes = s.as_bytes();
153 let end = self.used + bytes.len();
154 assert!(end <= self.buffer.len(), "sink overflowed");
155 self.buffer[self.used..end].copy_from_slice(bytes);
156 self.used = end;
157 Ok(())
158 }
159 }
160
161 fn rendered(error: SurfaceError) -> Sink {
162 let mut sink = Sink::new();
163 write!(sink, "{error}").expect("writing to the sink cannot fail");
164 sink
165 }
166
167 #[test]
168 fn each_side_reports_its_coordinate_and_bound() {
169 let cases = [
170 SurfaceError::XBelow {
171 coordinate: 3,
172 bound: 10,
173 },
174 SurfaceError::XAbove {
175 coordinate: 900,
176 bound: 500,
177 },
178 SurfaceError::YBelow {
179 coordinate: 0,
180 bound: 7,
181 },
182 SurfaceError::YAbove {
183 coordinate: u16::MAX,
184 bound: 1,
185 },
186 ];
187 let expected = [(3, 10), (900, 500), (0, 7), (u16::MAX, 1)];
188
189 for (error, (coordinate, bound)) in cases.into_iter().zip(expected) {
190 assert_eq!(error.coordinate(), coordinate);
191 assert_eq!(error.bound(), bound);
192 }
193 }
194
195 #[test]
196 fn sides_are_not_equal_to_each_other() {
197 let x_below = SurfaceError::XBelow {
198 coordinate: 4,
199 bound: 9,
200 };
201 let x_above = SurfaceError::XAbove {
202 coordinate: 4,
203 bound: 9,
204 };
205 let y_below = SurfaceError::YBelow {
206 coordinate: 4,
207 bound: 9,
208 };
209 let y_above = SurfaceError::YAbove {
210 coordinate: 4,
211 bound: 9,
212 };
213
214 assert_ne!(x_below, x_above);
215 assert_ne!(x_below, y_below);
216 assert_ne!(x_above, y_above);
217 assert_ne!(y_below, y_above);
218 assert_eq!(
219 x_below,
220 SurfaceError::XBelow {
221 coordinate: 4,
222 bound: 9,
223 }
224 );
225 }
226
227 #[test]
228 fn display_names_the_axis_the_direction_and_the_bound() {
229 let x_below = rendered(SurfaceError::XBelow {
230 coordinate: 3,
231 bound: 10,
232 });
233 assert_eq!(
234 x_below.as_str(),
235 "x coordinate 3 is below the x axis minimum 10"
236 );
237
238 let x_above = rendered(SurfaceError::XAbove {
239 coordinate: 900,
240 bound: 500,
241 });
242 assert_eq!(
243 x_above.as_str(),
244 "x coordinate 900 is above the x axis maximum 500"
245 );
246
247 let y_below = rendered(SurfaceError::YBelow {
248 coordinate: 3,
249 bound: 10,
250 });
251 assert_eq!(
252 y_below.as_str(),
253 "y coordinate 3 is below the y axis minimum 10"
254 );
255
256 let y_above = rendered(SurfaceError::YAbove {
257 coordinate: 900,
258 bound: 500,
259 });
260 assert_eq!(
261 y_above.as_str(),
262 "y coordinate 900 is above the y axis maximum 500"
263 );
264 }
265
266 #[test]
267 fn the_error_has_no_source() {
268 let error = SurfaceError::XBelow {
269 coordinate: 1,
270 bound: 2,
271 };
272
273 assert!(core::error::Error::source(&error).is_none());
274 }
275}