refactor: fix most pedantic clippy warnings
closes #29 Co-authored-by: Andy Pymont <andypymont@gmail.com>
This commit is contained in:
parent
f46d1e209c
commit
d7af3dca9e
10 changed files with 107 additions and 114 deletions
41
src/main.rs
41
src/main.rs
|
@ -1,13 +1,10 @@
|
||||||
use advent_of_code::template::commands::{
|
use advent_of_code::template::commands::{all, download, read, scaffold, solve};
|
||||||
all::all_handler, download::download_handler, read::read_handler, scaffold::scaffold_handler,
|
use args::{parse, AppArguments};
|
||||||
solve::solve_handler,
|
|
||||||
};
|
|
||||||
use args::{parse_args, AppArgs};
|
|
||||||
|
|
||||||
mod args {
|
mod args {
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
pub enum AppArgs {
|
pub enum AppArguments {
|
||||||
Download {
|
Download {
|
||||||
day: u8,
|
day: u8,
|
||||||
},
|
},
|
||||||
|
@ -29,31 +26,31 @@ mod args {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_args() -> Result<AppArgs, Box<dyn std::error::Error>> {
|
pub fn parse() -> Result<AppArguments, Box<dyn std::error::Error>> {
|
||||||
let mut args = pico_args::Arguments::from_env();
|
let mut args = pico_args::Arguments::from_env();
|
||||||
|
|
||||||
let app_args = match args.subcommand()?.as_deref() {
|
let app_args = match args.subcommand()?.as_deref() {
|
||||||
Some("all") => AppArgs::All {
|
Some("all") => AppArguments::All {
|
||||||
release: args.contains("--release"),
|
release: args.contains("--release"),
|
||||||
time: args.contains("--time"),
|
time: args.contains("--time"),
|
||||||
},
|
},
|
||||||
Some("download") => AppArgs::Download {
|
Some("download") => AppArguments::Download {
|
||||||
day: args.free_from_str()?,
|
day: args.free_from_str()?,
|
||||||
},
|
},
|
||||||
Some("read") => AppArgs::Read {
|
Some("read") => AppArguments::Read {
|
||||||
day: args.free_from_str()?,
|
day: args.free_from_str()?,
|
||||||
},
|
},
|
||||||
Some("scaffold") => AppArgs::Scaffold {
|
Some("scaffold") => AppArguments::Scaffold {
|
||||||
day: args.free_from_str()?,
|
day: args.free_from_str()?,
|
||||||
},
|
},
|
||||||
Some("solve") => AppArgs::Solve {
|
Some("solve") => AppArguments::Solve {
|
||||||
day: args.free_from_str()?,
|
day: args.free_from_str()?,
|
||||||
release: args.contains("--release"),
|
release: args.contains("--release"),
|
||||||
submit: args.opt_value_from_str("--submit")?,
|
submit: args.opt_value_from_str("--submit")?,
|
||||||
time: args.contains("--time"),
|
time: args.contains("--time"),
|
||||||
},
|
},
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
eprintln!("Unknown command: {}", x);
|
eprintln!("Unknown command: {x}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -64,7 +61,7 @@ mod args {
|
||||||
|
|
||||||
let remaining = args.finish();
|
let remaining = args.finish();
|
||||||
if !remaining.is_empty() {
|
if !remaining.is_empty() {
|
||||||
eprintln!("Warning: unknown argument(s): {:?}.", remaining);
|
eprintln!("Warning: unknown argument(s): {remaining:?}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(app_args)
|
Ok(app_args)
|
||||||
|
@ -72,22 +69,22 @@ mod args {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match parse_args() {
|
match parse() {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Error: {}", err);
|
eprintln!("Error: {err}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
Ok(args) => match args {
|
Ok(args) => match args {
|
||||||
AppArgs::All { release, time } => all_handler(release, time),
|
AppArguments::All { release, time } => all::handle(release, time),
|
||||||
AppArgs::Download { day } => download_handler(day),
|
AppArguments::Download { day } => download::handle(day),
|
||||||
AppArgs::Read { day } => read_handler(day),
|
AppArguments::Read { day } => read::handle(day),
|
||||||
AppArgs::Scaffold { day } => scaffold_handler(day),
|
AppArguments::Scaffold { day } => scaffold::handle(day),
|
||||||
AppArgs::Solve {
|
AppArguments::Solve {
|
||||||
day,
|
day,
|
||||||
release,
|
release,
|
||||||
time,
|
time,
|
||||||
submit,
|
submit,
|
||||||
} => solve_handler(day, release, time, submit),
|
} => solve::handle(day, release, time, submit),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,35 +5,35 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AocCliError {
|
pub enum AocCommandError {
|
||||||
CommandNotFound,
|
CommandNotFound,
|
||||||
CommandNotCallable,
|
CommandNotCallable,
|
||||||
BadExitStatus(Output),
|
BadExitStatus(Output),
|
||||||
IoError,
|
IoError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for AocCliError {
|
impl Display for AocCommandError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
AocCliError::CommandNotFound => write!(f, "aoc-cli is not present in environment."),
|
AocCommandError::CommandNotFound => write!(f, "aoc-cli is not present in environment."),
|
||||||
AocCliError::CommandNotCallable => write!(f, "aoc-cli could not be called."),
|
AocCommandError::CommandNotCallable => write!(f, "aoc-cli could not be called."),
|
||||||
AocCliError::BadExitStatus(_) => {
|
AocCommandError::BadExitStatus(_) => {
|
||||||
write!(f, "aoc-cli exited with a non-zero status.")
|
write!(f, "aoc-cli exited with a non-zero status.")
|
||||||
}
|
}
|
||||||
AocCliError::IoError => write!(f, "could not write output files to file system."),
|
AocCommandError::IoError => write!(f, "could not write output files to file system."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check() -> Result<(), AocCliError> {
|
pub fn check() -> Result<(), AocCommandError> {
|
||||||
Command::new("aoc")
|
Command::new("aoc")
|
||||||
.arg("-V")
|
.arg("-V")
|
||||||
.output()
|
.output()
|
||||||
.map_err(|_| AocCliError::CommandNotFound)?;
|
.map_err(|_| AocCommandError::CommandNotFound)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(day: u8) -> Result<Output, AocCliError> {
|
pub fn read(day: u8) -> 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 +49,7 @@ pub fn read(day: u8) -> Result<Output, AocCliError> {
|
||||||
call_aoc_cli(&args)
|
call_aoc_cli(&args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn download(day: u8) -> Result<Output, AocCliError> {
|
pub fn download(day: u8) -> 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 +72,7 @@ pub fn download(day: u8) -> Result<Output, AocCliError> {
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit(day: u8, part: u8, result: &str) -> Result<Output, AocCliError> {
|
pub fn submit(day: u8, 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());
|
||||||
|
@ -81,13 +81,13 @@ pub fn submit(day: u8, part: u8, result: &str) -> Result<Output, AocCliError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_input_path(day: u8) -> String {
|
fn get_input_path(day: u8) -> String {
|
||||||
let day_padded = format!("{:02}", day);
|
let day_padded = format!("{day:02}");
|
||||||
format!("data/inputs/{}.txt", day_padded)
|
format!("data/inputs/{day_padded}.txt")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_puzzle_path(day: u8) -> String {
|
fn get_puzzle_path(day: u8) -> String {
|
||||||
let day_padded = format!("{:02}", day);
|
let day_padded = format!("{day:02}");
|
||||||
format!("data/puzzles/{}.md", day_padded)
|
format!("data/puzzles/{day_padded}.md")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_year() -> Option<u16> {
|
fn get_year() -> Option<u16> {
|
||||||
|
@ -110,18 +110,18 @@ fn build_args(command: &str, args: &[String], day: u8) -> Vec<String> {
|
||||||
cmd_args
|
cmd_args
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_aoc_cli(args: &[String]) -> Result<Output, AocCliError> {
|
fn call_aoc_cli(args: &[String]) -> Result<Output, AocCommandError> {
|
||||||
// println!("Calling >aoc with: {}", args.join(" "));
|
// println!("Calling >aoc with: {}", args.join(" "));
|
||||||
let output = Command::new("aoc")
|
let output = Command::new("aoc")
|
||||||
.args(args)
|
.args(args)
|
||||||
.stdout(Stdio::inherit())
|
.stdout(Stdio::inherit())
|
||||||
.stderr(Stdio::inherit())
|
.stderr(Stdio::inherit())
|
||||||
.output()
|
.output()
|
||||||
.map_err(|_| AocCliError::CommandNotCallable)?;
|
.map_err(|_| AocCommandError::CommandNotCallable)?;
|
||||||
|
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
Ok(output)
|
Ok(output)
|
||||||
} else {
|
} else {
|
||||||
Err(AocCliError::BadExitStatus(output))
|
Err(AocCommandError::BadExitStatus(output))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::template::{
|
||||||
ANSI_BOLD, ANSI_ITALIC, ANSI_RESET,
|
ANSI_BOLD, ANSI_ITALIC, ANSI_RESET,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn all_handler(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| {
|
(1..=25).for_each(|day| {
|
||||||
|
@ -13,7 +13,7 @@ pub fn all_handler(is_release: bool, is_timed: bool) {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}Day {}{}", ANSI_BOLD, day, ANSI_RESET);
|
println!("{ANSI_BOLD}Day {day}{ANSI_RESET}");
|
||||||
println!("------");
|
println!("------");
|
||||||
|
|
||||||
let output = child_commands::run_solution(day, is_timed, is_release).unwrap();
|
let output = child_commands::run_solution(day, is_timed, is_release).unwrap();
|
||||||
|
@ -27,16 +27,13 @@ pub fn all_handler(is_release: bool, is_timed: bool) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if is_timed {
|
if is_timed {
|
||||||
let total_millis = timings.iter().map(|x| x.total_nanos).sum::<f64>() / 1000000_f64;
|
let total_millis = timings.iter().map(|x| x.total_nanos).sum::<f64>() / 1_000_000_f64;
|
||||||
|
|
||||||
println!(
|
println!("\n{ANSI_BOLD}Total:{ANSI_RESET} {ANSI_ITALIC}{total_millis:.2}ms{ANSI_RESET}");
|
||||||
"\n{}Total:{} {}{:.2}ms{}",
|
|
||||||
ANSI_BOLD, ANSI_RESET, ANSI_ITALIC, total_millis, ANSI_RESET
|
|
||||||
);
|
|
||||||
|
|
||||||
if is_release {
|
if is_release {
|
||||||
match readme_benchmarks::update(timings, total_millis) {
|
match readme_benchmarks::update(timings, total_millis) {
|
||||||
Ok(_) => println!("Successfully updated README with benchmarks."),
|
Ok(()) => println!("Successfully updated README with benchmarks."),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
eprintln!("Failed to update readme with benchmarks.");
|
eprintln!("Failed to update readme with benchmarks.");
|
||||||
}
|
}
|
||||||
|
@ -58,9 +55,10 @@ impl From<std::io::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn get_path_for_bin(day: usize) -> String {
|
pub fn get_path_for_bin(day: usize) -> String {
|
||||||
let day_padded = format!("{:02}", day);
|
let day_padded = format!("{day:02}");
|
||||||
format!("./src/bin/{}.rs", day_padded)
|
format!("./src/bin/{day_padded}.rs")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All solutions live in isolated binaries.
|
/// All solutions live in isolated binaries.
|
||||||
|
@ -80,7 +78,7 @@ mod child_commands {
|
||||||
is_timed: bool,
|
is_timed: bool,
|
||||||
is_release: bool,
|
is_release: bool,
|
||||||
) -> Result<Vec<String>, Error> {
|
) -> Result<Vec<String>, Error> {
|
||||||
let day_padded = format!("{:02}", day);
|
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() {
|
||||||
|
@ -121,7 +119,7 @@ mod child_commands {
|
||||||
|
|
||||||
for line in stdout.lines() {
|
for line in stdout.lines() {
|
||||||
let line = line.unwrap();
|
let line = line.unwrap();
|
||||||
println!("{}", line);
|
println!("{line}");
|
||||||
output.push(line);
|
output.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +144,9 @@ mod child_commands {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (timing_str, nanos) = match parse_time(l) {
|
let Some((timing_str, nanos)) = parse_time(l) else {
|
||||||
Some(v) => v,
|
eprintln!("Could not parse timings from line: {l}");
|
||||||
None => {
|
return None;
|
||||||
eprintln!("Could not parse timings from line: {l}");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let part = l.split(':').next()?;
|
let part = l.split(':').next()?;
|
||||||
|
@ -188,8 +183,8 @@ mod child_commands {
|
||||||
let parsed_timing = match str_timing {
|
let parsed_timing = match str_timing {
|
||||||
s if s.contains("ns") => s.split("ns").next()?.parse::<f64>().ok(),
|
s if s.contains("ns") => s.split("ns").next()?.parse::<f64>().ok(),
|
||||||
s if s.contains("µs") => parse_to_float(s, "µs").map(|x| x * 1000_f64),
|
s if s.contains("µs") => parse_to_float(s, "µs").map(|x| x * 1000_f64),
|
||||||
s if s.contains("ms") => parse_to_float(s, "ms").map(|x| x * 1000000_f64),
|
s if s.contains("ms") => parse_to_float(s, "ms").map(|x| x * 1_000_000_f64),
|
||||||
s => parse_to_float(s, "s").map(|x| x * 1000000000_f64),
|
s => parse_to_float(s, "s").map(|x| x * 1_000_000_000_f64),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Some((str_timing, parsed_timing))
|
Some((str_timing, parsed_timing))
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::template::aoc_cli;
|
use crate::template::aoc_cli;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
pub fn download_handler(day: u8) {
|
pub fn handle(day: u8) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = aoc_cli::download(day) {
|
if let Err(e) = aoc_cli::download(day) {
|
||||||
eprintln!("failed to call aoc-cli: {}", e);
|
eprintln!("failed to call aoc-cli: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@ use std::process;
|
||||||
|
|
||||||
use crate::template::aoc_cli;
|
use crate::template::aoc_cli;
|
||||||
|
|
||||||
pub fn read_handler(day: u8) {
|
pub fn handle(day: u8) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = aoc_cli::read(day) {
|
if let Err(e) = aoc_cli::read(day) {
|
||||||
eprintln!("failed to call aoc-cli: {}", e);
|
eprintln!("failed to call aoc-cli: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,27 +40,27 @@ 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 scaffold_handler(day: u8) {
|
pub fn handle(day: u8) {
|
||||||
let day_padded = format!("{:02}", day);
|
let day_padded = format!("{day:02}");
|
||||||
|
|
||||||
let input_path = format!("data/inputs/{}.txt", day_padded);
|
let input_path = format!("data/inputs/{day_padded}.txt");
|
||||||
let example_path = format!("data/examples/{}.txt", day_padded);
|
let example_path = format!("data/examples/{day_padded}.txt");
|
||||||
let module_path = format!("src/bin/{}.rs", day_padded);
|
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,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to create module file: {}", e);
|
eprintln!("Failed to create module file: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match file.write_all(MODULE_TEMPLATE.replace("DAY", &day.to_string()).as_bytes()) {
|
match file.write_all(MODULE_TEMPLATE.replace("DAY", &day.to_string()).as_bytes()) {
|
||||||
Ok(_) => {
|
Ok(()) => {
|
||||||
println!("Created module file \"{}\"", &module_path);
|
println!("Created module file \"{}\"", &module_path);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to write module contents: {}", e);
|
eprintln!("Failed to write module contents: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ pub fn scaffold_handler(day: u8) {
|
||||||
println!("Created empty input file \"{}\"", &input_path);
|
println!("Created empty input file \"{}\"", &input_path);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to create input file: {}", e);
|
eprintln!("Failed to create input file: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ pub fn scaffold_handler(day: u8) {
|
||||||
println!("Created empty example file \"{}\"", &example_path);
|
println!("Created empty example file \"{}\"", &example_path);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to create example file: {}", e);
|
eprintln!("Failed to create example file: {e}");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
pub fn solve_handler(day: u8, release: bool, time: bool, submit_part: Option<u8>) {
|
pub fn handle(day: u8, release: bool, time: bool, submit_part: Option<u8>) {
|
||||||
let day_padded = format!("{:02}", day);
|
let day_padded = format!("{day:02}");
|
||||||
|
|
||||||
let mut cmd_args = vec!["run".to_string(), "--bin".to_string(), day_padded];
|
let mut cmd_args = vec!["run".to_string(), "--bin".to_string(), day_padded];
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ pub fn solve_handler(day: u8, release: bool, time: bool, submit_part: Option<u8>
|
||||||
|
|
||||||
if let Some(submit_part) = submit_part {
|
if let Some(submit_part) = submit_part {
|
||||||
cmd_args.push("--submit".to_string());
|
cmd_args.push("--submit".to_string());
|
||||||
cmd_args.push(submit_part.to_string())
|
cmd_args.push(submit_part.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if time {
|
if time {
|
||||||
|
|
|
@ -10,12 +10,12 @@ 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.
|
||||||
pub fn read_file(folder: &str, day: u8) -> String {
|
#[must_use] pub fn read_file(folder: &str, day: u8) -> String {
|
||||||
let cwd = env::current_dir().unwrap();
|
let cwd = env::current_dir().unwrap();
|
||||||
let filepath = cwd
|
let filepath = cwd
|
||||||
.join("data")
|
.join("data")
|
||||||
.join(folder)
|
.join(folder)
|
||||||
.join(format!("{:02}.txt", day));
|
.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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,9 @@ pub struct TablePosition {
|
||||||
pos_end: usize,
|
pos_end: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_path_for_bin(day: usize) -> String {
|
#[must_use] pub fn get_path_for_bin(day: usize) -> String {
|
||||||
let day_padded = format!("{:02}", day);
|
let day_padded = format!("{day:02}");
|
||||||
format!("./src/bin/{}.rs", day_padded)
|
format!("./src/bin/{day_padded}.rs")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn locate_table(readme: &str) -> Result<TablePosition, Error> {
|
fn locate_table(readme: &str) -> Result<TablePosition, Error> {
|
||||||
|
@ -62,12 +62,12 @@ fn construct_table(prefix: &str, timings: Vec<Timings>, total_millis: f64) -> St
|
||||||
let mut lines: Vec<String> = vec![
|
let mut lines: Vec<String> = vec![
|
||||||
MARKER.into(),
|
MARKER.into(),
|
||||||
header,
|
header,
|
||||||
"".into(),
|
String::new(),
|
||||||
"| Day | Part 1 | Part 2 |".into(),
|
"| Day | Part 1 | Part 2 |".into(),
|
||||||
"| :---: | :---: | :---: |".into(),
|
"| :---: | :---: | :---: |".into(),
|
||||||
];
|
];
|
||||||
|
|
||||||
timings.into_iter().for_each(|timing| {
|
for timing in timings {
|
||||||
let path = get_path_for_bin(timing.day);
|
let path = get_path_for_bin(timing.day);
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"| [Day {}]({}) | `{}` | `{}` |",
|
"| [Day {}]({}) | `{}` | `{}` |",
|
||||||
|
@ -76,10 +76,10 @@ fn construct_table(prefix: &str, timings: Vec<Timings>, total_millis: f64) -> St
|
||||||
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())
|
||||||
));
|
));
|
||||||
});
|
}
|
||||||
|
|
||||||
lines.push("".into());
|
lines.push(String::new());
|
||||||
lines.push(format!("**Total: {:.2}ms**", total_millis));
|
lines.push(format!("**Total: {total_millis:.2}ms**"));
|
||||||
lines.push(MARKER.into());
|
lines.push(MARKER.into());
|
||||||
|
|
||||||
lines.join("\n")
|
lines.join("\n")
|
||||||
|
|
|
@ -9,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: u8, part: u8) {
|
||||||
let part_str = format!("Part {}", part);
|
let part_str = format!("Part {part}");
|
||||||
|
|
||||||
let (result, duration, samples) =
|
let (result, duration, samples) =
|
||||||
run_timed(func, input, |result| print_result(result, &part_str, ""));
|
run_timed(func, input, |result| print_result(result, &part_str, ""));
|
||||||
|
@ -35,9 +35,10 @@ fn run_timed<I: Clone, T>(
|
||||||
|
|
||||||
hook(&result);
|
hook(&result);
|
||||||
|
|
||||||
let run = match std::env::args().any(|x| x == "--time") {
|
let run = if std::env::args().any(|x| x == "--time") {
|
||||||
true => bench(func, input, &base_time),
|
bench(func, input, &base_time)
|
||||||
false => (base_time, 1),
|
} else {
|
||||||
|
(base_time, 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
(result, run.0, run.1)
|
(result, run.0, run.1)
|
||||||
|
@ -46,7 +47,7 @@ fn run_timed<I: Clone, T>(
|
||||||
fn bench<I: Clone, T>(func: impl Fn(I) -> T, input: I, base_time: &Duration) -> (Duration, u128) {
|
fn bench<I: Clone, T>(func: impl Fn(I) -> T, input: I, base_time: &Duration) -> (Duration, u128) {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
||||||
print!(" > {}benching{}", ANSI_ITALIC, ANSI_RESET);
|
print!(" > {ANSI_ITALIC}benching{ANSI_RESET}");
|
||||||
let _ = stdout.flush();
|
let _ = stdout.flush();
|
||||||
|
|
||||||
let bench_iterations = cmp::min(
|
let bench_iterations = cmp::min(
|
||||||
|
@ -68,20 +69,25 @@ fn bench<I: Clone, T>(func: impl Fn(I) -> T, input: I, base_time: &Duration) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
Duration::from_nanos(average_duration(&timers) as u64),
|
Duration::from_nanos(average_duration(&timers) as u64),
|
||||||
bench_iterations,
|
bench_iterations,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn average_duration(numbers: &[Duration]) -> u128 {
|
fn average_duration(numbers: &[Duration]) -> u128 {
|
||||||
numbers.iter().map(|d| d.as_nanos()).sum::<u128>() / numbers.len() as u128
|
numbers
|
||||||
|
.iter()
|
||||||
|
.map(std::time::Duration::as_nanos)
|
||||||
|
.sum::<u128>()
|
||||||
|
/ numbers.len() as u128
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_duration(duration: &Duration, samples: u128) -> String {
|
fn format_duration(duration: &Duration, samples: u128) -> String {
|
||||||
if samples == 1 {
|
if samples == 1 {
|
||||||
format!(" ({:.1?})", duration)
|
format!(" ({duration:.1?})")
|
||||||
} else {
|
} else {
|
||||||
format!(" ({:.1?} @ {} samples)", duration, samples)
|
format!(" ({duration:.1?} @ {samples} samples)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,33 +97,30 @@ fn print_result<T: Display>(result: &Option<T>, part: &str, duration_str: &str)
|
||||||
match result {
|
match result {
|
||||||
Some(result) => {
|
Some(result) => {
|
||||||
if result.to_string().contains('\n') {
|
if result.to_string().contains('\n') {
|
||||||
let str = format!("{}: ▼ {}", part, duration_str);
|
let str = format!("{part}: ▼ {duration_str}");
|
||||||
if is_intermediate_result {
|
if is_intermediate_result {
|
||||||
print!("{}", str);
|
print!("{str}");
|
||||||
} else {
|
} else {
|
||||||
print!("\r");
|
print!("\r");
|
||||||
println!("{}", str);
|
println!("{str}");
|
||||||
println!("{}", result);
|
println!("{result}");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let str = format!(
|
let str = format!("{part}: {ANSI_BOLD}{result}{ANSI_RESET}{duration_str}");
|
||||||
"{}: {}{}{}{}",
|
|
||||||
part, ANSI_BOLD, result, ANSI_RESET, duration_str
|
|
||||||
);
|
|
||||||
if is_intermediate_result {
|
if is_intermediate_result {
|
||||||
print!("{}", str);
|
print!("{str}");
|
||||||
} else {
|
} else {
|
||||||
print!("\r");
|
print!("\r");
|
||||||
println!("{}", str);
|
println!("{str}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if is_intermediate_result {
|
if is_intermediate_result {
|
||||||
print!("{}: ✖", part);
|
print!("{part}: ✖");
|
||||||
} else {
|
} else {
|
||||||
print!("\r");
|
print!("\r");
|
||||||
println!("{}: ✖ ", part);
|
println!("{part}: ✖ ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +133,7 @@ fn submit_result<T: Display>(
|
||||||
result: T,
|
result: T,
|
||||||
day: u8,
|
day: u8,
|
||||||
part: u8,
|
part: u8,
|
||||||
) -> Option<Result<Output, aoc_cli::AocCliError>> {
|
) -> Option<Result<Output, aoc_cli::AocCommandError>> {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
if !args.contains(&"--submit".into()) {
|
if !args.contains(&"--submit".into()) {
|
||||||
|
@ -143,12 +146,10 @@ fn submit_result<T: Display>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let part_index = args.iter().position(|x| x == "--submit").unwrap() + 1;
|
let part_index = args.iter().position(|x| x == "--submit").unwrap() + 1;
|
||||||
let part_submit = match args[part_index].parse::<u8>() {
|
|
||||||
Ok(x) => x,
|
let Ok(part_submit) = args[part_index].parse::<u8>() else {
|
||||||
Err(_) => {
|
eprintln!("Unexpected command-line input. Format: cargo solve 1 --submit 1");
|
||||||
eprintln!("Unexpected command-line input. Format: cargo solve 1 --submit 1");
|
process::exit(1);
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if part_submit != part {
|
if part_submit != part {
|
||||||
|
|
Loading…
Add table
Reference in a new issue