1use std::{env, io};
2
3use anyhow::Result;
4use clap::builder::Styles;
5use clap::builder::styling::{AnsiColor, Style};
6use clap::{CommandFactory, Parser, ValueEnum};
7use clap_complete::Generator;
8use clap_verbosity_flag::Verbosity;
9use supports_color::Stream;
10
11#[derive(Parser)]
12#[command(about, version = version_msg(), styles = get_styles())]
13pub struct Args {
14 #[arg(long, value_enum)]
16 pub completion: Option<Shell>,
17
18 #[command(flatten)]
19 pub verbose: Verbosity,
20}
21
22#[must_use]
23fn version_msg() -> String {
24 let version = clap::crate_version!();
25 let author = clap::crate_authors!();
26 let home_page = env!("CARGO_PKG_HOMEPAGE");
27
28 format!(
29 "\
30{version}
31
32Author: {author}
33Author's Telegram: https://t.me/TerakomariGandesblood
34Project home page: {home_page}"
35 )
36}
37
38const HEADER: Style = AnsiColor::Green.on_default().bold();
39const USAGE: Style = AnsiColor::Green.on_default().bold();
40const LITERAL: Style = AnsiColor::Cyan.on_default().bold();
41const PLACEHOLDER: Style = AnsiColor::Cyan.on_default();
42const ERROR: Style = AnsiColor::Red.on_default().bold();
43const VALID: Style = AnsiColor::Cyan.on_default().bold();
44const INVALID: Style = AnsiColor::Yellow.on_default().bold();
45const HELP_STYLES: Styles = Styles::styled()
46 .header(HEADER)
47 .usage(USAGE)
48 .literal(LITERAL)
49 .placeholder(PLACEHOLDER)
50 .error(ERROR)
51 .valid(VALID)
52 .invalid(INVALID);
53
54fn get_styles() -> Styles {
55 if supports_color::on(Stream::Stdout).is_some() {
56 HELP_STYLES
57 } else {
58 Styles::plain()
59 }
60}
61
62#[must_use]
63#[derive(Clone, ValueEnum)]
64pub enum Shell {
65 Bash,
66 Elvish,
67 Fish,
68 PowerShell,
69 Zsh,
70 Nushell,
71}
72
73impl Shell {
74 fn to_clap_type(&self) -> Box<dyn Generator> {
75 match self {
76 Self::Bash => Box::new(clap_complete::Shell::Bash),
77 Self::Elvish => Box::new(clap_complete::Shell::Elvish),
78 Self::Fish => Box::new(clap_complete::Shell::Fish),
79 Self::PowerShell => Box::new(clap_complete::Shell::PowerShell),
80 Self::Zsh => Box::new(clap_complete::Shell::Zsh),
81 Self::Nushell => Box::new(clap_complete_nushell::Nushell),
82 }
83 }
84}
85
86impl Generator for Shell {
87 fn file_name(&self, name: &str) -> String {
88 self.to_clap_type().file_name(name)
89 }
90
91 fn generate(&self, cmd: &clap::Command, buf: &mut dyn io::Write) {
92 self.to_clap_type().generate(cmd, buf);
93 }
94}
95
96pub fn generate_completion(shell: Shell) -> Result<()> {
97 let mut cmd = Args::command();
98 let bin_name = cmd.get_name().to_string();
99
100 clap_complete::generate(shell, &mut cmd, bin_name, &mut io::stdout());
101
102 Ok(())
103}
104
105#[cfg(test)]
106mod tests {
107 use clap::CommandFactory;
108
109 use super::*;
110
111 #[test]
112 fn verify_cli() {
113 Args::command().debug_assert();
114 }
115}