1#![deny(rust_2018_idioms)]
5#![allow(clippy::bool_comparison)]
6#![warn(clippy::dbg_macro)]
7#![warn(clippy::print_stdout)]
8#![warn(clippy::todo)]
9#![warn(clippy::undocumented_unsafe_blocks)]
10#![warn(missing_docs)]
11
12use core::ops;
13
14#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub enum Cardinal {
18 East,
20 North,
22 West,
24 South,
26}
27
28impl Cardinal {
29 #[must_use = "this returns the result of the operation, \
31 without modifying the original"]
32 pub fn rotate(self, amount: i32) -> Self {
33 let mut v = match self {
34 Cardinal::East => 0,
35 Cardinal::North => 1,
36 Cardinal::West => 2,
37 Cardinal::South => 3,
38 };
39
40 v += amount;
41 v = v.rem_euclid(4);
42
43 match v {
44 0 => Cardinal::East,
45 1 => Cardinal::North,
46 2 => Cardinal::West,
47 3 => Cardinal::South,
48 _ => unreachable!(),
49 }
50 }
51
52 pub fn iter_values() -> impl Iterator<Item = Self> {
54 [
55 Cardinal::East,
56 Cardinal::North,
57 Cardinal::West,
58 Cardinal::South,
59 ]
60 .into_iter()
61 }
62
63 pub fn to_ivec2(self) -> (i32, i32) {
66 match self {
67 Cardinal::East => (1, 0),
68 Cardinal::North => (0, 1),
69 Cardinal::West => (-1, 0),
70 Cardinal::South => (0, -1),
71 }
72 }
73
74 pub fn to_angle(self) -> f32 {
76 match self {
77 Cardinal::East => 0.0,
78 Cardinal::North => 90.0,
79 Cardinal::West => 180.0,
80 Cardinal::South => 270.0,
81 }
82 }
83
84 pub fn from_angle(angle: f32) -> Cardinal {
87 let angle = angle.rem_euclid(360.0);
88
89 if (45.0..135.0).contains(&angle) {
90 Cardinal::North
91 } else if (135.0..225.0).contains(&angle) {
92 Cardinal::West
93 } else if (225.0..315.0).contains(&angle) {
94 Cardinal::South
95 } else {
96 Cardinal::East
97 }
98 }
99
100 pub fn is_horizontal(self) -> bool {
102 matches!(self, Self::East | Self::West)
103 }
104
105 pub fn is_vertical(self) -> bool {
107 matches!(self, Self::North | Self::South)
108 }
109}
110
111impl core::fmt::Display for Cardinal {
112 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113 let word = match self {
114 Cardinal::East => "east",
115 Cardinal::North => "north",
116 Cardinal::West => "west",
117 Cardinal::South => "south",
118 };
119
120 f.pad(word)
121 }
122}
123
124#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Default, Hash)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
128pub struct CardinalValues<T> {
129 pub east: T,
131 pub north: T,
133 pub west: T,
135 pub south: T,
137}
138
139impl<T> CardinalValues<T> {
140 pub fn map<B, F>(self, mut f: F) -> CardinalValues<B>
142 where
143 F: FnMut(T) -> B,
144 {
145 CardinalValues {
146 east: f(self.east),
147 north: f(self.north),
148 west: f(self.west),
149 south: f(self.south),
150 }
151 }
152}
153
154impl<T> ops::Index<Cardinal> for CardinalValues<T> {
155 type Output = T;
156
157 fn index(&self, index: Cardinal) -> &Self::Output {
158 match index {
159 Cardinal::East => &self.east,
160 Cardinal::North => &self.north,
161 Cardinal::West => &self.west,
162 Cardinal::South => &self.south,
163 }
164 }
165}
166
167impl<T: Copy> IntoIterator for CardinalValues<T> {
168 type Item = T;
169
170 type IntoIter = CardinalIterator<T>;
171
172 fn into_iter(self) -> Self::IntoIter {
173 CardinalIterator(self, 0)
174 }
175}
176
177pub struct CardinalIterator<T>(CardinalValues<T>, usize);
179
180impl<T> CardinalIterator<T> {
181 pub fn enumerate(self) -> CardinalEnumeratedIterator<T> {
183 CardinalEnumeratedIterator(self.0, self.1)
184 }
185}
186
187impl<T: Copy> Iterator for CardinalIterator<T> {
188 type Item = T;
189
190 fn next(&mut self) -> Option<Self::Item> {
191 let found = match self.1 {
192 0 => Some(self.0.east),
193 1 => Some(self.0.west),
194 2 => Some(self.0.north),
195 3 => Some(self.0.south),
196 _ => return None,
197 };
198
199 self.1 += 1;
200
201 found
202 }
203}
204
205pub struct CardinalEnumeratedIterator<T>(CardinalValues<T>, usize);
208impl<T: Copy> Iterator for CardinalEnumeratedIterator<T> {
209 type Item = (Cardinal, T);
210
211 fn next(&mut self) -> Option<Self::Item> {
212 let found = match self.1 {
213 0 => Some((Cardinal::North, self.0.north)),
214 1 => Some((Cardinal::West, self.0.west)),
215 2 => Some((Cardinal::South, self.0.south)),
216 3 => Some((Cardinal::East, self.0.east)),
217 _ => return None,
218 };
219
220 self.1 += 1;
221
222 found
223 }
224}