From 9e8672d9b496b26414437370a6fa5657ccd10132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Wed, 7 Dec 2022 21:37:57 +0100 Subject: [PATCH 1/7] feat: add `--submit ` to `cargo solve` --- .cargo/{config => config.toml} | 5 +- src/bin/download.rs | 4 +- src/bin/read.rs | 4 +- src/bin/scaffold.rs | 4 +- src/bin/solve.rs | 58 +++++++++++++++++++++++ src/lib.rs | 84 +++++++++++++++++++++++++++------- 6 files changed, 134 insertions(+), 25 deletions(-) rename .cargo/{config => config.toml} (70%) create mode 100644 src/bin/solve.rs diff --git a/.cargo/config b/.cargo/config.toml similarity index 70% rename from .cargo/config rename to .cargo/config.toml index 03ad300..205746e 100644 --- a/.cargo/config +++ b/.cargo/config.toml @@ -3,5 +3,8 @@ scaffold = "run --bin scaffold --quiet --release -- " download = "run --bin download --quiet --release -- " read = "run --bin read --quiet --release -- " -solve = "run --bin" +solve = "run --bin solve --quiet --release -- " all = "run" + +[env] +AOC_YEAR = "2022" diff --git a/src/bin/download.rs b/src/bin/download.rs index 6632dc6..7eeaa0c 100644 --- a/src/bin/download.rs +++ b/src/bin/download.rs @@ -7,14 +7,12 @@ use std::process; struct Args { day: u8, - year: Option, } fn parse_args() -> Result { let mut args = pico_args::Arguments::from_env(); Ok(Args { day: args.free_from_str()?, - year: args.opt_value_from_str(["-y", "--year"])?, }) } @@ -32,7 +30,7 @@ fn main() { process::exit(1); } - match aoc_cli::download(args.day, args.year) { + match aoc_cli::download(args.day) { Ok(cmd_output) => { if !cmd_output.status.success() { process::exit(1); diff --git a/src/bin/read.rs b/src/bin/read.rs index d0a4321..e9393f0 100644 --- a/src/bin/read.rs +++ b/src/bin/read.rs @@ -7,14 +7,12 @@ use std::process; struct Args { day: u8, - year: Option, } fn parse_args() -> Result { let mut args = pico_args::Arguments::from_env(); Ok(Args { day: args.free_from_str()?, - year: args.opt_value_from_str(["-y", "--year"])?, }) } @@ -32,7 +30,7 @@ fn main() { process::exit(1); } - match aoc_cli::read(args.day, args.year) { + match aoc_cli::read(args.day) { Ok(cmd_output) => { if !cmd_output.status.success() { process::exit(1); diff --git a/src/bin/scaffold.rs b/src/bin/scaffold.rs index 0c77b4d..802372c 100644 --- a/src/bin/scaffold.rs +++ b/src/bin/scaffold.rs @@ -18,8 +18,8 @@ pub fn part_two(input: &str) -> Option { fn main() { let input = &advent_of_code::read_file("inputs", DAY); - advent_of_code::solve!(1, part_one, input); - advent_of_code::solve!(2, part_two, input); + advent_of_code::solve!(DAY, 1, part_one, input); + advent_of_code::solve!(DAY, 2, part_two, input); } #[cfg(test)] diff --git a/src/bin/solve.rs b/src/bin/solve.rs new file mode 100644 index 0000000..4c866b7 --- /dev/null +++ b/src/bin/solve.rs @@ -0,0 +1,58 @@ +/* + * This file contains template code. + * There is no need to edit this file unless you want to change template functionality. + */ + +use std::process::{self, Command, Stdio}; + +struct Args { + day: u8, + release: bool, + submit: Option, +} + +fn parse_args() -> Result { + let mut args = pico_args::Arguments::from_env(); + Ok(Args { + day: args.free_from_str()?, + release: args.contains("--release"), + submit: args.opt_value_from_str("--submit")?, + }) +} + +fn run_solution(day: u8, release: bool, submit_part: Option) -> Result<(), std::io::Error> { + let day_padded = format!("{:02}", day); + + let mut cmd_args = vec!["run".to_string(), "--bin".to_string(), day_padded]; + + if release { + cmd_args.push("--release".to_string()); + } + + if let Some(submit_part) = submit_part { + cmd_args.push("--".to_string()); + cmd_args.push("--submit".to_string()); + cmd_args.push(submit_part.to_string()) + } + + let mut cmd = Command::new("cargo") + .args(&cmd_args) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn()?; + + cmd.wait()?; + Ok(()) +} + +fn main() { + let args = match parse_args() { + Ok(args) => args, + Err(e) => { + eprintln!("Failed to process arguments: {}", e); + process::exit(1); + } + }; + + run_solution(args.day, args.release, args.submit).unwrap(); +} diff --git a/src/lib.rs b/src/lib.rs index 266177b..b10041f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,10 +14,42 @@ pub const ANSI_RESET: &str = "\x1b[0m"; #[macro_export] macro_rules! solve { - ($part:expr, $solver:ident, $input:expr) => {{ - use advent_of_code::{ANSI_BOLD, ANSI_ITALIC, ANSI_RESET}; + ($day:expr, $part:expr, $solver:ident, $input:expr) => {{ + use advent_of_code::{ANSI_BOLD, ANSI_ITALIC, ANSI_RESET, aoc_cli}; use std::fmt::Display; use std::time::Instant; + use std::env; + use std::process; + + fn submit_if_requested(result: T) { + let args: Vec = env::args().collect(); + + if args.contains(&"--submit".into()) { + 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); + } + + if args.len() < 3 { + eprintln!("Unexpected command-line input. Format: cargo solve 1 --submit 1"); + process::exit(1); + } + + let part_index = args.iter().position(|x| x == "--submit").unwrap() + 1; + let part_submit = match args[part_index].parse::() { + Ok(x) => x, + Err(_) => { + eprintln!("Unexpected command-line input. Format: cargo solve 1 --submit 1"); + process::exit(1); + } + }; + + if part_submit == $part { + println!("Submitting puzzle answer for part {}...", $part); + aoc_cli::submit($day, $part, result).unwrap(); + } + } + } fn print_result(func: impl FnOnce(&str) -> Option, input: &str) { let timer = Instant::now(); @@ -29,6 +61,7 @@ macro_rules! solve { "{} {}(elapsed: {:.2?}){}", result, ANSI_ITALIC, elapsed, ANSI_RESET ); + submit_if_requested(result); } None => { println!("not solved.") @@ -127,10 +160,10 @@ mod tests { pub mod aoc_cli { use std::{ fmt::Display, - fs::create_dir_all, process::{Command, Output, Stdio}, }; + #[derive(Debug)] pub enum AocCliError { CommandNotFound, CommandNotCallable, @@ -159,17 +192,25 @@ pub mod aoc_cli { Ok(()) } - pub fn read(day: u8, year: Option) -> Result { - // TODO: output local puzzle if present. - let args = build_args("read", &[], day, year); + pub fn read(day: u8) -> Result { + let puzzle_path = get_puzzle_path(day); + + let args = build_args( + "read", + &[ + "--description-only".into(), + "--puzzle-file".into(), + puzzle_path, + ], + day, + ); + call_aoc_cli(&args) } - pub fn download(day: u8, year: Option) -> Result { + pub fn download(day: u8) -> Result { let input_path = get_input_path(day); - let puzzle_path = get_puzzle_path(day); - create_dir_all("src/puzzles").map_err(|_| AocCliError::IoError)?; let args = build_args( "download", @@ -181,7 +222,6 @@ pub mod aoc_cli { puzzle_path.to_string(), ], day, - year, ); let output = call_aoc_cli(&args)?; @@ -196,6 +236,14 @@ pub mod aoc_cli { } } + pub fn submit(day: u8, part: u8, result: T) -> Result { + // workaround: the argument order is inverted for submit. + let mut args = build_args("submit", &[], day); + args.push(part.to_string()); + args.push(result.to_string()); + call_aoc_cli(&args) + } + fn get_input_path(day: u8) -> String { let day_padded = format!("{:02}", day); format!("src/inputs/{}.txt", day_padded) @@ -206,10 +254,17 @@ pub mod aoc_cli { format!("src/puzzles/{}.md", day_padded) } - fn build_args(command: &str, args: &[String], day: u8, year: Option) -> Vec { + fn get_year() -> Option { + match std::env::var("AOC_YEAR") { + Ok(x) => x.parse().ok().or(None), + Err(_) => None, + } + } + + fn build_args(command: &str, args: &[String], day: u8) -> Vec { let mut cmd_args = args.to_vec(); - if let Some(year) = year { + if let Some(year) = get_year() { cmd_args.push("--year".into()); cmd_args.push(year.to_string()); } @@ -220,10 +275,7 @@ pub mod aoc_cli { } fn call_aoc_cli(args: &[String]) -> Result { - if cfg!(debug_assertions) { - println!("Calling >aoc with: {}", args.join(" ")); - } - + println!("Calling >aoc with: {}", args.join(" ")); Command::new("aoc") .args(args) .stdout(Stdio::inherit()) From 392da4dba7ee49f9914023525f51e903d1feab94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Wed, 7 Dec 2022 21:47:07 +0100 Subject: [PATCH 2/7] fix: re-add debug guards --- src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b10041f..e4478aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,6 +193,7 @@ pub mod aoc_cli { } pub fn read(day: u8) -> Result { + // TODO: output local puzzle if present. let puzzle_path = get_puzzle_path(day); let args = build_args( @@ -275,7 +276,10 @@ pub mod aoc_cli { } fn call_aoc_cli(args: &[String]) -> Result { - println!("Calling >aoc with: {}", args.join(" ")); + if cfg!(debug_assertions) { + println!("Calling >aoc with: {}", args.join(" ")); + } + Command::new("aoc") .args(args) .stdout(Stdio::inherit()) From bbb0f2ceafb229038f95959e207210fdcc9d12a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:37:11 +0200 Subject: [PATCH 3/7] docs: add documentation --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 26851a4..f018dc7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ This template supports all major OS (macOS, Linux, Windows). ## Usage +> **Note** +> If you are solving a previous year's aoc and want to use the `aoc-cli` integration, change the `AOC_YEAR` variable in `.cargo/config.toml` to reflect that. + ### Scaffold a day ```sh @@ -96,6 +99,13 @@ cargo solve Displayed _timings_ show the raw execution time of your solution without overhead (e.g. file reads). +#### Submitting solutions + +> **Note** +> This requires [installing the aoc-cli crate](#download-puzzle-inputs-via-aoc-cli). + +In order to submit part of a solution for checking, append the `--submit ` option to the `solve` command. + ### Run all solutions ```sh @@ -140,6 +150,7 @@ cargo fmt ```sh cargo clippy ``` +## Optional template features ### Read puzzle description in terminal @@ -158,8 +169,6 @@ cargo read To read inputs for previous years, append the `--year/-y` flag. _(example: `cargo read 1 --year 2020`)_ -## Optional template features - ### Download puzzle inputs via aoc-cli 1. Install [`aoc-cli`](https://github.com/scarvalhojr/aoc-cli/) via cargo: `cargo install aoc-cli --version 0.7.0` From dfafa5fbb56a08c4a6f473e9363d3c3cb8d19bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:37:31 +0200 Subject: [PATCH 4/7] fix: fix clippy lints --- src/bin/scaffold.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/scaffold.rs b/src/bin/scaffold.rs index fda98bf..348f73b 100644 --- a/src/bin/scaffold.rs +++ b/src/bin/scaffold.rs @@ -8,7 +8,7 @@ use std::{ process, }; -const MODULE_TEMPLATE: &str = r###"pub fn part_one(input: &str) -> Option { +const MODULE_TEMPLATE: &str = r#"pub fn part_one(input: &str) -> Option { None } @@ -38,7 +38,7 @@ mod tests { assert_eq!(part_two(&input), None); } } -"###; +"#; fn parse_args() -> Result { let mut args = pico_args::Arguments::from_env(); From ea74da30e80bff9233cee8f7a1274b3fabd1c3b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:38:23 +0200 Subject: [PATCH 5/7] docs: update --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index f018dc7..268b2c9 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This template supports all major OS (macOS, Linux, Windows). 1. Open [the template repository](https://github.com/fspoettel/advent-of-code-rust) on Github. 2. Click [Use this template](https://github.com/fspoettel/advent-of-code-rust/generate) and create your repository. 3. Clone your repository to your computer. +4. If you are solving a previous year's aoc and want to use the `aoc-cli` integration, change the `AOC_YEAR` variable in `.cargo/config.toml` to reflect that. ### Setup rust 💻 @@ -30,9 +31,6 @@ This template supports all major OS (macOS, Linux, Windows). ## Usage -> **Note** -> If you are solving a previous year's aoc and want to use the `aoc-cli` integration, change the `AOC_YEAR` variable in `.cargo/config.toml` to reflect that. - ### Scaffold a day ```sh From 217531a820b5efda68bd7d30c102b882f43fdc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:40:15 +0200 Subject: [PATCH 6/7] docs: update year --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 268b2c9..b04dc81 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,6 @@ cargo download # 🎄 Successfully wrote puzzle to "src/puzzles/01.md". ``` -To download inputs for previous years, append the `--year/-y` flag. _(example: `cargo download 1 --year 2020`)_ - Puzzle descriptions are stored in `src/puzzles` as markdown files. Puzzle inputs are not checked into git. [Reasoning](https://old.reddit.com/r/adventofcode/comments/k99rod/sharing_input_data_were_we_requested_not_to/gf2ukkf/?context=3). ### Run solutions for a day @@ -165,8 +163,6 @@ cargo read # ...the input... ``` -To read inputs for previous years, append the `--year/-y` flag. _(example: `cargo read 1 --year 2020`)_ - ### Download puzzle inputs via aoc-cli 1. Install [`aoc-cli`](https://github.com/scarvalhojr/aoc-cli/) via cargo: `cargo install aoc-cli --version 0.7.0` From 8237301c877553ad15cb2fee0aebed7292ca4838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sp=C3=B6ttel?= <1682504+fspoettel@users.noreply.github.com> Date: Sat, 21 Oct 2023 14:43:50 +0200 Subject: [PATCH 7/7] docs: update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b04dc81..f13ff7d 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ cargo read ### Download puzzle inputs via aoc-cli -1. Install [`aoc-cli`](https://github.com/scarvalhojr/aoc-cli/) via cargo: `cargo install aoc-cli --version 0.7.0` +1. Install [`aoc-cli`](https://github.com/scarvalhojr/aoc-cli/) via cargo: `cargo install aoc-cli --version 0.12.0` 2. Create an `.adventofcode.session` file in your home directory and paste your session cookie[^1] into it. To get this, press F12 anywhere on the Advent of Code website to open your browser developer tools. Look in your Cookies under the Application or Storage tab, and copy out the `session` cookie value. Once installed, you can use the [download command](#download-input--description-for-a-day).