1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::error;
use std::fmt;
pub type CoordValue = u8;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Coords {
pub row: CoordValue,
pub column: CoordValue,
}
impl Coords {
pub fn new(row: CoordValue, column: CoordValue) -> Self {
Self { row, column }
}
pub fn is_on_board_with_size(&self, size: CoordValue) -> bool {
self.row < size && self.column < size
}
}
impl std::str::FromStr for Coords {
type Err = ParseCoordsError;
fn from_str(string: &str) -> Result<Self, Self::Err> {
let column = string.chars().next().and_then(parse_column_char);
let row = string
.get(1..)
.and_then(|s| s.parse::<CoordValue>().ok())
.filter(|&row| 0 < row)
.map(|row| row - 1);
match row.zip(column) {
Some((row, column)) => Ok(Coords { row, column }),
None => Err(ParseCoordsError {
description: format!("Invalid coordinates: {}", string),
}),
}
}
}
pub fn parse_column_char(c: char) -> Option<CoordValue> {
if ('a'..='z').contains(&c) {
Some(((c as u8) - b'a') as CoordValue)
} else {
None
}
}
pub fn to_column_char(column: CoordValue) -> char {
(b'a' + (column as u8)) as char
}
impl fmt::Display for Coords {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}{}", to_column_char(self.column), self.row + 1)
}
}
#[derive(Debug)]
pub struct ParseCoordsError {
description: String,
}
impl fmt::Display for ParseCoordsError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self.description)
}
}
impl error::Error for ParseCoordsError {}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_to_string() {
assert_eq!(Coords::new(0, 0).to_string(), "a1");
assert_eq!(Coords::new(12, 5).to_string(), "f13");
}
#[test]
fn test_from_str() {
assert_eq!(Coords::from_str("a1").unwrap(), Coords::new(0, 0));
assert_eq!(Coords::from_str("f13").unwrap(), Coords::new(12, 5));
}
#[test]
fn test_from_invalid_strings() {
assert!(Coords::from_str("").is_err());
assert!(Coords::from_str("abc").is_err());
assert!(Coords::from_str("A2").is_err());
assert!(Coords::from_str("a0").is_err());
assert!(Coords::from_str("ä2").is_err());
}
}