feat: use checked Day type instead of integers for days (#35)
This commit is contained in:
parent
6653e856a6
commit
6d9bf346a0
12 changed files with 248 additions and 64 deletions
172
src/day.rs
Normal file
172
src/day.rs
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// A valid day number of advent (i.e. an integer in range 1 to 25).
|
||||||
|
///
|
||||||
|
/// # Display
|
||||||
|
/// This value displays as a two digit number.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use advent_of_code::Day;
|
||||||
|
/// let day = Day::new(8).unwrap();
|
||||||
|
/// assert_eq!(day.to_string(), "08")
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Day(u8);
|
||||||
|
|
||||||
|
impl Day {
|
||||||
|
/// Creates a [`Day`] from the provided value if it's in the valid range,
|
||||||
|
/// returns [`None`] otherwise.
|
||||||
|
pub fn new(day: u8) -> Option<Self> {
|
||||||
|
if day == 0 || day > 25 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self(day))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not part of the public API
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub const fn __new_unchecked(day: u8) -> Self {
|
||||||
|
Self(day)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the [`Day`] into an [`u8`].
|
||||||
|
pub fn into_inner(self) -> u8 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Day {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:02}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<u8> for Day {
|
||||||
|
fn eq(&self, other: &u8) -> bool {
|
||||||
|
self.0.eq(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<u8> for Day {
|
||||||
|
fn partial_cmp(&self, other: &u8) -> Option<std::cmp::Ordering> {
|
||||||
|
self.0.partial_cmp(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
impl FromStr for Day {
|
||||||
|
type Err = DayFromStrError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let day = s.parse().map_err(|_| DayFromStrError)?;
|
||||||
|
Self::new(day).ok_or(DayFromStrError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error which can be returned when parsing a [`Day`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DayFromStrError;
|
||||||
|
|
||||||
|
impl Error for DayFromStrError {}
|
||||||
|
|
||||||
|
impl Display for DayFromStrError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("expecting a day number between 1 and 25")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/// An iterator that yields every day of advent from the 1st to the 25th.
|
||||||
|
pub fn all_days() -> AllDays {
|
||||||
|
AllDays::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator that yields every day of advent from the 1st to the 25th.
|
||||||
|
pub struct AllDays {
|
||||||
|
current: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllDays {
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { current: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for AllDays {
|
||||||
|
type Item = Day;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.current > 25 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// NOTE: the iterator starts at 1 and we have verified that the value is not above 25.
|
||||||
|
let day = Day(self.current);
|
||||||
|
self.current += 1;
|
||||||
|
|
||||||
|
Some(day)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/// Creates a [`Day`] value in a const context.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! day {
|
||||||
|
($day:expr) => {{
|
||||||
|
const _ASSERT: () = assert!(
|
||||||
|
$day != 0 && $day <= 25,
|
||||||
|
concat!(
|
||||||
|
"invalid day number `",
|
||||||
|
$day,
|
||||||
|
"`, expecting a value between 1 and 25"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$crate::Day::__new_unchecked($day)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#[cfg(feature = "test_lib")]
|
||||||
|
mod tests {
|
||||||
|
use super::{all_days, Day};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_days_iterator() {
|
||||||
|
let mut iter = all_days();
|
||||||
|
|
||||||
|
assert_eq!(iter.next(), Some(Day(1)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(2)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(3)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(4)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(5)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(6)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(7)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(8)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(9)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(10)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(11)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(12)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(13)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(14)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(15)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(16)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(17)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(18)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(19)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(20)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(21)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(22)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(23)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(24)));
|
||||||
|
assert_eq!(iter.next(), Some(Day(25)));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
|
@ -1 +1,4 @@
|
||||||
|
mod day;
|
||||||
pub mod template;
|
pub mod template;
|
||||||
|
|
||||||
|
pub use day::*;
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -4,18 +4,20 @@ use args::{parse, AppArguments};
|
||||||
mod args {
|
mod args {
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
use advent_of_code::Day;
|
||||||
|
|
||||||
pub enum AppArguments {
|
pub enum AppArguments {
|
||||||
Download {
|
Download {
|
||||||
day: u8,
|
day: Day,
|
||||||
},
|
},
|
||||||
Read {
|
Read {
|
||||||
day: u8,
|
day: Day,
|
||||||
},
|
},
|
||||||
Scaffold {
|
Scaffold {
|
||||||
day: u8,
|
day: Day,
|
||||||
},
|
},
|
||||||
Solve {
|
Solve {
|
||||||
day: u8,
|
day: Day,
|
||||||
release: bool,
|
release: bool,
|
||||||
time: bool,
|
time: bool,
|
||||||
submit: Option<u8>,
|
submit: Option<u8>,
|
||||||
|
|
|
@ -4,6 +4,8 @@ use std::{
|
||||||
process::{Command, Output, Stdio},
|
process::{Command, Output, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::Day;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AocCommandError {
|
pub enum AocCommandError {
|
||||||
CommandNotFound,
|
CommandNotFound,
|
||||||
|
@ -33,7 +35,7 @@ pub fn check() -> Result<(), AocCommandError> {
|
||||||
Ok(())
|
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 puzzle_path = get_puzzle_path(day);
|
||||||
|
|
||||||
let args = build_args(
|
let args = build_args(
|
||||||
|
@ -49,7 +51,7 @@ pub fn read(day: u8) -> Result<Output, AocCommandError> {
|
||||||
call_aoc_cli(&args)
|
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 input_path = get_input_path(day);
|
||||||
let puzzle_path = get_puzzle_path(day);
|
let puzzle_path = get_puzzle_path(day);
|
||||||
|
|
||||||
|
@ -72,7 +74,7 @@ pub fn download(day: u8) -> Result<Output, AocCommandError> {
|
||||||
Ok(output)
|
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.
|
// workaround: the argument order is inverted for submit.
|
||||||
let mut args = build_args("submit", &[], day);
|
let mut args = build_args("submit", &[], day);
|
||||||
args.push(part.to_string());
|
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)
|
call_aoc_cli(&args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_input_path(day: u8) -> String {
|
fn get_input_path(day: Day) -> String {
|
||||||
let day_padded = format!("{day:02}");
|
format!("data/inputs/{day}.txt")
|
||||||
format!("data/inputs/{day_padded}.txt")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_puzzle_path(day: u8) -> String {
|
fn get_puzzle_path(day: Day) -> String {
|
||||||
let day_padded = format!("{day:02}");
|
format!("data/puzzles/{day}.md")
|
||||||
format!("data/puzzles/{day_padded}.md")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_year() -> Option<u16> {
|
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();
|
let mut cmd_args = args.to_vec();
|
||||||
|
|
||||||
if let Some(year) = get_year() {
|
if let Some(year) = get_year() {
|
||||||
|
|
|
@ -4,11 +4,12 @@ use crate::template::{
|
||||||
readme_benchmarks::{self, Timings},
|
readme_benchmarks::{self, Timings},
|
||||||
ANSI_BOLD, ANSI_ITALIC, ANSI_RESET,
|
ANSI_BOLD, ANSI_ITALIC, ANSI_RESET,
|
||||||
};
|
};
|
||||||
|
use crate::{all_days, Day};
|
||||||
|
|
||||||
pub fn handle(is_release: bool, is_timed: bool) {
|
pub fn handle(is_release: bool, is_timed: bool) {
|
||||||
let mut timings: Vec<Timings> = vec![];
|
let mut timings: Vec<Timings> = vec![];
|
||||||
|
|
||||||
(1..=25).for_each(|day| {
|
all_days().for_each(|day| {
|
||||||
if day > 1 {
|
if day > 1 {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
@ -56,15 +57,15 @@ impl From<std::io::Error> for Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_path_for_bin(day: usize) -> String {
|
pub fn get_path_for_bin(day: Day) -> String {
|
||||||
let day_padded = format!("{day:02}");
|
format!("./src/bin/{day}.rs")
|
||||||
format!("./src/bin/{day_padded}.rs")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All solutions live in isolated binaries.
|
/// All solutions live in isolated binaries.
|
||||||
/// This module encapsulates interaction with these binaries, both invoking them as well as parsing the timing output.
|
/// This module encapsulates interaction with these binaries, both invoking them as well as parsing the timing output.
|
||||||
mod child_commands {
|
mod child_commands {
|
||||||
use super::{get_path_for_bin, Error};
|
use super::{get_path_for_bin, Error};
|
||||||
|
use crate::Day;
|
||||||
use std::{
|
use std::{
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
path::Path,
|
path::Path,
|
||||||
|
@ -73,18 +74,13 @@ mod child_commands {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Run the solution bin for a given day
|
/// Run the solution bin for a given day
|
||||||
pub fn run_solution(
|
pub fn run_solution(day: Day, is_timed: bool, is_release: bool) -> Result<Vec<String>, Error> {
|
||||||
day: usize,
|
|
||||||
is_timed: bool,
|
|
||||||
is_release: bool,
|
|
||||||
) -> Result<Vec<String>, Error> {
|
|
||||||
let day_padded = format!("{day:02}");
|
|
||||||
|
|
||||||
// skip command invocation for days that have not been scaffolded yet.
|
// skip command invocation for days that have not been scaffolded yet.
|
||||||
if !Path::new(&get_path_for_bin(day)).exists() {
|
if !Path::new(&get_path_for_bin(day)).exists() {
|
||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let day_padded = day.to_string();
|
||||||
let mut args = vec!["run", "--quiet", "--bin", &day_padded];
|
let mut args = vec!["run", "--quiet", "--bin", &day_padded];
|
||||||
|
|
||||||
if is_release {
|
if is_release {
|
||||||
|
@ -129,7 +125,7 @@ mod child_commands {
|
||||||
Ok(output)
|
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 {
|
let mut timings = super::Timings {
|
||||||
day,
|
day,
|
||||||
part_1: None,
|
part_1: None,
|
||||||
|
@ -208,6 +204,8 @@ mod child_commands {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::parse_exec_time;
|
use super::parse_exec_time;
|
||||||
|
|
||||||
|
use crate::day;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_well_formed() {
|
fn test_well_formed() {
|
||||||
let res = parse_exec_time(
|
let res = parse_exec_time(
|
||||||
|
@ -216,7 +214,7 @@ mod child_commands {
|
||||||
"Part 2: 10 (74.13ms @ 99999 samples)".into(),
|
"Part 2: 10 (74.13ms @ 99999 samples)".into(),
|
||||||
"".into(),
|
"".into(),
|
||||||
],
|
],
|
||||||
1,
|
day!(1),
|
||||||
);
|
);
|
||||||
assert_approx_eq!(res.total_nanos, 74130074.13_f64);
|
assert_approx_eq!(res.total_nanos, 74130074.13_f64);
|
||||||
assert_eq!(res.part_1.unwrap(), "74.13ns");
|
assert_eq!(res.part_1.unwrap(), "74.13ns");
|
||||||
|
@ -231,7 +229,7 @@ mod child_commands {
|
||||||
"Part 2: 10s (100ms @ 1 samples)".into(),
|
"Part 2: 10s (100ms @ 1 samples)".into(),
|
||||||
"".into(),
|
"".into(),
|
||||||
],
|
],
|
||||||
1,
|
day!(1),
|
||||||
);
|
);
|
||||||
assert_approx_eq!(res.total_nanos, 2100000000_f64);
|
assert_approx_eq!(res.total_nanos, 2100000000_f64);
|
||||||
assert_eq!(res.part_1.unwrap(), "2s");
|
assert_eq!(res.part_1.unwrap(), "2s");
|
||||||
|
@ -246,7 +244,7 @@ mod child_commands {
|
||||||
"Part 2: ✖ ".into(),
|
"Part 2: ✖ ".into(),
|
||||||
"".into(),
|
"".into(),
|
||||||
],
|
],
|
||||||
1,
|
day!(1),
|
||||||
);
|
);
|
||||||
assert_approx_eq!(res.total_nanos, 0_f64);
|
assert_approx_eq!(res.total_nanos, 0_f64);
|
||||||
assert_eq!(res.part_1.is_none(), true);
|
assert_eq!(res.part_1.is_none(), true);
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::template::aoc_cli;
|
use crate::template::aoc_cli;
|
||||||
|
use crate::Day;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
pub fn handle(day: u8) {
|
pub fn handle(day: Day) {
|
||||||
if aoc_cli::check().is_err() {
|
if aoc_cli::check().is_err() {
|
||||||
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
|
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use crate::template::aoc_cli;
|
use crate::template::aoc_cli;
|
||||||
|
use crate::Day;
|
||||||
|
|
||||||
pub fn handle(day: u8) {
|
pub fn handle(day: Day) {
|
||||||
if aoc_cli::check().is_err() {
|
if aoc_cli::check().is_err() {
|
||||||
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
|
eprintln!("command \"aoc\" not found or not callable. Try running \"cargo install aoc-cli\" to install it.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
|
|
|
@ -4,6 +4,8 @@ use std::{
|
||||||
process,
|
process,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::Day;
|
||||||
|
|
||||||
const MODULE_TEMPLATE: &str = r#"pub fn part_one(input: &str) -> Option<u32> {
|
const MODULE_TEMPLATE: &str = r#"pub fn part_one(input: &str) -> Option<u32> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -12,7 +14,7 @@ pub fn part_two(input: &str) -> Option<u32> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
advent_of_code::main!(DAY);
|
advent_of_code::main!(DAY_NUMBER);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -40,12 +42,10 @@ fn create_file(path: &str) -> Result<File, std::io::Error> {
|
||||||
OpenOptions::new().write(true).create(true).open(path)
|
OpenOptions::new().write(true).create(true).open(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle(day: u8) {
|
pub fn handle(day: Day) {
|
||||||
let day_padded = format!("{day:02}");
|
let input_path = format!("data/inputs/{day}.txt");
|
||||||
|
let example_path = format!("data/examples/{day}.txt");
|
||||||
let input_path = format!("data/inputs/{day_padded}.txt");
|
let module_path = format!("src/bin/{day}.rs");
|
||||||
let example_path = format!("data/examples/{day_padded}.txt");
|
|
||||||
let module_path = format!("src/bin/{day_padded}.rs");
|
|
||||||
|
|
||||||
let mut file = match safe_create_file(&module_path) {
|
let mut file = match safe_create_file(&module_path) {
|
||||||
Ok(file) => file,
|
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(()) => {
|
Ok(()) => {
|
||||||
println!("Created module file \"{}\"", &module_path);
|
println!("Created module file \"{}\"", &module_path);
|
||||||
}
|
}
|
||||||
|
@ -86,8 +90,5 @@ pub fn handle(day: u8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("---");
|
println!("---");
|
||||||
println!(
|
println!("🎄 Type `cargo solve {}` to run your solution.", day);
|
||||||
"🎄 Type `cargo solve {}` to run your solution.",
|
|
||||||
&day_padded
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
pub fn handle(day: u8, release: bool, time: bool, submit_part: Option<u8>) {
|
use crate::Day;
|
||||||
let day_padded = format!("{day:02}");
|
|
||||||
|
|
||||||
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 {
|
if release {
|
||||||
cmd_args.push("--release".to_string());
|
cmd_args.push("--release".to_string());
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::Day;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
pub mod aoc_cli;
|
pub mod aoc_cli;
|
||||||
|
@ -10,12 +11,10 @@ pub const ANSI_BOLD: &str = "\x1b[1m";
|
||||||
pub const ANSI_RESET: &str = "\x1b[0m";
|
pub const ANSI_RESET: &str = "\x1b[0m";
|
||||||
|
|
||||||
/// Helper function that reads a text file to a string.
|
/// 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 cwd = env::current_dir().unwrap();
|
||||||
let filepath = cwd
|
let filepath = cwd.join("data").join(folder).join(format!("{day}.txt"));
|
||||||
.join("data")
|
|
||||||
.join(folder)
|
|
||||||
.join(format!("{day:02}.txt"));
|
|
||||||
let f = fs::read_to_string(filepath);
|
let f = fs::read_to_string(filepath);
|
||||||
f.expect("could not open input file")
|
f.expect("could not open input file")
|
||||||
}
|
}
|
||||||
|
@ -24,11 +23,14 @@ pub const ANSI_RESET: &str = "\x1b[0m";
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! main {
|
macro_rules! main {
|
||||||
($day:expr) => {
|
($day:expr) => {
|
||||||
|
/// The current day.
|
||||||
|
const DAY: advent_of_code::Day = advent_of_code::day!($day);
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
use advent_of_code::template::runner::*;
|
use advent_of_code::template::runner::*;
|
||||||
let input = advent_of_code::template::read_file("inputs", $day);
|
let input = advent_of_code::template::read_file("inputs", DAY);
|
||||||
run_part(part_one, &input, $day, 1);
|
run_part(part_one, &input, DAY, 1);
|
||||||
run_part(part_two, &input, $day, 2);
|
run_part(part_two, &input, DAY, 2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
/// The approach taken is similar to how `aoc-readme-stars` handles this.
|
/// The approach taken is similar to how `aoc-readme-stars` handles this.
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
|
||||||
|
use crate::Day;
|
||||||
|
|
||||||
static MARKER: &str = "<!--- benchmarking table --->";
|
static MARKER: &str = "<!--- benchmarking table --->";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -18,7 +20,7 @@ impl From<std::io::Error> for Error {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Timings {
|
pub struct Timings {
|
||||||
pub day: usize,
|
pub day: Day,
|
||||||
pub part_1: Option<String>,
|
pub part_1: Option<String>,
|
||||||
pub part_2: Option<String>,
|
pub part_2: Option<String>,
|
||||||
pub total_nanos: f64,
|
pub total_nanos: f64,
|
||||||
|
@ -29,9 +31,9 @@ pub struct TablePosition {
|
||||||
pos_end: usize,
|
pos_end: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use] pub fn get_path_for_bin(day: usize) -> String {
|
#[must_use]
|
||||||
let day_padded = format!("{day:02}");
|
pub fn get_path_for_bin(day: Day) -> String {
|
||||||
format!("./src/bin/{day_padded}.rs")
|
format!("./src/bin/{day}.rs")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn locate_table(readme: &str) -> Result<TablePosition, Error> {
|
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);
|
let path = get_path_for_bin(timing.day);
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"| [Day {}]({}) | `{}` | `{}` |",
|
"| [Day {}]({}) | `{}` | `{}` |",
|
||||||
timing.day,
|
timing.day.into_inner(),
|
||||||
path,
|
path,
|
||||||
timing.part_1.unwrap_or_else(|| "-".into()),
|
timing.part_1.unwrap_or_else(|| "-".into()),
|
||||||
timing.part_2.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")]
|
#[cfg(feature = "test_lib")]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{update_content, Timings, MARKER};
|
use super::{update_content, Timings, MARKER};
|
||||||
|
use crate::day;
|
||||||
|
|
||||||
fn get_mock_timings() -> Vec<Timings> {
|
fn get_mock_timings() -> Vec<Timings> {
|
||||||
vec![
|
vec![
|
||||||
Timings {
|
Timings {
|
||||||
day: 1,
|
day: day!(1),
|
||||||
part_1: Some("10ms".into()),
|
part_1: Some("10ms".into()),
|
||||||
part_2: Some("20ms".into()),
|
part_2: Some("20ms".into()),
|
||||||
total_nanos: 3e+10,
|
total_nanos: 3e+10,
|
||||||
},
|
},
|
||||||
Timings {
|
Timings {
|
||||||
day: 2,
|
day: day!(2),
|
||||||
part_1: Some("30ms".into()),
|
part_1: Some("30ms".into()),
|
||||||
part_2: Some("40ms".into()),
|
part_2: Some("40ms".into()),
|
||||||
total_nanos: 7e+10,
|
total_nanos: 7e+10,
|
||||||
},
|
},
|
||||||
Timings {
|
Timings {
|
||||||
day: 4,
|
day: day!(4),
|
||||||
part_1: Some("40ms".into()),
|
part_1: Some("40ms".into()),
|
||||||
part_2: Some("50ms".into()),
|
part_2: Some("50ms".into()),
|
||||||
total_nanos: 9e+10,
|
total_nanos: 9e+10,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/// Encapsulates code that interacts with solution functions.
|
/// Encapsulates code that interacts with solution functions.
|
||||||
use crate::template::{aoc_cli, ANSI_ITALIC, ANSI_RESET};
|
use crate::template::{aoc_cli, ANSI_ITALIC, ANSI_RESET};
|
||||||
|
use crate::Day;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::io::{stdout, Write};
|
use std::io::{stdout, Write};
|
||||||
use std::process::Output;
|
use std::process::Output;
|
||||||
|
@ -8,7 +9,7 @@ use std::{cmp, env, process};
|
||||||
|
|
||||||
use super::ANSI_BOLD;
|
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 part_str = format!("Part {part}");
|
||||||
|
|
||||||
let (result, duration, samples) =
|
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.
|
/// 2. aoc-cli is installed.
|
||||||
fn submit_result<T: Display>(
|
fn submit_result<T: Display>(
|
||||||
result: T,
|
result: T,
|
||||||
day: u8,
|
day: Day,
|
||||||
part: u8,
|
part: u8,
|
||||||
) -> Option<Result<Output, aoc_cli::AocCommandError>> {
|
) -> Option<Result<Output, aoc_cli::AocCommandError>> {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
Loading…
Add table
Reference in a new issue