1use std::{error::Error, fmt, str::FromStr};
2
3#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
5pub struct Nag(pub u8);
6
7impl Nag {
8 pub fn from_ascii(s: &[u8]) -> Result<Nag, InvalidNag> {
24 if s == b"?!" {
25 Ok(Nag::DUBIOUS_MOVE)
26 } else if s == b"?" {
27 Ok(Nag::MISTAKE)
28 } else if s == b"??" {
29 Ok(Nag::BLUNDER)
30 } else if s == b"!" {
31 Ok(Nag::GOOD_MOVE)
32 } else if s == b"!!" {
33 Ok(Nag::BRILLIANT_MOVE)
34 } else if s == b"!?" {
35 Ok(Nag::SPECULATIVE_MOVE)
36 } else if s.len() > 1 && s[0] == b'$' {
37 btoi::btou(&s[1..]).ok().map(Nag).ok_or(InvalidNag)
38 } else {
39 Err(InvalidNag)
40 }
41 }
42
43 pub fn append_to_string(&self, s: &mut String) {
55 s.reserve(4);
56 s.push('$');
57 if self.0 >= 100 {
58 s.push((b'0' + (self.0 / 100) % 10) as char);
59 }
60 if self.0 >= 10 {
61 s.push((b'0' + (self.0 / 10) % 10) as char);
62 }
63 s.push((b'0' + (self.0 % 10)) as char);
64 }
65
66 pub fn append_ascii_to(&self, buf: &mut Vec<u8>) {
78 buf.reserve(4);
79 buf.push(b'$');
80 if self.0 >= 100 {
81 buf.push(b'0' + (self.0 / 100) % 10);
82 }
83 if self.0 >= 10 {
84 buf.push(b'0' + (self.0 / 10) % 10);
85 }
86 buf.push(b'0' + (self.0 % 10));
87 }
88
89 pub const GOOD_MOVE: Nag = Nag(1);
91
92 pub const MISTAKE: Nag = Nag(2);
94
95 pub const BRILLIANT_MOVE: Nag = Nag(3);
97
98 pub const BLUNDER: Nag = Nag(4);
100
101 pub const SPECULATIVE_MOVE: Nag = Nag(5);
103
104 pub const DUBIOUS_MOVE: Nag = Nag(6);
106}
107
108impl fmt::Display for Nag {
109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 write!(f, "${}", self.0)
111 }
112}
113
114impl From<u8> for Nag {
115 fn from(nag: u8) -> Nag {
116 Nag(nag)
117 }
118}
119
120impl From<Nag> for u8 {
121 fn from(Nag(nag): Nag) -> u8 {
122 nag
123 }
124}
125
126impl FromStr for Nag {
127 type Err = InvalidNag;
128
129 fn from_str(s: &str) -> Result<Nag, InvalidNag> {
130 Nag::from_ascii(s.as_bytes())
131 }
132}
133
134#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
136pub struct InvalidNag;
137
138impl fmt::Display for InvalidNag {
139 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140 f.write_str("invalid nag")
141 }
142}
143
144impl Error for InvalidNag {}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_from_ascii() {
152 assert_eq!(Nag::from_ascii(b"$1"), Ok(Nag(1)));
153 assert_eq!(Nag::from_ascii(b"$12"), Ok(Nag(12)));
154 assert_eq!(Nag::from_ascii(b"$123"), Ok(Nag(123)));
155 assert_eq!(Nag::from_ascii(b"$1234"), Err(InvalidNag));
156 }
157
158 #[test]
159 fn test_append_to_string() {
160 let mut s = String::new();
161 Nag(0).append_to_string(&mut s);
162 Nag(1).append_to_string(&mut s);
163 Nag(12).append_to_string(&mut s);
164 Nag(123).append_to_string(&mut s);
165 assert_eq!(s, "$0$1$12$123");
166 }
167
168 #[test]
169 fn test_append_ascii_to() {
170 let mut buf = Vec::new();
171 Nag(123).append_ascii_to(&mut buf);
172 Nag(12).append_ascii_to(&mut buf);
173 Nag(1).append_ascii_to(&mut buf);
174 Nag(0).append_ascii_to(&mut buf);
175 assert_eq!(buf, b"$123$12$1$0");
176 }
177}