diff --git a/Cargo.lock b/Cargo.lock index 69e6dc3..2c891da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,14 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi 0.3.8", -] - [[package]] name = "atty" version = "0.2.14" @@ -20,6 +11,12 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "backtrace" version = "0.3.44" @@ -62,19 +59,45 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "clap" -version = "2.33.0" +version = "3.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", "strsim", + "termcolor", "textwrap", "unicode-width", "vec_map", ] +[[package]] +name = "clap_derive" +version = "3.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_generate" +version = "3.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf420f8b687b628d2915ccfd43a660c437a170432e3fbcb66944e8717a0d68f" +dependencies = [ + "clap", +] + [[package]] name = "error-chain" version = "0.12.2" @@ -85,6 +108,21 @@ dependencies = [ "version_check", ] +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.8" @@ -94,6 +132,16 @@ dependencies = [ "libc", ] +[[package]] +name = "indexmap" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -104,12 +152,66 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +[[package]] +name = "os_str_bytes" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + [[package]] name = "redox_syscall" version = "0.1.56" @@ -121,6 +223,7 @@ name = "rm-improved" version = "0.13.1" dependencies = [ "clap", + "clap_generate", "error-chain", "time", "walkdir", @@ -144,15 +247,35 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] [[package]] name = "textwrap" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" dependencies = [ "unicode-width", ] @@ -168,12 +291,24 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + [[package]] name = "unicode-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + [[package]] name = "vec_map" version = "0.8.1" @@ -225,6 +360,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 67c8a1b..29721ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ categories = ["command-line-utilities"] autobins = false [dependencies] -clap = "2" +clap = "3.0.0-beta.2" +clap_generate = "3.0.0-beta.2" walkdir = "1" time = "0.1" error-chain = "0.12" @@ -25,4 +26,4 @@ opt-level = "s" [[bin]] name = "rip" -path = "src/main.rs" \ No newline at end of file +path = "src/main.rs" diff --git a/src/main.rs b/src/main.rs index 82a9ab0..3e5483c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ // -*- compile-command: "cargo build" -*- -#[macro_use] extern crate clap; extern crate core; #[macro_use] @@ -7,7 +6,9 @@ extern crate error_chain; extern crate time; extern crate walkdir; -use clap::{App, Arg}; +use clap::{crate_authors, crate_version, App, AppSettings, Arg}; +use clap_generate::generators::*; +use clap_generate::{generate, Generator}; use std::io::{BufRead, BufReader, Read, Write}; use std::os::unix::fs::{FileTypeExt, PermissionsExt}; use std::path::{Path, PathBuf}; @@ -52,55 +53,7 @@ fn main() { } fn run() -> Result<()> { - let matches = App::new("rip") - .version(crate_version!()) - .author(crate_authors!()) - .about( - "Rm ImProved -Send files to the graveyard (/tmp/graveyard-$USER by default) instead of unlinking them.", - ) - .arg( - Arg::with_name("TARGET") - .help("File or directory to remove") - .multiple(true) - .index(1), - ) - .arg( - Arg::with_name("graveyard") - .help("Directory where deleted files go to rest") - .long("graveyard") - .takes_value(true), - ) - .arg( - Arg::with_name("decompose") - .help("Permanently deletes (unlink) the entire graveyard") - .short("d") - .long("decompose"), - ) - .arg( - Arg::with_name("seance") - .help("Prints files that were sent under the current directory") - .short("s") - .long("seance"), - ) - .arg( - Arg::with_name("unbury") - .help( - "Undo the last removal by the current user, or specify some file(s) in the \ - graveyard. Combine with -s to restore everything printed by -s.", - ) - .short("u") - .long("unbury") - .value_name("target") - .min_values(0), - ) - .arg( - Arg::with_name("inspect") - .help("Prints some info about TARGET before prompting for action") - .short("i") - .long("inspect"), - ) - .get_matches(); + let matches = cli_rip().get_matches(); let graveyard: &PathBuf = &{ if let Some(flag) = matches.value_of("graveyard") { @@ -115,7 +68,9 @@ Send files to the graveyard (/tmp/graveyard-$USER by default) instead of unlinki env } else { format!("{}-{}", GRAVEYARD, get_user()) - }}.into(); + } + } + .into(); if matches.is_present("decompose") { if prompt_yes("Really unlink the entire graveyard?") { @@ -288,13 +243,111 @@ Send files to the graveyard (/tmp/graveyard-$USER by default) instead of unlinki bail!("Cannot remove {}: no such file or directory", target); } } - } else { - println!("{}\nrip -h for help", matches.usage()); + } + + if let Some(matches) = matches.subcommand_matches("completion") { + let shell = matches.value_of("shell").unwrap(); + + let mut app = cli_rip(); + match shell { + "bash" => print_completions::(&mut app), + "elvish" => print_completions::(&mut app), + "fish" => print_completions::(&mut app), + "powershell" => print_completions::(&mut app), + "zsh" => print_completions::(&mut app), + _ => panic!("Unknown generator"), + } + + //if matches.is_present("manual") { + // TODO: manual + //} } Ok(()) } +// cli interface +fn cli_rip() -> App<'static> { + App::new("rip") + .version(crate_version!()) + .author(crate_authors!()) + .setting(AppSettings::ArgRequiredElseHelp) + .about( + "Rm ImProved +Send files to the graveyard (/tmp/graveyard-$USER by default) instead of unlinking them.", + ) + .arg( + Arg::new("TARGET") + .about("File or directory to remove") + .short('t') + .long("target") + .takes_value(true) + .multiple(true), //.index(1) + ) + .arg( + Arg::new("graveyard") + .about("Directory where deleted files go to rest") + .long("graveyard") + .takes_value(true), + ) + .arg( + Arg::new("decompose") + .about("Permanently deletes (unlink) the entire graveyard") + .short('d') + .long("decompose"), + ) + .arg( + Arg::new("seance") + .about("Prints files that were sent under the current directory") + .short('s') + .long("seance"), + ) + .arg( + Arg::new("unbury") + .about( + "Undo the last removal by the current user, or specify some file(s) in the \ + graveyard. Combine with -s to restore everything printed by -s.", + ) + .short('u') + .long("unbury") + .value_name("target") + .min_values(0), + ) + .arg( + Arg::new("inspect") + .about("Prints some info about TARGET before prompting for action") + .short('i') + .long("inspect"), + ) + .subcommand( + App::new("completion") + .version(crate_version!()) + .author(crate_authors!()) + .setting(AppSettings::Hidden) + .about("AutoCompletion") + .arg( + Arg::new("shell") + .short('s') + .long("shell") + .about("Selects shell") + .required(true) + .takes_value(true) + .possible_values(&["bash", "elvish", "fish", "powershell", "zsh"]), + ) + .arg( + Arg::new("manual") + .short('m') + .long("manual") + .about("Display instructions on how to install autocompletions"), + ), + ) +} + +/// Print completions +pub fn print_completions(app: &mut App) { + generate::(app, app.get_name().to_string(), &mut io::stdout()); +} + /// Write deletion history to record fn write_log(source: S, dest: D, record: R) -> io::Result<()> where diff --git a/src/util.rs b/src/util.rs index c54442a..4a26c9c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,13 +1,11 @@ /// Concatenate two paths, even if the right argument is an absolute path. fn join_absolute, B: AsRef>(left: A, right: B) -> PathBuf { let (left, right) = (left.as_ref(), right.as_ref()); - left.join( - if let Ok(stripped) = right.strip_prefix("/") { - stripped - } else { - right - } - ) + left.join(if let Ok(stripped) = right.strip_prefix("/") { + stripped + } else { + right + }) } fn symlink_exists>(path: P) -> bool { @@ -26,7 +24,9 @@ fn prompt_yes>(prompt: T) -> bool { println!("{} (y/N)", prompt.as_ref()); } let stdin = BufReader::new(io::stdin()); - stdin.bytes().next() + stdin + .bytes() + .next() .and_then(|c| c.ok()) .map(|c| c as char) .map(|c| (c == 'y' || c == 'Y')) @@ -45,12 +45,17 @@ fn rename_grave>(grave: G) -> PathBuf { fn humanize_bytes(bytes: u64) -> String { let values = ["bytes", "KB", "MB", "GB", "TB"]; - let pair = values.iter() + let pair = values + .iter() .enumerate() .take_while(|x| bytes as usize / (1000 as usize).pow(x.0 as u32) > 10) .last(); if let Some((i, unit)) = pair { - format!("{} {}", bytes as usize / (1000 as usize).pow(i as u32), unit) + format!( + "{} {}", + bytes as usize / (1000 as usize).pow(i as u32), + unit + ) } else { format!("{} {}", bytes, values[0]) }