advent_of_code_rust_runner/
day.rs1use anyhow::Result;
2use std::time::{Duration, Instant};
3
4pub struct ExecutionResult<O: std::fmt::Display + Eq> {
5 pub(crate) part_1_result: O,
6 pub(crate) part_1_time: Duration,
7 pub(crate) part_2_result: O,
8 pub(crate) part_2_time: Duration
9}
10
11pub struct DayResult {
12 pub(crate) part_1_result: String,
13 pub(crate) part_1_time: Duration,
14 pub(crate) part_2_result: String,
15 pub(crate) part_2_time: Duration
16}
17
18impl<O: std::fmt::Display + Eq> From<ExecutionResult<O>> for DayResult {
19 fn from(result: ExecutionResult<O>) -> Self {
20 DayResult {
21 part_1_result: result.part_1_result.to_string(),
22 part_1_time: result.part_1_time,
23 part_2_result: result.part_2_result.to_string(),
24 part_2_time: result.part_2_time,
25 }
26 }
27}
28
29pub(crate) enum TestCaseResult {
30 NotExecuted,
31 Passed(Duration),
32 Failed(String, String)
33}
34
35pub struct TestResult {
36 pub(crate) part1: TestCaseResult,
37 pub(crate) part2: TestCaseResult
38}
39
40impl TestResult {
41 fn not_executed() -> Self {
42 TestResult {
43 part1: TestCaseResult::NotExecuted,
44 part2: TestCaseResult::NotExecuted
45 }
46 }
47
48 fn from_execution_result<O: std::fmt::Display + Eq>(result: ExecutionResult<O>, expected_part_1: Option<O>, expected_part_2: Option<O>) -> Self {
49 let part1 = if let Some(expected) = expected_part_1 {
50 if result.part_1_result == expected {
51 TestCaseResult::Passed(result.part_1_time)
52 } else {
53 TestCaseResult::Failed(expected.to_string(), result.part_1_result.to_string())
54 }
55 } else {
56 TestCaseResult::NotExecuted
57 };
58 let part2 = if let Some(expected) = expected_part_2 {
59 if result.part_2_result == expected {
60 TestCaseResult::Passed(result.part_2_time)
61 } else {
62 TestCaseResult::Failed(expected.to_string(), result.part_2_result.to_string())
63 }
64 } else {
65 TestCaseResult::NotExecuted
66 };
67 TestResult {
68 part1,
69 part2,
70 }
71 }
72}
73
74pub trait DayImplementation {
75 type Output<'a>: std::fmt::Display + Eq;
76 type Context<'a>;
77
78 fn day(&self) -> u8;
79 fn example_input(&self) -> Option<&'static str> { None }
80 fn example_part_1_result(&self) -> Option<Self::Output<'static>> { None }
81 fn example_part_2_result(&self) -> Option<Self::Output<'static>> { None }
82
83 fn execute_part_1<'a>(&self, input: &'a str) -> Result<(Self::Output<'a>, Self::Context<'a>)>;
84 fn execute_part_2<'a>(&self, input: &'a str, context: Self::Context<'a>) -> Result<Self::Output<'a>>;
85
86 fn run_with_input<'a>(&self, input: &'a str) -> Result<ExecutionResult<Self::Output<'a>>> {
87 log::debug!("Starting part 1 for day {}", self.day());
88 let start_part_1 = Instant::now();
89 let (part_1_result, context) = self.execute_part_1(input)?;
90 let part_1_time = start_part_1.elapsed();
91 log::debug!("Part 1 completed in {:?}, result: {}", part_1_time, part_1_result);
92
93 log::debug!("Starting part 2 for day {}", self.day());
94 let start_part_2 = Instant::now();
95 let part_2_result = self.execute_part_2(input, context)?;
96 let part_2_time = start_part_2.elapsed();
97 log::debug!("Part 2 completed in {:?}, result: {}", part_2_time, part_2_result);
98
99 Ok(ExecutionResult {
100 part_1_result,
101 part_1_time,
102 part_2_result,
103 part_2_time
104 })
105 }
106}
107
108pub trait Day {
109 fn day(&self) -> u8;
110
111 fn test_day(&self) -> Result<TestResult>;
112 fn execute_day(&self, input: &str) -> Result<DayResult>;
113}
114
115impl<T: DayImplementation> Day for T {
116 fn day(&self) -> u8 { DayImplementation::day(self) }
117
118 fn test_day(&self) -> Result<TestResult> {
119 log::debug!("Running tests for day {}", self.day());
120 if let Some(example_input) = DayImplementation::example_input(self) {
121 log::debug!("Example input found for day {}", self.day());
122 let result = self.run_with_input(example_input)?;
123 Ok(TestResult::from_execution_result(
124 result,
125 DayImplementation::example_part_1_result(self),
126 DayImplementation::example_part_2_result(self)
127 ))
128 } else {
129 log::debug!("No example input for day {}, skipping tests", self.day());
130 Ok(TestResult::not_executed())
131 }
132 }
133
134 fn execute_day(&self, input: &str) -> Result<DayResult> {
135 log::debug!("Executing day {} with actual input", self.day());
136 Ok(DayResult::from(self.run_with_input(input)?))
137 }
138}