1use std::{error, fmt, str::FromStr};
4use std::ops::Deref;
5
6use bincode::{Decode, Encode};
7
8#[derive(Encode, Decode, Clone, Copy, Debug, Eq, PartialEq)]
10pub struct Score(u16);
11
12impl fmt::Display for Score {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 write!(f, "{}", self.0)
15 }
16}
17
18impl Deref for Score {
19 type Target = u16;
20 fn deref(&self) -> &Self::Target { &self.0 }
21}
22
23#[derive(Clone, Debug, Eq, PartialEq)]
25pub enum ParseError {
26 Parse(lexical::Error),
28 Invalid(TryFromIntError),
30}
31
32impl error::Error for ParseError {}
33
34impl fmt::Display for ParseError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 Self::Parse(e) => write!(f, "parse error: {}", e),
38 Self::Invalid(e) => write!(f, "invalid input: {}", e),
39 }
40 }
41}
42
43impl FromStr for Score {
44 type Err = ParseError;
45
46 fn from_str(s: &str) -> Result<Self, Self::Err> {
47 let n: u32 = lexical::parse(s).map_err(ParseError::Parse)?;
48 Ok(Self::try_from(n).unwrap_or(Score(1000)))
49 }
51}
52
53#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct TryFromIntError(u32);
56
57impl error::Error for TryFromIntError {}
58
59impl fmt::Display for TryFromIntError {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "invalid value: {}", self.0)
62 }
63}
64
65impl TryFrom<u32> for Score {
66 type Error = TryFromIntError;
67
68 fn try_from(n: u32) -> Result<Self, Self::Error> {
69 if n > 1000 {
70 Err(TryFromIntError(n))
71 } else {
72 Ok(Self(n as u16))
73 }
74 }
75}
76
77impl TryFrom<u16> for Score {
78 type Error = TryFromIntError;
79
80 fn try_from(n: u16) -> Result<Self, Self::Error> {
81 Self::try_from(n as u32)
82 }
83}
84
85impl From<Score> for u16 {
86 fn from(score: Score) -> Self {
87 score.0
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_fmt() {
97 assert_eq!(Score(1).to_string(), "1");
98 assert_eq!(Score(1000).to_string(), "1000");
99 }
100
101 #[test]
102 fn test_try_from_u16_for_score() {
103 assert_eq!(Score::try_from(1u16), Ok(Score(1)));
104 assert_eq!(Score::try_from(1000u16), Ok(Score(1000)));
105 }
106
107 #[test]
108 fn test_from_score_for_u16() {
109 assert_eq!(u16::from(Score(8)), 8);
110 }
111}