feat: use checked Day type instead of integers for days (#35)

This commit is contained in:
Tristan Guichaoua
2023-11-22 14:10:19 +01:00
committed by GitHub
parent 6653e856a6
commit 6d9bf346a0
12 changed files with 248 additions and 64 deletions

View File

@@ -4,6 +4,8 @@ use std::{
process::{Command, Output, Stdio},
};
use crate::Day;
#[derive(Debug)]
pub enum AocCommandError {
CommandNotFound,
@@ -33,7 +35,7 @@ pub fn check() -> Result<(), AocCommandError> {
Ok(())
}
pub fn read(day: u8) -> Result<Output, AocCommandError> {
pub fn read(day: Day) -> Result<Output, AocCommandError> {
let puzzle_path = get_puzzle_path(day);
let args = build_args(
@@ -49,7 +51,7 @@ pub fn read(day: u8) -> Result<Output, AocCommandError> {
call_aoc_cli(&args)
}
pub fn download(day: u8) -> Result<Output, AocCommandError> {
pub fn download(day: Day) -> Result<Output, AocCommandError> {
let input_path = get_input_path(day);
let puzzle_path = get_puzzle_path(day);
@@ -72,7 +74,7 @@ pub fn download(day: u8) -> Result<Output, AocCommandError> {
Ok(output)
}
pub fn submit(day: u8, part: u8, result: &str) -> Result<Output, AocCommandError> {
pub fn submit(day: Day, part: u8, result: &str) -> Result<Output, AocCommandError> {
// workaround: the argument order is inverted for submit.
let mut args = build_args("submit", &[], day);
args.push(part.to_string());
@@ -80,14 +82,12 @@ pub fn submit(day: u8, part: u8, result: &str) -> Result<Output, AocCommandError
call_aoc_cli(&args)
}
fn get_input_path(day: u8) -> String {
let day_padded = format!("{day:02}");
format!("data/inputs/{day_padded}.txt")
fn get_input_path(day: Day) -> String {
format!("data/inputs/{day}.txt")
}
fn get_puzzle_path(day: u8) -> String {
let day_padded = format!("{day:02}");
format!("data/puzzles/{day_padded}.md")
fn get_puzzle_path(day: Day) -> String {
format!("data/puzzles/{day}.md")
}
fn get_year() -> Option<u16> {
@@ -97,7 +97,7 @@ fn get_year() -> Option<u16> {
}
}
fn build_args(command: &str, args: &[String], day: u8) -> Vec<String> {
fn build_args(command: &str, args: &[String], day: Day) -> Vec<String> {
let mut cmd_args = args.to_vec();
if let Some(year) = get_year() {

View File

@@ -4,11 +4,12 @@ use crate::template::{
readme_benchmarks::{self, Timings},
ANSI_BOLD, ANSI_ITALIC, ANSI_RESET,
};
use crate::{all_days, Day};
pub fn handle(is_release: bool, is_timed: bool) {
let mut timings: Vec<Timings> = vec![];
(1..=25).for_each(|day| {
all_days().for_each(|day| {
if day > 1 {
println!();
}
@@ -56,15 +57,15 @@ impl From<std::io::Error> for Error {
}
#[must_use]
pub fn get_path_for_bin(day: usize) -> String {
let day_padded = format!("{day:02}");
format!("./src/bin/{day_padded}.rs")
pub fn get_path_for_bin(day: Day) -> String {
format!("./src/bin/{day}.rs")
}
/// All solutions live in isolated binaries.
/// This module encapsulates interaction with these binaries, both invoking them as well as parsing the timing output.
mod child_commands {
use super::{get_path_for_bin, Error};
use crate::Day;
use std::{
io::{BufRead, BufReader},
path::Path,
@@ -73,18 +74,13 @@ mod child_commands {
};
/// Run the solution bin for a given day
pub fn run_solution(
day: usize,
is_timed: bool,
is_release: bool,
) -> Result<Vec<String>, Error> {
let day_padded = format!("{day:02}");
pub fn run_solution(day: Day, is_timed: bool, is_release: bool) -> Result<Vec<String>, Error> {
// skip command invocation for days that have not been scaffolded yet.
if !Path::new(&get_path_for_bin(day)).exists() {
return Ok(vec![]);
}
let day_padded = day.to_string();
let mut args = vec!["run", "--quiet", "--bin", &day_padded];
if is_release {
@@ -129,7 +125,7 @@ mod child_commands {
Ok(output)
}
pub fn parse_exec_time(output: &[String], day: usize) -> super::Timings {
pub fn parse_exec_time(output: &[String], day: Day) -> super::Timings {
let mut timings = super::Timings {
day,
part_1: None,
@@ -208,6 +204,8 @@ mod child_commands {
mod tests {
use super::parse_exec_time;
use crate::day;
#[test]
fn test_well_formed() {
let res = parse_exec_time(
@@ -216,7 +214,7 @@ mod child_commands {
"Part 2: 10 (74.13ms @ 99999 samples)".into(),
"".into(),
],
1,
day!(1),
);
assert_approx_eq!(res.total_nanos, 74130074.13_f64);
assert_eq!(res.part_1.unwrap(), "74.13ns");
@@ -231,7 +229,7 @@ mod child_commands {
"Part 2: 10s (100ms @ 1 samples)".into(),
"".into(),
],
1,
day!(1),
);
assert_approx_eq!(res.total_nanos, 2100000000_f64);
assert_eq!(res.part_1.unwrap(), "2s");
@@ -246,7 +244,7 @@ mod child_commands {
"Part 2: ✖ ".into(),
"".into(),
],
1,
day!(1),
);
assert_approx_eq!(res.total_nanos, 0_f64);
assert_eq!(res.part_1.is_none(), true);

View File

@@ -1,7 +1,8 @@
use crate::template::aoc_cli;
use crate::Day;
use std::process;
pub fn handle(day: u8) {
pub fn handle(day: Day) {
if aoc_cli::check().is_err() {
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
process::exit(1);

View File

@@ -1,8 +1,9 @@
use std::process;
use crate::template::aoc_cli;
use crate::Day;
pub fn handle(day: u8) {
pub fn handle(day: Day) {
if aoc_cli::check().is_err() {
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
process::exit(1);

View File

@@ -4,6 +4,8 @@ use std::{
process,
};
use crate::Day;
const MODULE_TEMPLATE: &str = r#"pub fn part_one(input: &str) -> Option<u32> {
None
}
@@ -12,7 +14,7 @@ pub fn part_two(input: &str) -> Option<u32> {
None
}
advent_of_code::main!(DAY);
advent_of_code::main!(DAY_NUMBER);
#[cfg(test)]
mod tests {
@@ -40,12 +42,10 @@ fn create_file(path: &str) -> Result<File, std::io::Error> {
OpenOptions::new().write(true).create(true).open(path)
}
pub fn handle(day: u8) {
let day_padded = format!("{day:02}");
let input_path = format!("data/inputs/{day_padded}.txt");
let example_path = format!("data/examples/{day_padded}.txt");
let module_path = format!("src/bin/{day_padded}.rs");
pub fn handle(day: Day) {
let input_path = format!("data/inputs/{day}.txt");
let example_path = format!("data/examples/{day}.txt");
let module_path = format!("src/bin/{day}.rs");
let mut file = match safe_create_file(&module_path) {
Ok(file) => file,
@@ -55,7 +55,11 @@ pub fn handle(day: u8) {
}
};
match file.write_all(MODULE_TEMPLATE.replace("DAY", &day.to_string()).as_bytes()) {
match file.write_all(
MODULE_TEMPLATE
.replace("DAY_NUMBER", &day.into_inner().to_string())
.as_bytes(),
) {
Ok(()) => {
println!("Created module file \"{}\"", &module_path);
}
@@ -86,8 +90,5 @@ pub fn handle(day: u8) {
}
println!("---");
println!(
"🎄 Type `cargo solve {}` to run your solution.",
&day_padded
);
println!("🎄 Type `cargo solve {}` to run your solution.", day);
}

View File

@@ -1,9 +1,9 @@
use std::process::{Command, Stdio};
pub fn handle(day: u8, release: bool, time: bool, submit_part: Option<u8>) {
let day_padded = format!("{day:02}");
use crate::Day;
let mut cmd_args = vec!["run".to_string(), "--bin".to_string(), day_padded];
pub fn handle(day: Day, release: bool, time: bool, submit_part: Option<u8>) {
let mut cmd_args = vec!["run".to_string(), "--bin".to_string(), day.to_string()];
if release {
cmd_args.push("--release".to_string());

View File

@@ -1,3 +1,4 @@
use crate::Day;
use std::{env, fs};
pub mod aoc_cli;
@@ -10,12 +11,10 @@ pub const ANSI_BOLD: &str = "\x1b[1m";
pub const ANSI_RESET: &str = "\x1b[0m";
/// Helper function that reads a text file to a string.
#[must_use] pub fn read_file(folder: &str, day: u8) -> String {
#[must_use]
pub fn read_file(folder: &str, day: Day) -> String {
let cwd = env::current_dir().unwrap();
let filepath = cwd
.join("data")
.join(folder)
.join(format!("{day:02}.txt"));
let filepath = cwd.join("data").join(folder).join(format!("{day}.txt"));
let f = fs::read_to_string(filepath);
f.expect("could not open input file")
}
@@ -24,11 +23,14 @@ pub const ANSI_RESET: &str = "\x1b[0m";
#[macro_export]
macro_rules! main {
($day:expr) => {
/// The current day.
const DAY: advent_of_code::Day = advent_of_code::day!($day);
fn main() {
use advent_of_code::template::runner::*;
let input = advent_of_code::template::read_file("inputs", $day);
run_part(part_one, &input, $day, 1);
run_part(part_two, &input, $day, 2);
let input = advent_of_code::template::read_file("inputs", DAY);
run_part(part_one, &input, DAY, 1);
run_part(part_two, &input, DAY, 2);
}
};
}

View File

@@ -2,6 +2,8 @@
/// The approach taken is similar to how `aoc-readme-stars` handles this.
use std::{fs, io};
use crate::Day;
static MARKER: &str = "<!--- benchmarking table --->";
#[derive(Debug)]
@@ -18,7 +20,7 @@ impl From<std::io::Error> for Error {
#[derive(Clone)]
pub struct Timings {
pub day: usize,
pub day: Day,
pub part_1: Option<String>,
pub part_2: Option<String>,
pub total_nanos: f64,
@@ -29,9 +31,9 @@ pub struct TablePosition {
pos_end: usize,
}
#[must_use] pub fn get_path_for_bin(day: usize) -> String {
let day_padded = format!("{day:02}");
format!("./src/bin/{day_padded}.rs")
#[must_use]
pub fn get_path_for_bin(day: Day) -> String {
format!("./src/bin/{day}.rs")
}
fn locate_table(readme: &str) -> Result<TablePosition, Error> {
@@ -71,7 +73,7 @@ fn construct_table(prefix: &str, timings: Vec<Timings>, total_millis: f64) -> St
let path = get_path_for_bin(timing.day);
lines.push(format!(
"| [Day {}]({}) | `{}` | `{}` |",
timing.day,
timing.day.into_inner(),
path,
timing.part_1.unwrap_or_else(|| "-".into()),
timing.part_2.unwrap_or_else(|| "-".into())
@@ -103,23 +105,24 @@ pub fn update(timings: Vec<Timings>, total_millis: f64) -> Result<(), Error> {
#[cfg(feature = "test_lib")]
mod tests {
use super::{update_content, Timings, MARKER};
use crate::day;
fn get_mock_timings() -> Vec<Timings> {
vec![
Timings {
day: 1,
day: day!(1),
part_1: Some("10ms".into()),
part_2: Some("20ms".into()),
total_nanos: 3e+10,
},
Timings {
day: 2,
day: day!(2),
part_1: Some("30ms".into()),
part_2: Some("40ms".into()),
total_nanos: 7e+10,
},
Timings {
day: 4,
day: day!(4),
part_1: Some("40ms".into()),
part_2: Some("50ms".into()),
total_nanos: 9e+10,

View File

@@ -1,5 +1,6 @@
/// Encapsulates code that interacts with solution functions.
use crate::template::{aoc_cli, ANSI_ITALIC, ANSI_RESET};
use crate::Day;
use std::fmt::Display;
use std::io::{stdout, Write};
use std::process::Output;
@@ -8,7 +9,7 @@ use std::{cmp, env, process};
use super::ANSI_BOLD;
pub fn run_part<I: Clone, T: Display>(func: impl Fn(I) -> Option<T>, input: I, day: u8, part: u8) {
pub fn run_part<I: Clone, T: Display>(func: impl Fn(I) -> Option<T>, input: I, day: Day, part: u8) {
let part_str = format!("Part {part}");
let (result, duration, samples) =
@@ -131,7 +132,7 @@ fn print_result<T: Display>(result: &Option<T>, part: &str, duration_str: &str)
/// 2. aoc-cli is installed.
fn submit_result<T: Display>(
result: T,
day: u8,
day: Day,
part: u8,
) -> Option<Result<Output, aoc_cli::AocCommandError>> {
let args: Vec<String> = env::args().collect();