use std::str::FromStr;
use crate::{Error, BOARD_SIZE};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[repr(u8)]
pub enum File {
A,
B,
C,
D,
E,
F,
G,
H,
}
pub const NUM_FILES: usize = BOARD_SIZE.1 as usize;
pub const ALL_FILES: [File; NUM_FILES] = [
File::A,
File::B,
File::C,
File::D,
File::E,
File::F,
File::G,
File::H,
];
impl File {
#[inline]
pub fn new(index: usize) -> Self {
ALL_FILES[index % NUM_FILES]
}
#[inline]
pub fn to_index(&self) -> usize {
*self as usize
}
#[inline]
pub fn left(&self) -> Self {
File::new(self.to_index().wrapping_sub(1))
}
#[inline]
pub fn right(&self) -> Self {
File::new(self.to_index() + 1)
}
#[inline]
pub fn distance(&self, other: File) -> u32 {
self.to_index().abs_diff(other.to_index()) as u32
}
#[inline]
pub fn between(&self, lower_bound: File, upper_bound: File) -> bool {
lower_bound <= *self && *self <= upper_bound
}
}
impl FromStr for File {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(Error::InvalidFile);
}
match s.chars().next().unwrap() {
'a' => Ok(File::A),
'b' => Ok(File::B),
'c' => Ok(File::C),
'd' => Ok(File::D),
'e' => Ok(File::E),
'f' => Ok(File::F),
'g' => Ok(File::G),
'h' => Ok(File::H),
_ => Err(Error::InvalidFile),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_index() {
assert_eq!(File::A.to_index(), 0);
assert_eq!(File::B.to_index(), 1);
assert_eq!(File::C.to_index(), 2);
assert_eq!(File::D.to_index(), 3);
assert_eq!(File::E.to_index(), 4);
assert_eq!(File::F.to_index(), 5);
assert_eq!(File::G.to_index(), 6);
assert_eq!(File::H.to_index(), 7);
}
#[test]
fn right() {
assert_eq!(File::A.right(), File::B);
assert_eq!(File::B.right(), File::C);
assert_eq!(File::C.right(), File::D);
assert_eq!(File::D.right(), File::E);
assert_eq!(File::E.right(), File::F);
assert_eq!(File::F.right(), File::G);
assert_eq!(File::G.right(), File::H);
assert_eq!(File::H.right(), File::A);
}
#[test]
fn left() {
assert_eq!(File::A.left(), File::H);
assert_eq!(File::B.left(), File::A);
assert_eq!(File::C.left(), File::B);
assert_eq!(File::D.left(), File::C);
assert_eq!(File::E.left(), File::D);
assert_eq!(File::F.left(), File::E);
assert_eq!(File::G.left(), File::F);
assert_eq!(File::H.left(), File::G);
}
#[test]
fn distance() {
assert_eq!(File::A.distance(File::A), 0);
assert_eq!(File::A.distance(File::D), 3);
assert_eq!(File::A.distance(File::H), 7);
}
#[test]
fn between() {
assert!(File::A.between(File::A, File::H));
assert!(File::H.between(File::A, File::H));
assert!(File::A.between(File::A, File::A));
assert!(!File::A.between(File::B, File::H));
assert!(!File::H.between(File::A, File::G));
assert!(!File::B.between(File::C, File::A));
}
#[test]
fn from_str() {
assert_eq!(File::from_str("a"), Ok(File::A));
assert_eq!(File::from_str("b"), Ok(File::B));
assert_eq!(File::from_str("c"), Ok(File::C));
assert_eq!(File::from_str("d"), Ok(File::D));
assert_eq!(File::from_str("e"), Ok(File::E));
assert_eq!(File::from_str("f"), Ok(File::F));
assert_eq!(File::from_str("g"), Ok(File::G));
assert_eq!(File::from_str("h"), Ok(File::H));
}
#[test]
fn from_str_error() {
assert_eq!(File::from_str(""), Err(Error::InvalidFile));
assert_eq!(File::from_str(" a"), Err(Error::InvalidFile));
assert_eq!(File::from_str("A"), Err(Error::InvalidFile));
}
}