#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Boundary {
#[default]
Error,
Clamp,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct BoundaryPolicy {
x_below: Boundary,
x_above: Boundary,
y_below: Boundary,
y_above: Boundary,
}
impl BoundaryPolicy {
#[must_use]
pub const fn new() -> Self {
Self {
x_below: Boundary::Error,
x_above: Boundary::Error,
y_below: Boundary::Error,
y_above: Boundary::Error,
}
}
#[must_use]
pub const fn with_x_below(self, boundary: Boundary) -> Self {
Self {
x_below: boundary,
..self
}
}
#[must_use]
pub const fn with_x_above(self, boundary: Boundary) -> Self {
Self {
x_above: boundary,
..self
}
}
#[must_use]
pub const fn with_y_below(self, boundary: Boundary) -> Self {
Self {
y_below: boundary,
..self
}
}
#[must_use]
pub const fn with_y_above(self, boundary: Boundary) -> Self {
Self {
y_above: boundary,
..self
}
}
#[must_use]
pub const fn x_below(&self) -> Boundary {
self.x_below
}
#[must_use]
pub const fn x_above(&self) -> Boundary {
self.x_above
}
#[must_use]
pub const fn y_below(&self) -> Boundary {
self.y_below
}
#[must_use]
pub const fn y_above(&self) -> Boundary {
self.y_above
}
}
#[cfg(test)]
mod tests {
use super::{Boundary, BoundaryPolicy};
use core::mem::size_of;
#[test]
fn default_policy_rejects_every_side() {
let policy = BoundaryPolicy::default();
assert_eq!(policy.x_below(), Boundary::Error);
assert_eq!(policy.x_above(), Boundary::Error);
assert_eq!(policy.y_below(), Boundary::Error);
assert_eq!(policy.y_above(), Boundary::Error);
}
#[test]
fn default_trait_matches_the_const_constructor() {
assert_eq!(BoundaryPolicy::default(), BoundaryPolicy::new());
assert_eq!(Boundary::default(), Boundary::Error);
}
#[test]
fn each_builder_method_changes_only_its_own_side() {
let base = BoundaryPolicy::new();
let x_below = base.with_x_below(Boundary::Clamp);
assert_eq!(x_below.x_below(), Boundary::Clamp);
assert_eq!(x_below.x_above(), Boundary::Error);
assert_eq!(x_below.y_below(), Boundary::Error);
assert_eq!(x_below.y_above(), Boundary::Error);
let x_above = base.with_x_above(Boundary::Clamp);
assert_eq!(x_above.x_below(), Boundary::Error);
assert_eq!(x_above.x_above(), Boundary::Clamp);
assert_eq!(x_above.y_below(), Boundary::Error);
assert_eq!(x_above.y_above(), Boundary::Error);
let y_below = base.with_y_below(Boundary::Clamp);
assert_eq!(y_below.x_below(), Boundary::Error);
assert_eq!(y_below.x_above(), Boundary::Error);
assert_eq!(y_below.y_below(), Boundary::Clamp);
assert_eq!(y_below.y_above(), Boundary::Error);
let y_above = base.with_y_above(Boundary::Clamp);
assert_eq!(y_above.x_below(), Boundary::Error);
assert_eq!(y_above.x_above(), Boundary::Error);
assert_eq!(y_above.y_below(), Boundary::Error);
assert_eq!(y_above.y_above(), Boundary::Clamp);
}
#[test]
fn builders_are_usable_in_const_context() {
const POLICY: BoundaryPolicy = BoundaryPolicy::new()
.with_x_below(Boundary::Clamp)
.with_x_above(Boundary::Clamp)
.with_y_below(Boundary::Clamp)
.with_y_above(Boundary::Clamp);
assert_eq!(POLICY.x_below(), Boundary::Clamp);
assert_eq!(POLICY.x_above(), Boundary::Clamp);
assert_eq!(POLICY.y_below(), Boundary::Clamp);
assert_eq!(POLICY.y_above(), Boundary::Clamp);
}
#[test]
fn the_policy_is_four_single_byte_selections() {
assert_eq!(size_of::<Boundary>(), 1);
assert_eq!(size_of::<BoundaryPolicy>(), 4);
}
}