119625d8cSopenharmony_ciuse std::fmt::Display;
219625d8cSopenharmony_ciuse std::path::Path;
319625d8cSopenharmony_ciuse std::str::FromStr;
419625d8cSopenharmony_ci
519625d8cSopenharmony_ciuse clap::builder::PossibleValue;
619625d8cSopenharmony_ciuse clap::ValueEnum;
719625d8cSopenharmony_ci
819625d8cSopenharmony_ciuse crate::shells;
919625d8cSopenharmony_ciuse crate::Generator;
1019625d8cSopenharmony_ci
1119625d8cSopenharmony_ci/// Shell with auto-generated completion script available.
1219625d8cSopenharmony_ci#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1319625d8cSopenharmony_ci#[non_exhaustive]
1419625d8cSopenharmony_cipub enum Shell {
1519625d8cSopenharmony_ci    /// Bourne Again SHell (bash)
1619625d8cSopenharmony_ci    Bash,
1719625d8cSopenharmony_ci    /// Elvish shell
1819625d8cSopenharmony_ci    Elvish,
1919625d8cSopenharmony_ci    /// Friendly Interactive SHell (fish)
2019625d8cSopenharmony_ci    Fish,
2119625d8cSopenharmony_ci    /// PowerShell
2219625d8cSopenharmony_ci    PowerShell,
2319625d8cSopenharmony_ci    /// Z SHell (zsh)
2419625d8cSopenharmony_ci    Zsh,
2519625d8cSopenharmony_ci}
2619625d8cSopenharmony_ci
2719625d8cSopenharmony_ciimpl Display for Shell {
2819625d8cSopenharmony_ci    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2919625d8cSopenharmony_ci        self.to_possible_value()
3019625d8cSopenharmony_ci            .expect("no values are skipped")
3119625d8cSopenharmony_ci            .get_name()
3219625d8cSopenharmony_ci            .fmt(f)
3319625d8cSopenharmony_ci    }
3419625d8cSopenharmony_ci}
3519625d8cSopenharmony_ci
3619625d8cSopenharmony_ciimpl FromStr for Shell {
3719625d8cSopenharmony_ci    type Err = String;
3819625d8cSopenharmony_ci
3919625d8cSopenharmony_ci    fn from_str(s: &str) -> Result<Self, Self::Err> {
4019625d8cSopenharmony_ci        for variant in Self::value_variants() {
4119625d8cSopenharmony_ci            if variant.to_possible_value().unwrap().matches(s, false) {
4219625d8cSopenharmony_ci                return Ok(*variant);
4319625d8cSopenharmony_ci            }
4419625d8cSopenharmony_ci        }
4519625d8cSopenharmony_ci        Err(format!("invalid variant: {s}"))
4619625d8cSopenharmony_ci    }
4719625d8cSopenharmony_ci}
4819625d8cSopenharmony_ci
4919625d8cSopenharmony_ci// Hand-rolled so it can work even when `derive` feature is disabled
5019625d8cSopenharmony_ciimpl ValueEnum for Shell {
5119625d8cSopenharmony_ci    fn value_variants<'a>() -> &'a [Self] {
5219625d8cSopenharmony_ci        &[
5319625d8cSopenharmony_ci            Shell::Bash,
5419625d8cSopenharmony_ci            Shell::Elvish,
5519625d8cSopenharmony_ci            Shell::Fish,
5619625d8cSopenharmony_ci            Shell::PowerShell,
5719625d8cSopenharmony_ci            Shell::Zsh,
5819625d8cSopenharmony_ci        ]
5919625d8cSopenharmony_ci    }
6019625d8cSopenharmony_ci
6119625d8cSopenharmony_ci    fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
6219625d8cSopenharmony_ci        Some(match self {
6319625d8cSopenharmony_ci            Shell::Bash => PossibleValue::new("bash"),
6419625d8cSopenharmony_ci            Shell::Elvish => PossibleValue::new("elvish"),
6519625d8cSopenharmony_ci            Shell::Fish => PossibleValue::new("fish"),
6619625d8cSopenharmony_ci            Shell::PowerShell => PossibleValue::new("powershell"),
6719625d8cSopenharmony_ci            Shell::Zsh => PossibleValue::new("zsh"),
6819625d8cSopenharmony_ci        })
6919625d8cSopenharmony_ci    }
7019625d8cSopenharmony_ci}
7119625d8cSopenharmony_ci
7219625d8cSopenharmony_ciimpl Generator for Shell {
7319625d8cSopenharmony_ci    fn file_name(&self, name: &str) -> String {
7419625d8cSopenharmony_ci        match self {
7519625d8cSopenharmony_ci            Shell::Bash => shells::Bash.file_name(name),
7619625d8cSopenharmony_ci            Shell::Elvish => shells::Elvish.file_name(name),
7719625d8cSopenharmony_ci            Shell::Fish => shells::Fish.file_name(name),
7819625d8cSopenharmony_ci            Shell::PowerShell => shells::PowerShell.file_name(name),
7919625d8cSopenharmony_ci            Shell::Zsh => shells::Zsh.file_name(name),
8019625d8cSopenharmony_ci        }
8119625d8cSopenharmony_ci    }
8219625d8cSopenharmony_ci
8319625d8cSopenharmony_ci    fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
8419625d8cSopenharmony_ci        match self {
8519625d8cSopenharmony_ci            Shell::Bash => shells::Bash.generate(cmd, buf),
8619625d8cSopenharmony_ci            Shell::Elvish => shells::Elvish.generate(cmd, buf),
8719625d8cSopenharmony_ci            Shell::Fish => shells::Fish.generate(cmd, buf),
8819625d8cSopenharmony_ci            Shell::PowerShell => shells::PowerShell.generate(cmd, buf),
8919625d8cSopenharmony_ci            Shell::Zsh => shells::Zsh.generate(cmd, buf),
9019625d8cSopenharmony_ci        }
9119625d8cSopenharmony_ci    }
9219625d8cSopenharmony_ci}
9319625d8cSopenharmony_ci
9419625d8cSopenharmony_ciimpl Shell {
9519625d8cSopenharmony_ci    /// Parse a shell from a path to the executable for the shell
9619625d8cSopenharmony_ci    ///
9719625d8cSopenharmony_ci    /// # Examples
9819625d8cSopenharmony_ci    ///
9919625d8cSopenharmony_ci    /// ```
10019625d8cSopenharmony_ci    /// use clap_complete::shells::Shell;
10119625d8cSopenharmony_ci    ///
10219625d8cSopenharmony_ci    /// assert_eq!(Shell::from_shell_path("/bin/bash"), Some(Shell::Bash));
10319625d8cSopenharmony_ci    /// assert_eq!(Shell::from_shell_path("/usr/bin/zsh"), Some(Shell::Zsh));
10419625d8cSopenharmony_ci    /// assert_eq!(Shell::from_shell_path("/opt/my_custom_shell"), None);
10519625d8cSopenharmony_ci    /// ```
10619625d8cSopenharmony_ci    pub fn from_shell_path<P: AsRef<Path>>(path: P) -> Option<Shell> {
10719625d8cSopenharmony_ci        parse_shell_from_path(path.as_ref())
10819625d8cSopenharmony_ci    }
10919625d8cSopenharmony_ci
11019625d8cSopenharmony_ci    /// Determine the user's current shell from the environment
11119625d8cSopenharmony_ci    ///
11219625d8cSopenharmony_ci    /// This will read the SHELL environment variable and try to determine which shell is in use
11319625d8cSopenharmony_ci    /// from that.
11419625d8cSopenharmony_ci    ///
11519625d8cSopenharmony_ci    /// If SHELL is not set, then on windows, it will default to powershell, and on
11619625d8cSopenharmony_ci    /// other OSes it will return `None`.
11719625d8cSopenharmony_ci    ///
11819625d8cSopenharmony_ci    /// If SHELL is set, but contains a value that doesn't correspond to one of the supported shell
11919625d8cSopenharmony_ci    /// types, then return `None`.
12019625d8cSopenharmony_ci    ///
12119625d8cSopenharmony_ci    /// # Example:
12219625d8cSopenharmony_ci    ///
12319625d8cSopenharmony_ci    /// ```no_run
12419625d8cSopenharmony_ci    /// # use clap::Command;
12519625d8cSopenharmony_ci    /// use clap_complete::{generate, shells::Shell};
12619625d8cSopenharmony_ci    /// # fn build_cli() -> Command {
12719625d8cSopenharmony_ci    /// #     Command::new("compl")
12819625d8cSopenharmony_ci    /// # }
12919625d8cSopenharmony_ci    /// let mut cmd = build_cli();
13019625d8cSopenharmony_ci    /// generate(Shell::from_env().unwrap_or(Shell::Bash), &mut cmd, "myapp", &mut std::io::stdout());
13119625d8cSopenharmony_ci    /// ```
13219625d8cSopenharmony_ci    pub fn from_env() -> Option<Shell> {
13319625d8cSopenharmony_ci        if let Some(env_shell) = std::env::var_os("SHELL") {
13419625d8cSopenharmony_ci            Shell::from_shell_path(env_shell)
13519625d8cSopenharmony_ci        } else if cfg!(windows) {
13619625d8cSopenharmony_ci            Some(Shell::PowerShell)
13719625d8cSopenharmony_ci        } else {
13819625d8cSopenharmony_ci            None
13919625d8cSopenharmony_ci        }
14019625d8cSopenharmony_ci    }
14119625d8cSopenharmony_ci}
14219625d8cSopenharmony_ci
14319625d8cSopenharmony_ci// use a separate function to avoid having to monomorphize the entire function due
14419625d8cSopenharmony_ci// to from_shell_path being generic
14519625d8cSopenharmony_cifn parse_shell_from_path(path: &Path) -> Option<Shell> {
14619625d8cSopenharmony_ci    let name = path.file_stem()?.to_str()?;
14719625d8cSopenharmony_ci    match name {
14819625d8cSopenharmony_ci        "bash" => Some(Shell::Bash),
14919625d8cSopenharmony_ci        "zsh" => Some(Shell::Zsh),
15019625d8cSopenharmony_ci        "fish" => Some(Shell::Fish),
15119625d8cSopenharmony_ci        "elvish" => Some(Shell::Elvish),
15219625d8cSopenharmony_ci        "powershell" | "powershell_ise" => Some(Shell::PowerShell),
15319625d8cSopenharmony_ci        _ => None,
15419625d8cSopenharmony_ci    }
15519625d8cSopenharmony_ci}
156