1use std::str::FromStr;
2
3use crate::{Error, BOARD_SIZE};
4
5#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
7#[repr(u8)]
8pub enum File {
9 A,
10 B,
11 C,
12 D,
13 E,
14 F,
15 G,
16 H,
17}
18
19pub const NUM_FILES: usize = BOARD_SIZE.1 as usize;
21
22pub const ALL_FILES: [File; NUM_FILES] = [
24 File::A,
25 File::B,
26 File::C,
27 File::D,
28 File::E,
29 File::F,
30 File::G,
31 File::H,
32];
33
34impl File {
35 #[inline]
39 pub fn new(index: usize) -> Self {
40 ALL_FILES[index % NUM_FILES]
41 }
42
43 #[inline]
45 pub fn to_index(&self) -> usize {
46 *self as usize
47 }
48
49 #[inline]
53 pub fn left(&self) -> Self {
54 File::new(self.to_index().wrapping_sub(1))
55 }
56
57 #[inline]
61 pub fn right(&self) -> Self {
62 File::new(self.to_index() + 1)
63 }
64
65 #[inline]
67 pub fn distance(&self, other: File) -> u32 {
68 self.to_index().abs_diff(other.to_index()) as u32
69 }
70
71 #[inline]
75 pub fn between(&self, lower_bound: File, upper_bound: File) -> bool {
76 lower_bound <= *self && *self <= upper_bound
77 }
78}
79
80impl FromStr for File {
81 type Err = Error;
82
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
85 if s.is_empty() {
86 return Err(Error::InvalidFile);
87 }
88 match s.chars().next().unwrap() {
89 'a' => Ok(File::A),
90 'b' => Ok(File::B),
91 'c' => Ok(File::C),
92 'd' => Ok(File::D),
93 'e' => Ok(File::E),
94 'f' => Ok(File::F),
95 'g' => Ok(File::G),
96 'h' => Ok(File::H),
97 _ => Err(Error::InvalidFile),
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn to_index() {
108 assert_eq!(File::A.to_index(), 0);
109 assert_eq!(File::B.to_index(), 1);
110 assert_eq!(File::C.to_index(), 2);
111 assert_eq!(File::D.to_index(), 3);
112 assert_eq!(File::E.to_index(), 4);
113 assert_eq!(File::F.to_index(), 5);
114 assert_eq!(File::G.to_index(), 6);
115 assert_eq!(File::H.to_index(), 7);
116 }
117
118 #[test]
119 fn right() {
120 assert_eq!(File::A.right(), File::B);
121 assert_eq!(File::B.right(), File::C);
122 assert_eq!(File::C.right(), File::D);
123 assert_eq!(File::D.right(), File::E);
124 assert_eq!(File::E.right(), File::F);
125 assert_eq!(File::F.right(), File::G);
126 assert_eq!(File::G.right(), File::H);
127 assert_eq!(File::H.right(), File::A);
128 }
129
130 #[test]
131 fn left() {
132 assert_eq!(File::A.left(), File::H);
133 assert_eq!(File::B.left(), File::A);
134 assert_eq!(File::C.left(), File::B);
135 assert_eq!(File::D.left(), File::C);
136 assert_eq!(File::E.left(), File::D);
137 assert_eq!(File::F.left(), File::E);
138 assert_eq!(File::G.left(), File::F);
139 assert_eq!(File::H.left(), File::G);
140 }
141
142 #[test]
143 fn distance() {
144 assert_eq!(File::A.distance(File::A), 0);
145 assert_eq!(File::A.distance(File::D), 3);
146 assert_eq!(File::A.distance(File::H), 7);
147 }
148
149 #[test]
150 fn between() {
151 assert!(File::A.between(File::A, File::H));
153 assert!(File::H.between(File::A, File::H));
154 assert!(File::A.between(File::A, File::A));
155 assert!(!File::A.between(File::B, File::H));
157 assert!(!File::H.between(File::A, File::G));
158 assert!(!File::B.between(File::C, File::A));
159 }
160
161 #[test]
162 fn from_str() {
163 assert_eq!(File::from_str("a"), Ok(File::A));
164 assert_eq!(File::from_str("b"), Ok(File::B));
165 assert_eq!(File::from_str("c"), Ok(File::C));
166 assert_eq!(File::from_str("d"), Ok(File::D));
167 assert_eq!(File::from_str("e"), Ok(File::E));
168 assert_eq!(File::from_str("f"), Ok(File::F));
169 assert_eq!(File::from_str("g"), Ok(File::G));
170 assert_eq!(File::from_str("h"), Ok(File::H));
171 }
172
173 #[test]
174 fn from_str_error() {
175 assert_eq!(File::from_str(""), Err(Error::InvalidFile));
176 assert_eq!(File::from_str(" a"), Err(Error::InvalidFile));
177 assert_eq!(File::from_str("A"), Err(Error::InvalidFile));
178 }
179}