use core::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SurfaceError {
XBelow {
coordinate: u16,
bound: u16,
},
XAbove {
coordinate: u16,
bound: u16,
},
YBelow {
coordinate: u16,
bound: u16,
},
YAbove {
coordinate: u16,
bound: u16,
},
}
impl SurfaceError {
#[must_use]
pub const fn coordinate(&self) -> u16 {
match *self {
Self::XBelow { coordinate, .. }
| Self::XAbove { coordinate, .. }
| Self::YBelow { coordinate, .. }
| Self::YAbove { coordinate, .. } => coordinate,
}
}
#[must_use]
pub const fn bound(&self) -> u16 {
match *self {
Self::XBelow { bound, .. }
| Self::XAbove { bound, .. }
| Self::YBelow { bound, .. }
| Self::YAbove { bound, .. } => bound,
}
}
}
impl Display for SurfaceError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match *self {
Self::XBelow { coordinate, bound } => {
write!(
f,
"x coordinate {coordinate} is below the x axis minimum {bound}"
)
}
Self::XAbove { coordinate, bound } => {
write!(
f,
"x coordinate {coordinate} is above the x axis maximum {bound}"
)
}
Self::YBelow { coordinate, bound } => {
write!(
f,
"y coordinate {coordinate} is below the y axis minimum {bound}"
)
}
Self::YAbove { coordinate, bound } => {
write!(
f,
"y coordinate {coordinate} is above the y axis maximum {bound}"
)
}
}
}
}
impl core::error::Error for SurfaceError {}
#[cfg(test)]
mod tests {
use super::SurfaceError;
use core::fmt::Write;
struct Sink {
buffer: [u8; 96],
used: usize,
}
impl Sink {
const fn new() -> Self {
Self {
buffer: [0; 96],
used: 0,
}
}
fn as_str(&self) -> &str {
core::str::from_utf8(&self.buffer[..self.used]).expect("sink holds valid utf-8")
}
}
impl Write for Sink {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let bytes = s.as_bytes();
let end = self.used + bytes.len();
assert!(end <= self.buffer.len(), "sink overflowed");
self.buffer[self.used..end].copy_from_slice(bytes);
self.used = end;
Ok(())
}
}
fn rendered(error: SurfaceError) -> Sink {
let mut sink = Sink::new();
write!(sink, "{error}").expect("writing to the sink cannot fail");
sink
}
#[test]
fn each_side_reports_its_coordinate_and_bound() {
let cases = [
SurfaceError::XBelow {
coordinate: 3,
bound: 10,
},
SurfaceError::XAbove {
coordinate: 900,
bound: 500,
},
SurfaceError::YBelow {
coordinate: 0,
bound: 7,
},
SurfaceError::YAbove {
coordinate: u16::MAX,
bound: 1,
},
];
let expected = [(3, 10), (900, 500), (0, 7), (u16::MAX, 1)];
for (error, (coordinate, bound)) in cases.into_iter().zip(expected) {
assert_eq!(error.coordinate(), coordinate);
assert_eq!(error.bound(), bound);
}
}
#[test]
fn sides_are_not_equal_to_each_other() {
let x_below = SurfaceError::XBelow {
coordinate: 4,
bound: 9,
};
let x_above = SurfaceError::XAbove {
coordinate: 4,
bound: 9,
};
let y_below = SurfaceError::YBelow {
coordinate: 4,
bound: 9,
};
let y_above = SurfaceError::YAbove {
coordinate: 4,
bound: 9,
};
assert_ne!(x_below, x_above);
assert_ne!(x_below, y_below);
assert_ne!(x_above, y_above);
assert_ne!(y_below, y_above);
assert_eq!(
x_below,
SurfaceError::XBelow {
coordinate: 4,
bound: 9,
}
);
}
#[test]
fn display_names_the_axis_the_direction_and_the_bound() {
let x_below = rendered(SurfaceError::XBelow {
coordinate: 3,
bound: 10,
});
assert_eq!(
x_below.as_str(),
"x coordinate 3 is below the x axis minimum 10"
);
let x_above = rendered(SurfaceError::XAbove {
coordinate: 900,
bound: 500,
});
assert_eq!(
x_above.as_str(),
"x coordinate 900 is above the x axis maximum 500"
);
let y_below = rendered(SurfaceError::YBelow {
coordinate: 3,
bound: 10,
});
assert_eq!(
y_below.as_str(),
"y coordinate 3 is below the y axis minimum 10"
);
let y_above = rendered(SurfaceError::YAbove {
coordinate: 900,
bound: 500,
});
assert_eq!(
y_above.as_str(),
"y coordinate 900 is above the y axis maximum 500"
);
}
#[test]
fn the_error_has_no_source() {
let error = SurfaceError::XBelow {
coordinate: 1,
bound: 2,
};
assert!(core::error::Error::source(&error).is_none());
}
}