use std::collections::HashMap;
use regex::Regex;
use thiserror::Error;
use advent_of_code_data::{Answer, Day, Part, Year};
use crate::SolverArgs;
#[derive(Error, Debug)]
pub enum SolverError {
#[error("this solver is not finished")]
NotFinished,
#[error("the answer was submitted too soon after an incorrect answer, please wait before trying again")]
TooSoon,
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
pub type Result<T> = core::result::Result<T, SolverError>;
pub type SolverPartFn = fn(&SolverArgs) -> Result<Answer>;
#[derive(Clone, Debug)]
pub struct SolverAutoRegister {
pub modpath: &'static str,
pub part_one: SolverPart,
pub part_two: SolverPart,
}
#[derive(Clone, Debug)]
pub struct Solver {
pub day: Day,
pub year: Year,
pub part_one: SolverPart,
pub part_two: SolverPart,
}
impl Solver {
pub fn part(&self, part: Part) -> &SolverPart {
match part {
Part::One => &self.part_one,
Part::Two => &self.part_two,
}
}
}
#[derive(Clone, Debug)]
pub struct SolverPart {
pub func: SolverPartFn,
pub examples: &'static [Example],
}
impl SolverPart {
pub fn example(&self, index: usize) -> &Example {
&self.examples[index]
}
}
#[derive(Clone, Debug)]
pub struct Example {
pub input: &'static str,
pub expected: Answer,
}
pub struct SolverRegistry {
solvers: HashMap<Year, HashMap<Day, Solver>>,
}
impl SolverRegistry {
pub fn compiled_from(all_solvers: &[SolverAutoRegister]) -> Self {
let mut solvers: HashMap<Year, HashMap<Day, Solver>> = Default::default();
let re = Regex::new(r"::y(?<year>\d{4,4})::day(?<day>(\d+))$").unwrap();
for registration in all_solvers.iter() {
let captures = re
.captures(registration.modpath)
.expect("module path must be in expected form ::yYYYY::dayD");
let year = Year(
captures
.name("year")
.unwrap()
.as_str()
.parse::<usize>()
.unwrap(),
);
let day = Day(captures
.name("day")
.unwrap()
.as_str()
.parse::<usize>()
.unwrap());
let solver = Solver {
year,
day,
part_one: registration.part_one.clone(),
part_two: registration.part_two.clone(),
};
match solvers.get_mut(&year) {
Some(solvers_for_year) => {
solvers_for_year.insert(day, solver);
}
None => {
solvers.insert(year, HashMap::from([(day, solver)]));
}
}
}
Self { solvers }
}
pub fn years(&self) -> Vec<Year> {
let mut y: Vec<_> = self.solvers.keys().cloned().collect();
y.sort();
y
}
pub fn days(&self, year: Year) -> Option<Vec<Day>> {
let mut d: Vec<_> = self.solvers.get(&year)?.values().map(|s| s.day).collect();
d.sort();
Some(d)
}
pub fn solver(&self, year: Year, day: Day) -> Option<&Solver> {
self.solvers
.get(&year)
.and_then(|solvers_for_year| solvers_for_year.get(&day))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_part(_args: &SolverArgs) -> Result<Answer> {
Err(SolverError::NotFinished)
}
fn create_solver(modpath: &'static str) -> SolverAutoRegister {
SolverAutoRegister {
modpath,
part_one: SolverPart {
func: test_part,
examples: &[],
},
part_two: SolverPart {
func: test_part,
examples: &[],
},
}
}
#[test]
fn years_empty_if_no_solvers() {
let registry = SolverRegistry::compiled_from(&[]);
assert!(registry.years().is_empty());
}
#[test]
fn years_are_from_solvers_in_registry() {
let registry = SolverRegistry::compiled_from(&[
create_solver("testcrate::y2024::day1"),
create_solver("testcrate::y2024::day1"),
create_solver("testcrate::y2024::day5"),
create_solver("testcrate::y2025::day1"),
create_solver("testcrate::y1999::day1"),
]);
assert_eq!(registry.years(), vec![Year(1999), Year(2024), Year(2025)]);
}
#[test]
fn days_empty_if_no_solvers() {
let registry = SolverRegistry::compiled_from(&[create_solver("testcrate::y2024::day1")]);
assert!(registry.days(Year(2023)).is_none());
assert!(registry.days(Year(2024)).is_some());
}
#[test]
fn days_are_only_for_the_requested_year() {
let registry = SolverRegistry::compiled_from(&[
create_solver("testcrate::y2024::day1"),
create_solver("testcrate::y2024::day1"),
create_solver("testcrate::y2024::day7"),
create_solver("testcrate::y2024::day5"),
create_solver("testcrate::y2025::day1"),
create_solver("testcrate::y1999::day19"),
create_solver("testcrate::y1999::day15"),
]);
assert_eq!(registry.days(Year(1999)), Some(vec![Day(15), Day(19)]));
assert_eq!(
registry.days(Year(2024)),
Some(vec![Day(1), Day(5), Day(7)])
);
assert_eq!(registry.days(Year(2025)), Some(vec![Day(1)]));
}
#[test]
fn get_solver_empty_if_year_or_day_do_not_exist() {
let registry = SolverRegistry::compiled_from(&[create_solver("testcrate::y2024::day1")]);
assert!(registry.solver(Year(2025), Day(1)).is_none());
assert!(registry.solver(Year(2024), Day(2)).is_none());
assert!(registry.solver(Year(2024), Day(1)).is_some());
}
#[test]
fn get_solver_matches_day_and_year() {
let registry = SolverRegistry::compiled_from(&[
create_solver("testcrate::y2024::day1"),
create_solver("testcrate::y2024::day5"),
create_solver("testcrate::y2025::day2"),
]);
assert_eq!(
registry.solver(Year(2024), Day(1)).unwrap().year,
Year(2024)
);
assert_eq!(registry.solver(Year(2024), Day(1)).unwrap().day, Day(1));
assert_eq!(
registry.solver(Year(2024), Day(5)).unwrap().year,
Year(2024)
);
assert_eq!(registry.solver(Year(2024), Day(5)).unwrap().day, Day(5));
assert_eq!(
registry.solver(Year(2025), Day(2)).unwrap().year,
Year(2025)
);
assert_eq!(registry.solver(Year(2025), Day(2)).unwrap().day, Day(2));
}
}