use anyhow::{bail, Result};
use colored::*;
use std::collections::HashMap;
use std::time::{Duration, Instant};
pub use aoc_toolbox_derive::{aoc_main, aoc_solver};
pub use clap;
pub use clap::Parser;
pub use std::error::Error;
pub mod utils;
enum Measure {
Real(Instant),
Mock,
}
impl Measure {
fn step(&self) -> Option<Duration> {
match self {
Measure::Real(start) => Some(Instant::now().duration_since(*start)),
Measure::Mock => None,
}
}
}
type Solver = fn() -> String;
pub struct Aoc<'a> {
year: u32,
with_duration: bool,
solvers: HashMap<&'a str, HashMap<&'a str, Solver>>,
}
impl<'a> Aoc<'a> {
pub fn new(year: u32, with_duration: bool) -> Self {
Aoc {
year,
with_duration,
solvers: HashMap::new(),
}
}
pub fn add_solver(&mut self, day: &'a str, part: &'a str, solver: Solver) {
let day = self.solvers.entry(day).or_default();
day.insert(part, solver);
}
fn visit_solvers<F>(&self, visit: F)
where
F: Fn(&'a str, Option<(&'a str, &Solver)>),
{
let mut day_keys: Vec<&&str> = self.solvers.keys().collect();
day_keys.sort_unstable();
for day in day_keys {
visit(day, None);
let parts = self.solvers.get(day).unwrap();
let mut part_keys: Vec<&&str> = parts.keys().collect();
part_keys.sort_unstable();
for part in part_keys {
let solver = parts.get(part).unwrap();
visit(day, Some((part, solver)));
}
}
}
fn pretty_format(&self, day: &str, part: &str, solver: &Solver) -> String {
let measure = if self.with_duration {
Measure::Real(Instant::now())
} else {
Measure::Mock
};
let result = solver();
let duration = measure.step();
if self.with_duration {
let duration = duration.unwrap();
format!(
"{}::{} [{:>4}s {:>4}ms] {} {}",
day.blue(),
part.green(),
duration.as_secs(),
duration.subsec_millis(),
"................".cyan(),
result,
)
} else {
format!(
"{}::{} {} {}",
day.blue(),
part.green(),
"................".cyan(),
result
)
}
}
fn run_all(&self) -> Result<()> {
self.visit_solvers(|day, part_solver| match part_solver {
None => println!("{}{}", "# ".yellow(), day.yellow()),
Some((part, solver)) => {
println!(" - {}", self.pretty_format(day, part, solver));
}
});
Ok(())
}
fn run_day(&self, day: &str) -> Result<()> {
self.visit_solvers(|visit_day, part_solver| {
if visit_day == day {
if let Some((part, solver)) = part_solver {
println!("{}", self.pretty_format(day, part, solver));
}
}
});
Ok(())
}
fn run_part(&self, day: &str, part: &str) -> Result<()> {
self.visit_solvers(|visit_day, part_solver| {
if visit_day == day {
match part_solver {
Some((visit_part, solver)) if visit_part == part => {
println!("{}", self.pretty_format(day, part, solver));
}
_ => {}
}
}
});
Ok(())
}
pub fn list(&self) {
self.visit_solvers(|day, part_solver| {
if let Some((part, _)) = part_solver {
println!("{}::{}", day.blue(), part.green())
}
});
}
pub fn run(&self, label: String) -> Result<()> {
println!("{} {}", "Year".green().bold(), self.year);
match label.split("::").collect::<Vec<&str>>().as_slice() {
["all"] => self.run_all(),
[day] => self.run_day(day),
[day, part] => self.run_part(day, part),
_ => bail!("Invalid label, must be like <day>::<part>"),
}?;
Ok(())
}
}