feat: add --submit <part>
option to cargo solve
(#25)
* remove `--year` flag in favor of `.config/config.toml` * cleanup option handling for `cargo solve`
This commit is contained in:
parent
1c8ea27eae
commit
d10ec0573e
7 changed files with 145 additions and 29 deletions
|
@ -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"
|
17
README.md
17
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 💻
|
||||
|
||||
|
@ -71,8 +72,6 @@ cargo download <day>
|
|||
# 🎄 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
|
||||
|
@ -96,6 +95,13 @@ cargo solve <day>
|
|||
|
||||
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 <part>` option to the `solve` command.
|
||||
|
||||
### Run all solutions
|
||||
|
||||
```sh
|
||||
|
@ -140,6 +146,7 @@ cargo fmt
|
|||
```sh
|
||||
cargo clippy
|
||||
```
|
||||
## Optional template features
|
||||
|
||||
### Read puzzle description in terminal
|
||||
|
||||
|
@ -156,13 +163,9 @@ cargo read <day>
|
|||
# ...the input...
|
||||
```
|
||||
|
||||
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`
|
||||
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).
|
||||
|
|
|
@ -7,14 +7,12 @@ use std::process;
|
|||
|
||||
struct Args {
|
||||
day: u8,
|
||||
year: Option<u16>,
|
||||
}
|
||||
|
||||
fn parse_args() -> Result<Args, pico_args::Error> {
|
||||
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);
|
||||
|
|
|
@ -7,14 +7,12 @@ use std::process;
|
|||
|
||||
struct Args {
|
||||
day: u8,
|
||||
year: Option<u16>,
|
||||
}
|
||||
|
||||
fn parse_args() -> Result<Args, pico_args::Error> {
|
||||
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);
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
|||
process,
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,8 @@ pub fn part_two(input: &str) -> Option<u32> {
|
|||
|
||||
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)]
|
||||
|
@ -38,7 +38,7 @@ mod tests {
|
|||
assert_eq!(part_two(&input), None);
|
||||
}
|
||||
}
|
||||
"###;
|
||||
"#;
|
||||
|
||||
fn parse_args() -> Result<u8, pico_args::Error> {
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
|
|
58
src/bin/solve.rs
Normal file
58
src/bin/solve.rs
Normal file
|
@ -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<u8>,
|
||||
}
|
||||
|
||||
fn parse_args() -> Result<Args, pico_args::Error> {
|
||||
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<u8>) -> 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();
|
||||
}
|
78
src/lib.rs
78
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<T: Display>(result: T) {
|
||||
let args: Vec<String> = 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::<u8>() {
|
||||
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<T: Display>(func: impl FnOnce(&str) -> Option<T>, 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,26 @@ pub mod aoc_cli {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(day: u8, year: Option<u16>) -> Result<Output, AocCliError> {
|
||||
pub fn read(day: u8) -> Result<Output, AocCliError> {
|
||||
// TODO: output local puzzle if present.
|
||||
let args = build_args("read", &[], day, year);
|
||||
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<u16>) -> Result<Output, AocCliError> {
|
||||
pub fn download(day: u8) -> Result<Output, AocCliError> {
|
||||
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 +223,6 @@ pub mod aoc_cli {
|
|||
puzzle_path.to_string(),
|
||||
],
|
||||
day,
|
||||
year,
|
||||
);
|
||||
|
||||
let output = call_aoc_cli(&args)?;
|
||||
|
@ -196,6 +237,14 @@ pub mod aoc_cli {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn submit<T: Display>(day: u8, part: u8, result: T) -> Result<Output, AocCliError> {
|
||||
// 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!("{day:02}");
|
||||
format!("src/inputs/{day_padded}.txt")
|
||||
|
@ -206,10 +255,17 @@ pub mod aoc_cli {
|
|||
format!("src/puzzles/{day_padded}.md")
|
||||
}
|
||||
|
||||
fn build_args(command: &str, args: &[String], day: u8, year: Option<u16>) -> Vec<String> {
|
||||
fn get_year() -> Option<u16> {
|
||||
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<String> {
|
||||
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());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue