-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.rs
121 lines (107 loc) · 3.78 KB
/
run.rs
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
use std::{fmt::Display, num::ParseIntError, str::FromStr};
#[derive(Debug,Default,PartialEq)]
pub struct Run {
pub(crate) red: u32, pub(crate) green: u32, pub(crate) blue: u32
}
impl Run {
pub fn is_feasible(&self, run: &Run) -> bool {
self.red <= run.red
&& self.blue <= run.blue
&& self.green <= run.green
}
pub fn power(&self) -> u32 {
self.red * self.green * self.blue
}
}
#[derive(Debug,PartialEq)]
pub enum RunError {
InvalidColourValue,
MissingColourValue,
InvalidColourName,
MissingColourName,
}
impl Display for RunError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RunError::InvalidColourValue => write!(f, "colour value must be a positive number"),
RunError::MissingColourValue => write!(f, "colour value not found with colour name"),
RunError::InvalidColourName => write!(f, "colour name must be one of [ red | green | blue ]"),
RunError::MissingColourName => write!(f, "colour name not found with colour value"),
}
}
}
impl From<ParseIntError> for RunError {
fn from(_: ParseIntError) -> Self {
RunError::InvalidColourValue
}
}
impl FromStr for Run {
type Err = RunError;
/// convert " 3 blue, 4 red"," 1 red, 2 green, 6 blue", "2 green"
/// to [(Blue,3),(Red,4)], etc
fn from_str(input: &str) -> Result<Self, Self::Err> {
#[derive(Debug,Eq, PartialEq,Hash)]
enum Colour { Red, Green, Blue }
input
.trim()
.split(',')
.map(|picked| {
let mut split = picked.trim().split_ascii_whitespace();
let count = split.next().ok_or(RunError::MissingColourValue)?.parse::<u32>()?;
let colour = match split.next().ok_or(RunError::MissingColourName)? {
"red" => Ok(Colour::Red),
"green" => Ok(Colour::Green),
"blue" => Ok(Colour::Blue),
_ => Err(RunError::InvalidColourName)
}?;
Ok((colour,count))
})
.collect::<Result<Vec<(Colour,u32)>,RunError>>()
.map(|colours| colours
.into_iter()
.fold(Run::default(),|mut run, (col, val)| {
match col {
Colour::Red => run.red = val,
Colour::Green => run.green = val,
Colour::Blue => run.blue = val
}
run
})
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_run_parse() {
let input = [
(" 3 blue, 4 red", Run { red: 4, blue: 3, green: 0 }),
(" 1 red, 2 green, 6 blue", Run { red: 1, blue: 6, green: 2 }),
(" 2 green", Run { red: 0, blue: 0, green: 2 })
];
for (test, res) in input {
match test.parse::<Run>() {
Ok(run) => assert_eq!(run,res),
Err(e) => panic!("Test {:?} failed with {:?}",test,e)
}
}
}
#[test]
fn test_run_parse_errors() {
let input = [
(" d blue, 4 red", RunError::InvalidColourValue),
(" 1 red, 2 green, 6 orange", RunError::InvalidColourName ),
(" d eno", RunError::InvalidColourValue),
("1 , 4 red ", RunError::MissingColourName),
("1 4 red ", RunError::InvalidColourName),
("red, 4 blue ", RunError::InvalidColourValue)
];
for (test, res) in input {
match test.parse::<Run>() {
Ok(_) => panic!("Test {:?} should not succeed",test),
Err(e) => assert_eq!(e,res, "{:?} expected [{:?}] got [{:?}]",test, res,e)
}
}
}
}