use std::ops::Index;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Dir {
S,
E,
N,
W,
}
macro_rules! match_enum_transform {
($match:expr, $enum:ident, [$($v1:ident : $v2:ident),+]) => {
match $match {
$($enum::$v1 => $enum::$v2,)+
}
};
}
impl Dir {
#[inline]
#[must_use]
pub const fn opposite(self) -> Self {
match_enum_transform!(self, Dir, [S:N, E:W, N:S, W:E])
}
#[inline]
pub const fn opposite_mut(&mut self) {
*self = self.opposite();
}
#[inline]
#[must_use]
pub const fn rotate_ccw(self) -> Self {
match_enum_transform!(self, Dir, [S:E, E:N, N:W, W:S])
}
#[inline]
pub const fn rotate_ccw_mut(&mut self) {
*self = self.rotate_ccw();
}
#[inline]
#[must_use]
pub const fn rotate_cw(self) -> Self {
match_enum_transform!(self, Dir, [S:W, E:S, N:E, W:N])
}
#[inline]
pub const fn rotate_cw_mut(&mut self) {
*self = self.rotate_cw();
}
}
impl From<usize> for Dir {
#[inline]
fn from(value: usize) -> Self {
match value {
0 => Self::S,
1 => Self::E,
2 => Self::N,
3 => Self::W,
_ => unreachable!("Invalid direction value"),
}
}
}
impl From<Dir> for usize {
#[inline]
fn from(value: Dir) -> Self {
value as Self
}
}
impl<T> Index<Dir> for [T] {
type Output = T;
#[inline]
fn index(&self, index: Dir) -> &Self::Output {
&self[index as usize]
}
}
#[cfg(test)]
mod tests {
use super::Dir::{self, *};
macro_rules! assert_dir {
([$($in:ident),*], [$($ex:ident),*], $func:ident) => {
let input = [$($in),*];
let expected = [$($ex),*];
let output = input.map(Dir::$func);
assert_eq!(expected, output, "\n input: {input:?}");
};
}
macro_rules! assert_dir_mut {
([$($in:ident),*], [$($ex:ident),*], $func:ident) => {
let input = [$($in),*];
let expected = [$($ex),*];
let mut output = input;
for dir in &mut output {
dir.$func();
}
assert_eq!(expected, output, "\n input: {input:?}");
};
}
#[test]
fn opposite() {
assert_dir!([S, E, N, W], [N, W, S, E], opposite);
}
#[test]
fn opposite_mut() {
assert_dir_mut!([S, E, N, W], [N, W, S, E], opposite_mut);
}
#[test]
fn rotate_ccw() {
assert_dir!([S, E, N, W], [E, N, W, S], rotate_ccw);
}
#[test]
fn rotate_ccw_mut() {
assert_dir_mut!([S, E, N, W], [E, N, W, S], rotate_ccw_mut);
}
#[test]
fn rotate_cw() {
assert_dir!([S, E, N, W], [W, S, E, N], rotate_cw);
}
#[test]
fn rotate_cw_mut() {
assert_dir_mut!([S, E, N, W], [W, S, E, N], rotate_cw_mut);
}
#[test]
fn from_usize() {
let input = [0, 1, 2, 3];
let expected = [S, E, N, W];
let output = input.map(Dir::from);
assert_eq!(expected, output, "\n input: {input:?}");
}
#[test]
#[should_panic = "Invalid direction"]
fn from_usize_panic() {
let _ = Dir::from(4);
}
#[test]
fn into_usize() {
let input = [S, E, N, W];
let expected = [0, 1, 2, 3];
let output = input.map(usize::from);
assert_eq!(expected, output, "\n input: {input:?}");
}
#[test]
fn index_array() {
let input = ([0, 1, 2, 3], [S, E, N, W]);
let expected = [0, 1, 2, 3];
let output = input.1.map(|dir| input.0[dir]);
assert_eq!(expected, output, "\n input: {input:?}");
}
}