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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::collections::{BTreeMap, HashMap};
use structopt::StructOpt;
mod day_01;
mod day_02;
mod day_03;
mod day_04;
mod day_05;
mod day_06;
mod day_07;
pub mod day_08;
pub mod day_20;
pub mod day_21;
pub mod day_22;
pub mod day_23;
pub mod day_24;
pub mod day_25;
#[derive(StructOpt)]
pub struct Cli {
#[structopt(long)]
day: Option<u32>,
#[structopt(long)]
part: Option<u32>,
}
const NUM_DAY: u32 = 7;
type AnyError = Box<dyn std::error::Error>;
type Solver = dyn Fn(&str) -> Result<String, AnyError>;
fn _candidates(args: &Cli) -> Result<BTreeMap<(u32, u32), &Solver>, AnyError> {
let mut functions: HashMap<(u32, u32), &Solver> = HashMap::new();
functions.insert((1, 1), &day_01::part_1);
functions.insert((1, 2), &day_01::part_2);
functions.insert((2, 1), &day_02::part_1);
functions.insert((2, 2), &day_02::part_2);
functions.insert((3, 1), &day_03::part_1);
functions.insert((3, 2), &day_03::part_2);
functions.insert((4, 1), &day_04::part_1);
functions.insert((4, 2), &day_04::part_2);
functions.insert((5, 1), &day_05::part_1);
functions.insert((5, 2), &day_05::part_2);
functions.insert((6, 1), &day_06::part_1);
functions.insert((6, 2), &day_06::part_2);
functions.insert((7, 1), &day_07::part_1);
functions.insert((7, 2), &day_07::part_2);
let functions = functions;
let mut result = BTreeMap::new();
let parts: Vec<u32> = match args.part {
None => 1..=2,
Some(1) => 1..=1,
Some(2) => 2..=2,
_ => return Err("Invalid part".into()),
}
.collect();
let days = match args.day {
None => 1..=NUM_DAY,
Some(day) if (1..=NUM_DAY).contains(&day) => day..=day,
_ => return Err("Invalid day".into()),
};
for day in days {
for part in &parts {
result.insert((day, *part), functions[&(day, *part)]);
}
}
Ok(result)
}
pub fn helper(args: &Cli, text: &str) -> Result<Vec<String>, AnyError> {
let candidates: BTreeMap<(u32, u32), &Solver> = _candidates(args)?;
if candidates.is_empty() {
return Err("Invalid day and part".into());
}
let mut result = Vec::new();
for ((day, part), func) in candidates.iter() {
eprintln!("Trying day {} part {}", day, part);
match func(text) {
Ok(output) => result.push(output),
Err(error) => match args.day {
None => eprintln!("{}", error),
Some(_) => return Err(error),
},
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use std::fs;
use super::*;
const STEMS: [&str; 2] = ["example", "input"];
#[test]
fn specific_day_returns_2_solutions_on_right_input() {
for day in 1..=NUM_DAY {
let args = Cli {
day: Some(day),
part: None,
};
let input_day = day;
for stem in STEMS {
let stdin =
fs::read_to_string(format!("inputs/{:02}/{}.txt", input_day, stem)).unwrap();
println!("day:{} input_day:{} stem:{}", day, input_day, stem);
assert_eq!(helper(&args, &stdin).unwrap().len(), 2);
}
}
}
#[test]
fn specific_part_returns_1_solution_on_right_input() {
for part in 1..=2 {
let args = Cli {
day: None,
part: Some(part),
};
for input_day in 1..=NUM_DAY {
for stem in STEMS {
let stdin = fs::read_to_string(format!("inputs/{:02}/{}.txt", input_day, stem))
.unwrap();
println!("day:* input_day:{} stem:{}", input_day, stem);
assert_eq!(helper(&args, &stdin).unwrap().len(), 1);
}
}
}
}
#[test]
#[ignore]
fn specific_day_returns_error_on_wrong_input() {
for day in 1..=NUM_DAY {
let args = Cli {
day: Some(day),
part: None,
};
for input_day in (1..=NUM_DAY).filter(|d| *d != day) {
for stem in STEMS {
let stdin = fs::read_to_string(format!("inputs/{:02}/{}.txt", input_day, stem))
.unwrap();
println!("day:{} input_day:{} stem:{}", day, input_day, stem);
helper(&args, &stdin).unwrap().len();
}
}
}
}
}