diff --git a/Cargo.toml b/Cargo.toml index e152f60..9342f32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,9 @@ clap_complete = "4.0.6" clap_complete_nushell = "0.1.8" clap_mangen = "0.2.5" hostname = { version = "0.3", features = ["set"] } + +[profile.release] +codegen-units = 1 +strip = true +lto = true + diff --git a/src/cmd/bootstrap/mod.rs b/src/cmd/bootstrap/mod.rs index c1b435c..f0800b9 100644 --- a/src/cmd/bootstrap/mod.rs +++ b/src/cmd/bootstrap/mod.rs @@ -1,17 +1,18 @@ use crate::cmd; -use clap::{value_parser, Arg, ArgMatches, Command}; +use clap::{value_parser, Arg, ArgMatches, Command, ArgAction}; +use clap_complete::{generate_to, shells}; use clap_mangen::Man; -use std::{fs, io, path::PathBuf, process}; +use std::{fs, io, path::{Path, PathBuf}, process}; -const programs: [&str; 8] = [ - "shitbox", - "bootstrap", - "echo", - "false", - "head", - "hostname", - "true", - "sleep", +const COMMANDS: [fn() -> clap::Command; 8] = [ + cmd::bootstrap::cli, + cmd::echo::cli, + cmd::r#false::cli, + cmd::head::cli, + cmd::hostname::cli, + cmd::r#true::cli, + cmd::sleep::cli, + crate::cli::cli, ]; #[must_use] @@ -60,38 +61,37 @@ pub fn cli() -> Command { Arg::new("all") .help("Install completions for all supported shells") .short('a') - .long("all"), + .long("all") + .action(ArgAction::SetTrue), Arg::new("bash") .help("Bash shell completions") .short('b') - .long("bash"), + .long("bash") + .action(ArgAction::SetTrue), Arg::new("fish") .help("Fish shell completions") .short('f') - .long("fish"), + .long("fish") + .action(ArgAction::SetTrue), Arg::new("nu") .help("Nushell completions") .short('n') - .long("nu"), + .long("nu") + .action(ArgAction::SetTrue), Arg::new("pwsh") .help("PowerShell completions") .short('p') - .long("pwsh"), + .long("pwsh") + .action(ArgAction::SetTrue), ]), ]) } -fn manpage(prefix: &str, cmd: &str) -> Result<(), io::Error> { - let (fname, cmd) = match cmd { - "shitbox" => ("shitbox.1", crate::cli::cli()), - "bootstrap" => ("shitbox-bootstrap.1", cli()), - "echo" => ("echo.1", cmd::echo::cli()), - "false" => ("false.1", cmd::r#false::cli()), - "head" => ("head.1", cmd::head::cli()), - "hostname" => ("hostname.1", cmd::hostname::cli()), - "true" => ("true.1", cmd::r#true::cli()), - "sleep" => ("sleep.1", cmd::sleep::cli()), - _ => unimplemented!(), +fn manpage(prefix: &str, f: &dyn Fn() -> Command) -> Result<(), io::Error> { + let cmd = f(); + let fname = match cmd.get_name() { + "bootstrap" => "shitbox-bootstrap.1".to_string(), + s => format!("{s}.1"), }; let outdir: PathBuf = [prefix, "usr", "share", "man", "man1"].iter().collect(); if !outdir.exists() { @@ -109,7 +109,56 @@ fn manpage(prefix: &str, cmd: &str) -> Result<(), io::Error> { fn manpages(prefix: &str) -> Result<(), io::Error> { println!("Generating Unix man pages:"); - programs.iter().try_for_each(|cmd| manpage(prefix, cmd))?; + COMMANDS.iter().try_for_each(|cmd| { + manpage(prefix, &cmd) + })?; + Ok(()) +} + +fn gen_bash_completion(outdir: &Path, f: &dyn Fn() -> Command) -> Result<(), io::Error> { + let cmd = f(); + let name = cmd.get_name(); + let mut cmd = cmd.clone(); + let path = generate_to(shells::Bash, &mut cmd, name, outdir)?; + println!(" {}", path.display()); + Ok(()) +} + +fn gen_fish_completion(outdir: &Path, f: &dyn Fn() -> Command) -> Result<(), io::Error> { + let cmd = f(); + let name = cmd.get_name(); + let mut cmd = cmd.clone(); + let path = generate_to(shells::Fish, &mut cmd, name, outdir)?; + println!(" {}", path.display()); + Ok(()) +} + +fn completions(prefix: &str, matches: &ArgMatches) -> Result<(), io::Error> { + println!("Generating completions:"); + if matches.get_flag("bash") || matches.get_flag("all") { + let outdir: PathBuf = [prefix, "share", "bash-completion", "completion"] + .iter() + .collect(); + if !outdir.exists() { + fs::create_dir_all(&outdir)?; + } + COMMANDS.iter().try_for_each(|cmd| gen_bash_completion(&outdir, &cmd) )?; + } + if matches.get_flag("fish") || matches.get_flag("all") { + let outdir: PathBuf = ["target", "dist", "share", "fish", "completions"] + .iter() + .collect(); + if !outdir.exists() { + fs::create_dir_all(&outdir)?; + } + COMMANDS.iter().try_for_each(|cmd| gen_fish_completion(&outdir, &cmd) )?; + } + if matches.get_flag("nu") || matches.get_flag("all") { + } + if matches.get_flag("pwsh") || matches.get_flag("all") { + } + if matches.get_flag("zsh") || matches.get_flag("all") { + } Ok(()) } @@ -117,7 +166,19 @@ pub fn run(matches: &ArgMatches) { if let Some(prefix) = matches.get_one::("prefix") { match matches.subcommand() { Some(("manpages", _matches)) => { - if let Err(e) = manpages(&prefix) { + if let Err(e) = manpages(prefix) { + eprintln!("{e}"); + process::exit(1); + } + } + Some(("completions", matches)) => { + if let Err(e) = completions(prefix, matches) { + eprintln!("{e}"); + process::exit(1); + } + } + Some(("all", _matches)) => { + if let Err(e) = manpages(prefix) { eprintln!("{e}"); process::exit(1); } diff --git a/src/cmd/head/mod.rs b/src/cmd/head/mod.rs index b25f08c..3db8e50 100644 --- a/src/cmd/head/mod.rs +++ b/src/cmd/head/mod.rs @@ -42,6 +42,7 @@ pub fn cli() -> Command { .short('n') .long("lines") .default_value("10") + .allow_negative_numbers(false) .value_parser(value_parser!(usize)) ]) } diff --git a/src/cmd/sleep/mod.rs b/src/cmd/sleep/mod.rs index c22cf81..b3055c3 100644 --- a/src/cmd/sleep/mod.rs +++ b/src/cmd/sleep/mod.rs @@ -19,6 +19,7 @@ pub fn cli() -> Command { Arg::new("seconds") .help("The number of seconds to sleep") .num_args(1) + .allow_negative_numbers(false) .value_parser(value_parser!(f64)) .required(true) .action(ArgAction::Set)