diff --git a/completions/bash b/completions/bash new file mode 100644 index 0000000..175a5d8 --- /dev/null +++ b/completions/bash @@ -0,0 +1,63 @@ +#/usr/bin/env bash + +_remove_opts() { + local opt new_opts + for opt in ${available_opts}; do + if [[ $opt != $1 && $opt != $2 ]]; then + new_opts+="$opt " + fi + done + available_opts=$new_opts +} + +_doby_completion() { + local cur="${COMP_WORDS[COMP_CWORD]}" + local opts="-f --force-encrypt -i --interactive -h --help -V --version --password -t --time-cost -m --memory-cost -p --parallelism -b --block-size -c --cipher" + if [[ ${cur} == -* ]]; then + local i available_opts=$opts + for i in ${COMP_WORDS[@]}; do + if [[ ${opts[*]} =~ $i ]]; then + case $i in + "-f"|"--force-encrypt") + _remove_opts "-f" "--force-encrypt" + ;; + "-i"|"--interactive") + _remove_opts "-i" "--interactive" + ;; + "-h"|"--help") + _remove_opts "-h" "--help" + ;; + "-V"|"--version") + _remove_opts "-V" "--version" + ;; + "--password") + _remove_opts "--password" + ;; + "-t"|"--time-cost") + _remove_opts "-t" "--time-cost" + ;; + "-m"|"--memory-cost") + _remove_opts "-m" "--memory-cost" + ;; + "-p"|"--parallelism") + _remove_opts "-p" "--parallelism" + ;; + "-b"|"--block-size") + _remove_opts "-b" "--block-size" + ;; + "-c"|"--cipher") + _remove_opts "-c" "--cipher" + ;; + esac + fi + done + COMPREPLY=($(compgen -W "${available_opts}" -- "${cur}")) + else + local prev="${COMP_WORDS[COMP_CWORD-1]}" + if [[ ${prev} == "-c" || ${prev} == "--cipher" ]]; then + COMPREPLY=($(compgen -W "aes xchacha20" -- "${cur}")) + fi + fi +} + +complete -F _doby_completion -o bashdefault -o default doby \ No newline at end of file diff --git a/completions/zsh b/completions/zsh new file mode 100644 index 0000000..0599366 --- /dev/null +++ b/completions/zsh @@ -0,0 +1,19 @@ +#compdef doby + +function _doby { + _arguments \ + '(-f --force-encrypt)'{-f,--force-encrypt}'[Encrypt even if doby format is recognized]' \ + '(-i --interactive)'{-i,--interactive}'[Prompt before overwriting files]' \ + '(: * -)'{-h,--help}'[Prints help information]' \ + '(: * -)'{-V,--version}'[Prints version information]' \ + '--password=[Password used to derive encryption keys]' \ + '(-t --time-cost)'{-t,--time-cost}'[Argon2 time cost]' \ + '(-m --memory-cost)'{-m,--memory-cost}'[Argon2 memory cost (in kilobytes)]' \ + '(-p --parallelism)'{-p,--parallelism}'[Argon2 parallelism cost]' \ + '(-b --block-size)'{-b,--block-size}'[Size of the I/O buffer (in bytes)]' \ + '(-c --cipher)'{-c,--cipher}'[Encryption cipher to use]: :(aes xchacha20)' \ + ':::_files' \ + ':::_files' \ +} + +_doby "$@" diff --git a/src/bin/compgen.rs b/src/bin/compgen.rs new file mode 100644 index 0000000..a3bfb3a --- /dev/null +++ b/src/bin/compgen.rs @@ -0,0 +1,17 @@ +use std::{env, io}; +use clap::Shell; +use doby::cli; + +fn main() { + let mut args = env::args().skip(1); + if let Some(shell) = args.next() { + if let Ok(shell) = shell.parse() { + cli::app().gen_completions_to("doby", shell, &mut io::stdout()); + } else { + eprintln!("error: invalid shell: {}", shell); + eprintln!("shell variants: {:?}", Shell::variants()); + } + } else { + eprintln!("usage: compgen "); + } +} \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index ca2a1a8..09e6c9f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -31,8 +31,8 @@ impl From for ParseResult { } } -pub fn parse() -> Option { - let app = App::new(crate_name!()) +pub fn app<'a>() -> App<'a, 'a> { + App::new(crate_name!()) .version(crate_version!()) .setting(AppSettings::ColoredHelp) .about("Secure symmetric encryption from the command line.") @@ -42,7 +42,7 @@ pub fn parse() -> Option { Arg::with_name("1_force_encrypt") .short("f") .long("force-encrypt") - .help(&format!("Encrypt even if {} format is recognized", crate_name!())) + .help(concat!("Encrypt even if ", crate_name!(), " format is recognized")) ) .arg( Arg::with_name("2_interactive") @@ -97,7 +97,10 @@ pub fn parse() -> Option { .possible_values(&["aes", "xchacha20"]) .case_insensitive(true) ) - .get_matches(); +} + +pub fn parse() -> Option { + let app = app().get_matches(); let params = { let t_cost = number(app.value_of("2_t_cost").unwrap())?;