1use std::ops::Index;
2
3#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
5pub enum Dir {
6 S,
8
9 E,
11
12 N,
14
15 W,
17}
18
19macro_rules! match_enum_transform {
21 ($match:expr, $enum:ident, [$($v1:ident : $v2:ident),+]) => {
22 match $match {
23 $($enum::$v1 => $enum::$v2,)+
24 }
25 };
26}
27
28impl Dir {
29 #[inline]
31 #[must_use]
32 pub const fn opposite(self) -> Self {
33 match_enum_transform!(self, Dir, [S:N, E:W, N:S, W:E])
34 }
35
36 #[inline]
38 pub const fn opposite_mut(&mut self) {
39 *self = self.opposite();
40 }
41
42 #[inline]
44 #[must_use]
45 pub const fn rotate_ccw(self) -> Self {
46 match_enum_transform!(self, Dir, [S:E, E:N, N:W, W:S])
47 }
48
49 #[inline]
51 pub const fn rotate_ccw_mut(&mut self) {
52 *self = self.rotate_ccw();
53 }
54
55 #[inline]
57 #[must_use]
58 pub const fn rotate_cw(self) -> Self {
59 match_enum_transform!(self, Dir, [S:W, E:S, N:E, W:N])
60 }
61
62 #[inline]
64 pub const fn rotate_cw_mut(&mut self) {
65 *self = self.rotate_cw();
66 }
67}
68
69impl From<usize> for Dir {
73 #[inline]
74 fn from(value: usize) -> Self {
75 match value {
76 0 => Self::S,
77 1 => Self::E,
78 2 => Self::N,
79 3 => Self::W,
80 _ => unreachable!("Invalid direction value"),
81 }
82 }
83}
84
85impl From<Dir> for usize {
86 #[inline]
87 fn from(value: Dir) -> Self {
88 value as Self
89 }
90}
91
92impl<T> Index<Dir> for [T] {
93 type Output = T;
94
95 #[inline]
96 fn index(&self, index: Dir) -> &Self::Output {
97 &self[index as usize]
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::Dir::{self, *};
104
105 macro_rules! assert_dir {
106 ([$($in:ident),*], [$($ex:ident),*], $func:ident) => {
107 let input = [$($in),*];
108 let expected = [$($ex),*];
109 let output = input.map(Dir::$func);
110 assert_eq!(expected, output, "\n input: {input:?}");
111 };
112 }
113
114 macro_rules! assert_dir_mut {
115 ([$($in:ident),*], [$($ex:ident),*], $func:ident) => {
116 let input = [$($in),*];
117 let expected = [$($ex),*];
118 let mut output = input;
119 for dir in &mut output {
120 dir.$func();
121 }
122 assert_eq!(expected, output, "\n input: {input:?}");
123 };
124 }
125
126 #[test]
130 fn opposite() {
131 assert_dir!([S, E, N, W], [N, W, S, E], opposite);
132 }
133
134 #[test]
135 fn opposite_mut() {
136 assert_dir_mut!([S, E, N, W], [N, W, S, E], opposite_mut);
137 }
138
139 #[test]
143 fn rotate_ccw() {
144 assert_dir!([S, E, N, W], [E, N, W, S], rotate_ccw);
145 }
146
147 #[test]
148 fn rotate_ccw_mut() {
149 assert_dir_mut!([S, E, N, W], [E, N, W, S], rotate_ccw_mut);
150 }
151
152 #[test]
153 fn rotate_cw() {
154 assert_dir!([S, E, N, W], [W, S, E, N], rotate_cw);
155 }
156
157 #[test]
158 fn rotate_cw_mut() {
159 assert_dir_mut!([S, E, N, W], [W, S, E, N], rotate_cw_mut);
160 }
161
162 #[test]
166 fn from_usize() {
167 let input = [0, 1, 2, 3];
168 let expected = [S, E, N, W];
169 let output = input.map(Dir::from);
170 assert_eq!(expected, output, "\n input: {input:?}");
171 }
172
173 #[test]
174 #[should_panic = "Invalid direction"]
175 fn from_usize_panic() {
176 let _ = Dir::from(4);
177 }
178
179 #[test]
180 fn into_usize() {
181 let input = [S, E, N, W];
182 let expected = [0, 1, 2, 3];
183 let output = input.map(usize::from);
184 assert_eq!(expected, output, "\n input: {input:?}");
185 }
186
187 #[test]
191 fn index_array() {
192 let input = ([0, 1, 2, 3], [S, E, N, W]);
193 let expected = [0, 1, 2, 3];
194 let output = input.1.map(|dir| input.0[dir]);
195 assert_eq!(expected, output, "\n input: {input:?}");
196 }
197}