/
shell.rs
124 lines (113 loc) · 3.74 KB
/
shell.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::fmt::Display;
use std::str::FromStr;
use std::path::PathBuf;
use clap::builder::PossibleValue;
use clap::ValueEnum;
use crate::shells;
use crate::Generator;
/// Shell with auto-generated completion script available.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Shell {
/// Bourne Again SHell (bash)
Bash,
/// Elvish shell
Elvish,
/// Friendly Interactive SHell (fish)
Fish,
/// PowerShell
PowerShell,
/// Z SHell (zsh)
Zsh,
}
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))
}
}
// Hand-rolled so it can work even when `derive` feature is disabled
impl ValueEnum for Shell {
fn value_variants<'a>() -> &'a [Self] {
&[
Shell::Bash,
Shell::Elvish,
Shell::Fish,
Shell::PowerShell,
Shell::Zsh,
]
}
fn to_possible_value<'a>(&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"),
})
}
}
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),
}
}
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),
}
}
}
impl Shell {
/// Determine the user's current shell from the environment
///
/// This will read the SHELL environment variable and try to determine which shell is in use
/// from that.
///
/// If SHELL is not set, then on windows, it will default to powershell, and on
/// other OSes it will return an error.
///
/// If SHELL is set, but contains a value that doesn't correspond to one of the supported shell
/// types, then an error is returned.
///
/// # Example:
///
/// ```ignore
/// use clap_complete::{generate, shells::Shell}
/// let mut cmd = build_cli();
/// generate(Shell::from_env().unwrap_or(Shell::Bash), "myapp", &mut std::io::stdout());
/// ```
pub fn from_env() -> Result<Shell, String> {
let env_shell = std::env::var_os("SHELL").map(PathBuf::from);
if let Some(shell) = env_shell.as_ref().and_then(|s| s.file_name()).and_then(|s| s.to_str()) {
Ok(shell.parse::<Shell>()?)
} else if cfg!(windows) {
// Assume powershell for windows
Ok(Shell::PowerShell)
} else {
Err("SHELL environment variable not set".to_string())
}
}
}