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