use std::{
io::{self, Write},
path::PathBuf,
};
use byte_unit::Byte;
use clap::{CommandFactory, Parser, ValueEnum, ValueHint};
use clap_complete::Generator;
use jiff::Span;
const LONG_VERSION: &str = concat!(
env!("CARGO_PKG_VERSION"),
'\n',
"Copyright (C) 2025 Shun Sakai\n",
'\n',
"This program is distributed under the terms of the GNU General Public License\n",
"v3.0 or later.\n",
'\n',
"This is free software: you are free to change and redistribute it. There is NO\n",
"WARRANTY, to the extent permitted by law.\n",
'\n',
"Report bugs to <https://github.com/sorairolake/ngrv/issues>."
);
const AFTER_LONG_HELP: &str = "See `ngrv(1)` for more details.";
#[derive(Debug, Parser)]
#[allow(clippy::struct_excessive_bools)]
#[command(
version,
long_version(LONG_VERSION),
about,
max_term_width(100),
after_long_help(AFTER_LONG_HELP)
)]
pub struct Opt {
#[arg(long)]
pub no_progress: bool,
#[arg(long)]
pub no_timer: bool,
#[arg(long)]
pub no_eta: bool,
#[arg(long)]
pub no_rate: bool,
#[arg(long)]
pub no_bytes: bool,
#[arg(short('k'), long)]
pub si: bool,
#[arg(short, long, value_name("SIZE"))]
pub size: Option<Byte>,
#[arg(short, long, value_name("SEC"))]
pub interval: Option<Span>,
#[arg(short('N'), long, value_name("NAME"))]
pub name: Option<String>,
#[arg(short('B'), long, value_name("BYTES"))]
pub buffer_size: Option<Byte>,
#[arg(long, value_enum, value_name("SHELL"))]
pub generate_completion: Option<Shell>,
#[arg(value_name("FILE"), value_hint(ValueHint::FilePath))]
pub inputs: Option<Vec<PathBuf>>,
}
impl Opt {
pub fn print_completion(generator: impl Generator) {
clap_complete::generate(
generator,
&mut Self::command(),
Self::command().get_name(),
&mut io::stdout(),
);
}
}
#[derive(Clone, Debug, ValueEnum)]
#[allow(clippy::doc_markdown)]
#[value(rename_all = "lower")]
pub enum Shell {
Bash,
Elvish,
Fish,
Nushell,
#[allow(clippy::enum_variant_names)]
PowerShell,
Zsh,
}
impl Generator for Shell {
fn file_name(&self, name: &str) -> String {
match self {
Self::Bash => clap_complete::Shell::Bash.file_name(name),
Self::Elvish => clap_complete::Shell::Elvish.file_name(name),
Self::Fish => clap_complete::Shell::Fish.file_name(name),
Self::Nushell => clap_complete_nushell::Nushell.file_name(name),
Self::PowerShell => clap_complete::Shell::PowerShell.file_name(name),
Self::Zsh => clap_complete::Shell::Zsh.file_name(name),
}
}
fn generate(&self, cmd: &clap::Command, buf: &mut dyn Write) {
match self {
Self::Bash => clap_complete::Shell::Bash.generate(cmd, buf),
Self::Elvish => clap_complete::Shell::Elvish.generate(cmd, buf),
Self::Fish => clap_complete::Shell::Fish.generate(cmd, buf),
Self::Nushell => clap_complete_nushell::Nushell.generate(cmd, buf),
Self::PowerShell => clap_complete::Shell::PowerShell.generate(cmd, buf),
Self::Zsh => clap_complete::Shell::Zsh.generate(cmd, buf),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verify_app() {
Opt::command().debug_assert();
}
#[test]
fn file_name_shell() {
assert_eq!(Shell::Bash.file_name("ngrv"), "ngrv.bash");
assert_eq!(Shell::Elvish.file_name("ngrv"), "ngrv.elv");
assert_eq!(Shell::Fish.file_name("ngrv"), "ngrv.fish");
assert_eq!(Shell::Nushell.file_name("ngrv"), "ngrv.nu");
assert_eq!(Shell::PowerShell.file_name("ngrv"), "_ngrv.ps1");
assert_eq!(Shell::Zsh.file_name("ngrv"), "_ngrv");
}
}