use std::any::type_name; use std::fmt::Display; pub use std::path::Path; pub use color_eyre::eyre::Result; use tracing::info; use tracing_subscriber::EnvFilter; pub trait Solution<Input, Output: Display> { fn day() -> String { let mut day = String::from(type_name::<Self>().split("::").next().unwrap()); day.make_ascii_lowercase(); day.to_string() } fn parse_input<P: AsRef<Path>>(pathname: P) -> Input; fn part_1(input: &Input) -> Output; fn part_2(input: &Input) -> Output; fn main() -> Result<()> where Self: Sized, { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .with_target(false) .with_file(true) .with_line_number(true) .without_time() .compact() .init(); color_eyre::install()?; let input = Self::parse_input(format!("inputs/{}.txt", Self::day())); info!("Part 1: {}", Self::part_1(&input)); info!("Part 2: {}", Self::part_2(&input)); Ok(()) } } #[macro_export] macro_rules! test_sample { ($mod_name:ident, $day_struct:tt, $part_1:expr, $part_2:expr) => { #[cfg(test)] mod $mod_name { use super::*; #[test] fn test_part_1() { let sample = $day_struct::parse_input(&format!("samples/{}.txt", $day_struct::day())); assert_eq!($day_struct::part_1(&sample), $part_1); } #[test] fn test_part_2() { let sample = $day_struct::parse_input(&format!("samples/{}.txt", $day_struct::day())); assert_eq!($day_struct::part_2(&sample), $part_2); } } }; }