Better CLI with clap and day runner macro

This commit is contained in:
2025-12-03 00:53:27 -05:00
parent 9cce1d7fdb
commit cdc0225ca8
6 changed files with 260 additions and 34 deletions

View File

@@ -4,7 +4,7 @@ use color_eyre::{
Result,
eyre::{Error, eyre},
};
use tracing::{debug, info, instrument};
use tracing::{debug, instrument};
pub const INPUT: &str = include_str!("input/input.txt");
@@ -98,18 +98,6 @@ pub fn part2(input: &str) -> Result<i32> {
Ok(visited_zero_count)
}
pub fn solve() -> Result<()> {
info!("Day 1");
{
let _span = tracing::info_span!("day01").entered();
let p1 = part1(INPUT)?;
info!("Part 1: {}", p1);
let p2 = part2(INPUT)?;
info!("Part 2: {}", p2);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -5,7 +5,7 @@ use color_eyre::{
eyre::{Error, OptionExt, eyre},
};
use itertools::Itertools;
use tracing::{debug, debug_span, info, info_span, instrument};
use tracing::{debug, debug_span, instrument};
pub const INPUT: &str = include_str!("input/input.txt");
@@ -141,18 +141,6 @@ pub fn part2(input: &str) -> Result<i64> {
Ok(total_invalid)
}
pub fn solve() -> Result<()> {
info!("Day 2");
{
let _span = info_span!("day02").entered();
let p1 = part1(INPUT)?;
info!("Part 1: {}", p1);
let p2 = part2(INPUT)?;
info!("Part 2: {}", p2);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,6 +1,9 @@
mod runner;
pub mod day01;
pub mod day02;
use clap::Parser;
use color_eyre::Result;
use tracing::info;
use tracing_error::ErrorLayer;
@@ -8,6 +11,24 @@ use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::prelude::*;
#[derive(Parser, Debug)]
#[command(name = "Advent of Code 2025")]
#[command(about = "Solutions for Advent of Code 2025", long_about = None)]
struct Args {
/// Day to run (1-25). If not specified, runs all days.
#[arg(short, long)]
day: Option<u8>,
/// Part to run (1 or 2). If not specified, runs all parts.
#[arg(short, long)]
part: Option<u8>,
}
runner::days! {
1 => day01,
2 => day02,
}
fn main() -> Result<()> {
color_eyre::install()?;
@@ -21,11 +42,12 @@ fn main() -> Result<()> {
)
.init();
let args = Args::parse();
info!("Advent of Code 2025");
{
let _span = tracing::info_span!("aoc").entered();
day01::solve()?;
day02::solve()?;
}
let _span = tracing::info_span!("aoc").entered();
run_days(args.day, args.part)?;
Ok(())
}

57
src/runner.rs Normal file
View File

@@ -0,0 +1,57 @@
use color_eyre::Result;
use tracing::info;
macro_rules! days {
($($day_num:literal => $day_mod:ident),* $(,)?) => {
pub fn run_days(day: Option<u8>, part: Option<u8>) -> Result<()> {
match day {
$(
Some($day_num) => $crate::runner::run_day($day_num, part, $day_mod::part1, $day_mod::part2, $day_mod::INPUT)?,
)*
Some(d) => color_eyre::eyre::bail!("Day {} is not yet implemented", d),
None => {
$(
$crate::runner::run_day($day_num, None, $day_mod::part1, $day_mod::part2, $day_mod::INPUT)?;
)*
}
}
Ok(())
}
};
}
pub(crate) use days;
pub fn run_day<T1, T2>(
day: u8,
part: Option<u8>,
part1_fn: fn(&str) -> Result<T1>,
part2_fn: fn(&str) -> Result<T2>,
input: &str,
) -> Result<()>
where
T1: std::fmt::Display,
T2: std::fmt::Display,
{
info!("Day {}", day);
let day_name = format!("{:02}", day);
let _span = tracing::info_span!("day", day = %day_name).entered();
if part.is_none() || part == Some(1) {
let result = part1_fn(input)?;
info!("Part 1: {}", result);
}
if part.is_none() || part == Some(2) {
let result = part2_fn(input)?;
info!("Part 2: {}", result);
}
if let Some(p) = part {
if p != 1 && p != 2 {
color_eyre::eyre::bail!("Part {} is invalid. Must be 1 or 2.", p);
}
}
Ok(())
}