advent_of_code_rust_runner/
day.rs

1use 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}