use clap::Parser;
use clap_complete_nushell::Nushell;
use std::fmt::Display;
use std::path::Path;
use std::str::FromStr;
use clap::ValueEnum;
use clap::builder::PossibleValue;
use clap_complete::Generator;
use clap_complete::shells;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Shell {
Bash,
Elvish,
Fish,
PowerShell,
Zsh,
Nushell,
}
impl Display for Shell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
impl FromStr for Shell {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
for variant in Self::value_variants() {
if variant.to_possible_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("invalid variant: {s}"))
}
}
impl ValueEnum for Shell {
fn value_variants<'a>() -> &'a [Self] {
&[
Shell::Bash,
Shell::Elvish,
Shell::Fish,
Shell::PowerShell,
Shell::Zsh,
Shell::Nushell,
]
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(match self {
Shell::Bash => PossibleValue::new("bash"),
Shell::Elvish => PossibleValue::new("elvish"),
Shell::Fish => PossibleValue::new("fish"),
Shell::PowerShell => PossibleValue::new("powershell"),
Shell::Zsh => PossibleValue::new("zsh"),
Shell::Nushell => PossibleValue::new("nushell"),
})
}
}
impl Generator for Shell {
fn file_name(&self, name: &str) -> String {
match self {
Shell::Bash => shells::Bash.file_name(name),
Shell::Elvish => shells::Elvish.file_name(name),
Shell::Fish => shells::Fish.file_name(name),
Shell::PowerShell => shells::PowerShell.file_name(name),
Shell::Zsh => shells::Zsh.file_name(name),
Shell::Nushell => Nushell.file_name(name),
}
}
fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
match self {
Shell::Bash => shells::Bash.generate(cmd, buf),
Shell::Elvish => shells::Elvish.generate(cmd, buf),
Shell::Fish => shells::Fish.generate(cmd, buf),
Shell::PowerShell => shells::PowerShell.generate(cmd, buf),
Shell::Zsh => shells::Zsh.generate(cmd, buf),
Shell::Nushell => Nushell.generate(cmd, buf),
}
}
}
impl Shell {
pub fn from_shell_path<P: AsRef<Path>>(path: P) -> Option<Shell> {
parse_shell_from_path(path.as_ref())
}
pub fn from_env() -> Option<Shell> {
if let Some(env_shell) = std::env::var_os("SHELL") {
Shell::from_shell_path(env_shell)
} else if cfg!(windows) {
Some(Shell::PowerShell)
} else {
None
}
}
}
fn parse_shell_from_path(path: &Path) -> Option<Shell> {
let name = path.file_stem()?.to_str()?;
match name {
"bash" => Some(Shell::Bash),
"zsh" => Some(Shell::Zsh),
"fish" => Some(Shell::Fish),
"elvish" => Some(Shell::Elvish),
"powershell" | "powershell_ise" => Some(Shell::PowerShell),
"nushell" => Some(Shell::Nushell),
_ => None,
}
}
#[derive(Parser, Debug)]
#[command(
about,
author,
long_about = None,
help_template("\
{about-with-newline}
{usage-heading} {usage}
{all-args}{after-help}
{author-with-newline}
"))]
pub struct Args {
#[arg(long, short, help = "Specify a list of authors to fetch quotes from")]
pub authors: Option<String>,
#[arg(long, short, help = "Set the theme color for the displayed quotes")]
pub theme_color: Option<String>,
#[arg(long, short, help = "Set the maximum number of tries to fetch a quote")]
pub max_tries: Option<usize>,
#[arg(long, short, help = "Specify the log file path")]
pub log_file: Option<String>,
#[arg(
long,
short,
help = "Enable rainbow mode for gradient rainbow quote colors",
action = clap::ArgAction::Set,
num_args = 0..=1,
default_missing_value = "true"
)]
pub rainbow_mode: Option<bool>,
#[arg(long, short, help = "Initialize the quote cache for offline mode")]
pub init_cache: bool,
#[arg(long, short, help = "Run in offline mode, using cached quotes")]
pub offline: bool,
#[arg(long, short, help = "Print version information")]
pub version: bool,
#[arg(long, short = 'C', help = "Use a custom TOML configuration file")]
pub config: Option<String>,
#[arg(long, short, help = "Generate shell completion script")]
pub completion: Option<Shell>,
}