diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd7ef66..8b39fe7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,6 +58,4 @@ it finds, and make sure your code is properly formatted with `cargo fmt`. - Give user the option to overwrite or rename a downloaded file if it already exists (rather than fail). -- Generate shell completion - - Add tests and documentation! \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index b09bf67..d6d4eb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,23 +43,24 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -76,17 +77,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -95,6 +96,7 @@ version = "0.12.2" dependencies = [ "aoc-client", "clap", + "clap_complete", "env_logger", "exit-code", "log", @@ -202,14 +204,14 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] name = "clap" -version = "4.4.8" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -217,9 +219,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -227,11 +229,20 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -241,9 +252,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "colorchoice" @@ -259,7 +270,7 @@ checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -343,7 +354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -490,9 +501,9 @@ checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -675,9 +686,15 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.9" @@ -819,7 +836,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -954,7 +971,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1182,7 +1199,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1206,7 +1223,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1319,7 +1336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1350,9 +1367,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" @@ -1407,7 +1424,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1488,7 +1505,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2 0.5.5", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1740,7 +1757,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1749,7 +1766,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1758,13 +1784,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1773,42 +1815,90 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winreg" version = "0.50.0" @@ -1816,7 +1906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b45cb10..f1bbd66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,8 @@ path = "src/main.rs" [dependencies] aoc-client = { version = "0.2", path = "aoc-client" } -clap = { version = "4", features = ["cargo", "color", "derive"]} +clap = { version = "4", features = ["cargo", "color", "derive"] } +clap_complete = "4" exit-code = "1.0" env_logger = "0.10" log = "0.4" diff --git a/README.md b/README.md index 93b3425..674dbdd 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ Commands: read Read puzzle statement (the default command) [aliases: r] submit Submit puzzle answer [aliases: s] private-leaderboard Show the state of a private leaderboard [aliases: p] + generate-completion Generate a completion script. Enable completion using `source <(aoc generate-completion )` help Print this message or the help of the given subcommand(s) Options: @@ -133,10 +134,11 @@ Options: -P, --puzzle-only Download puzzle description only -i, --input-file Path where to save puzzle input [default: input] -p, --puzzle-file Path where to save puzzle description [default: puzzle.md] + -m, --show-html-markup Show HTML markup including links -q, --quiet Restrict log messages to errors only --debug Enable debug logging - -h, --help Print help information - -V, --version Print version information + -h, --help Print help + -V, --version Print version ``` ``` @@ -160,9 +162,28 @@ Options: -P, --puzzle-only Download puzzle description only -i, --input-file Path where to save puzzle input [default: input] -p, --puzzle-file Path where to save puzzle description [default: puzzle.md] + -m, --show-html-markup Show HTML markup including links -q, --quiet Restrict log messages to errors only --debug Enable debug logging - -h, --help Print help information + -h, --help Print help +``` + +### Generate command-line completion script + +Generate a script to enable completion in your shell. Supports shells listed +[here](https://docs.rs/clap_complete/4.5.38/clap_complete/aot/enum.Shell.html). + +``` +# source <(aoc generate-completion zsh) + +# aoc +calendar c -- Show Advent of Code calendar and stars collected +download d -- Save puzzle description and input to files +generate-completion -- Generate a completion script. Enable completion using `source <(aoc generate-completion )` +help -- Print this message or the help of the given subcommand(s) +private-leaderboard p -- Show the state of a private leaderboard +read r -- Read puzzle statement (the default command) +submit s -- Submit puzzle answer ``` ### Read puzzle description diff --git a/src/args.rs b/src/args.rs index fba86ac..337accf 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,5 +1,6 @@ use aoc_client::{LeaderboardId, PuzzleDay, PuzzleYear}; use clap::{Parser, Subcommand}; +use clap_complete::Shell; #[derive(Parser, Debug)] #[command(version, about, infer_subcommands = true)] @@ -108,4 +109,15 @@ pub enum Command { /// Private leaderboard ID leaderboard_id: LeaderboardId, }, + + /// Generate a completion script. Enable completion using + /// `source <(aoc generate-completion )` + GenerateCompletion(GenerateCompletionCommand), +} + +#[derive(Parser, Debug)] +pub(crate) struct GenerateCompletionCommand { + /// The shell for which to generate the completion script. + #[clap(value_enum)] + pub(crate) shell: Shell, } diff --git a/src/main.rs b/src/main.rs index ab9e7f0..0635e14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,13 @@ mod args; use aoc_client::{AocClient, AocError, AocResult}; -use args::{Args, Command}; -use clap::{crate_description, crate_name, Parser}; +use args::{Args, Command, GenerateCompletionCommand}; +use clap::{crate_description, crate_name, CommandFactory, Parser}; +use clap_complete::generate; use env_logger::{Builder, Env}; use exit_code::*; use log::{error, info, warn, LevelFilter}; +use std::io; use std::process::exit; fn main() { @@ -93,22 +95,37 @@ fn build_client(args: &Args) -> AocResult { fn run(args: &Args, client: AocClient) -> AocResult<()> { match &args.command { - Some(Command::Calendar) => client.show_calendar(), - Some(Command::Download) => { - if !args.input_only { - client.save_puzzle_markdown()?; + Some(command) => match command { + Command::Calendar => client.show_calendar(), + Command::Download => { + if !args.input_only { + client.save_puzzle_markdown()?; + } + if !args.puzzle_only { + client.save_input()?; + } + Ok(()) } - if !args.puzzle_only { - client.save_input()?; + Command::Submit { part, answer } => { + client.submit_answer_and_show_outcome(part, answer) } - Ok(()) - } - Some(Command::Submit { part, answer }) => { - client.submit_answer_and_show_outcome(part, answer) - } - Some(Command::PrivateLeaderboard { leaderboard_id }) => { - client.show_private_leaderboard(*leaderboard_id) - } - _ => client.show_puzzle(), + Command::PrivateLeaderboard { leaderboard_id } => { + client.show_private_leaderboard(*leaderboard_id) + } + Command::Read => client.show_puzzle(), + Command::GenerateCompletion(command) => { + generate_completion(command); + Ok(()) + } + }, + None => client.show_puzzle(), } } + +/// Generate a completion script. +fn generate_completion(command: &GenerateCompletionCommand) { + let shell = command.shell; + let mut app = Args::command(); + let bin_name = env!("CARGO_BIN_NAME"); + generate(shell, &mut app, bin_name, &mut io::stdout()); +}