#![deny(rust_2018_idioms)]
#![allow(clippy::bool_comparison)]
#![warn(clippy::dbg_macro)]
#![warn(clippy::print_stdout)]
#![warn(clippy::todo)]
#![warn(clippy::undocumented_unsafe_blocks)]
#![warn(missing_docs)]
use core::ops;
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Cardinal {
East,
North,
West,
South,
}
impl Cardinal {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
pub fn rotate(self, amount: i32) -> Self {
let mut v = match self {
Cardinal::East => 0,
Cardinal::North => 1,
Cardinal::West => 2,
Cardinal::South => 3,
};
v += amount;
v = v.rem_euclid(4);
match v {
0 => Cardinal::East,
1 => Cardinal::North,
2 => Cardinal::West,
3 => Cardinal::South,
_ => unreachable!(),
}
}
pub fn iter_values() -> impl Iterator<Item = Self> {
[
Cardinal::East,
Cardinal::North,
Cardinal::West,
Cardinal::South,
]
.into_iter()
}
pub fn to_ivec2(self) -> (i32, i32) {
match self {
Cardinal::East => (1, 0),
Cardinal::North => (0, 1),
Cardinal::West => (-1, 0),
Cardinal::South => (0, -1),
}
}
pub fn to_angle(self) -> f32 {
match self {
Cardinal::East => 0.0,
Cardinal::North => 90.0,
Cardinal::West => 180.0,
Cardinal::South => 270.0,
}
}
pub fn from_angle(angle: f32) -> Cardinal {
let angle = angle.rem_euclid(360.0);
if (45.0..135.0).contains(&angle) {
Cardinal::North
} else if (135.0..225.0).contains(&angle) {
Cardinal::West
} else if (225.0..315.0).contains(&angle) {
Cardinal::South
} else {
Cardinal::East
}
}
pub fn is_horizontal(self) -> bool {
matches!(self, Self::East | Self::West)
}
pub fn is_vertical(self) -> bool {
matches!(self, Self::North | Self::South)
}
}
impl core::fmt::Display for Cardinal {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let word = match self {
Cardinal::East => "east",
Cardinal::North => "north",
Cardinal::West => "west",
Cardinal::South => "south",
};
f.pad(word)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Default, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CardinalValues<T> {
pub east: T,
pub north: T,
pub west: T,
pub south: T,
}
impl<T> CardinalValues<T> {
pub fn map<B, F>(self, mut f: F) -> CardinalValues<B>
where
F: FnMut(T) -> B,
{
CardinalValues {
east: f(self.east),
north: f(self.north),
west: f(self.west),
south: f(self.south),
}
}
}
impl<T> ops::Index<Cardinal> for CardinalValues<T> {
type Output = T;
fn index(&self, index: Cardinal) -> &Self::Output {
match index {
Cardinal::East => &self.east,
Cardinal::North => &self.north,
Cardinal::West => &self.west,
Cardinal::South => &self.south,
}
}
}
impl<T: Copy> IntoIterator for CardinalValues<T> {
type Item = T;
type IntoIter = CardinalIterator<T>;
fn into_iter(self) -> Self::IntoIter {
CardinalIterator(self, 0)
}
}
pub struct CardinalIterator<T>(CardinalValues<T>, usize);
impl<T> CardinalIterator<T> {
pub fn enumerate(self) -> CardinalEnumeratedIterator<T> {
CardinalEnumeratedIterator(self.0, self.1)
}
}
impl<T: Copy> Iterator for CardinalIterator<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let found = match self.1 {
0 => Some(self.0.east),
1 => Some(self.0.west),
2 => Some(self.0.north),
3 => Some(self.0.south),
_ => return None,
};
self.1 += 1;
found
}
}
pub struct CardinalEnumeratedIterator<T>(CardinalValues<T>, usize);
impl<T: Copy> Iterator for CardinalEnumeratedIterator<T> {
type Item = (Cardinal, T);
fn next(&mut self) -> Option<Self::Item> {
let found = match self.1 {
0 => Some((Cardinal::North, self.0.north)),
1 => Some((Cardinal::West, self.0.west)),
2 => Some((Cardinal::South, self.0.south)),
3 => Some((Cardinal::East, self.0.east)),
_ => return None,
};
self.1 += 1;
found
}
}