1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
74
75#![warn(clippy::cast_lossless)]
76#![warn(clippy::cast_possible_wrap)]
77#![warn(clippy::default_trait_access)]
78#![warn(clippy::else_if_without_else)]
79#![warn(clippy::empty_enum)]
80#![warn(clippy::empty_line_after_outer_attr)]
81#![warn(clippy::enum_glob_use)]
82#![warn(clippy::equatable_if_let)]
83#![warn(clippy::float_cmp)]
84#![warn(clippy::fn_params_excessive_bools)]
85#![warn(clippy::get_unwrap)]
86#![warn(clippy::inefficient_to_string)]
87#![warn(clippy::integer_division)]
88#![warn(clippy::let_unit_value)]
89#![warn(clippy::linkedlist)]
90#![warn(clippy::lossy_float_literal)]
91#![warn(clippy::macro_use_imports)]
92#![warn(clippy::manual_assert)]
93#![warn(clippy::manual_ok_or)]
94#![warn(clippy::many_single_char_names)]
95#![warn(clippy::map_unwrap_or)]
96#![warn(clippy::match_bool)]
97#![warn(clippy::match_on_vec_items)]
98#![warn(clippy::match_same_arms)]
99#![warn(clippy::match_wild_err_arm)]
100#![warn(clippy::match_wildcard_for_single_variants)]
101#![warn(clippy::mem_forget)]
102#![warn(clippy::missing_const_for_fn)]
103#![warn(clippy::must_use_candidate)]
104#![warn(clippy::mut_mut)]
105#![warn(clippy::negative_feature_names)]
106#![warn(non_ascii_idents)]
107#![warn(clippy::option_option)]
108#![warn(clippy::redundant_feature_names)]
109#![warn(clippy::redundant_pub_crate)]
110#![warn(clippy::single_match_else)]
111#![warn(clippy::str_to_string)]
112#![warn(clippy::string_to_string)]
113#![warn(clippy::trait_duplication_in_bounds)]
114#![warn(clippy::unused_async)]
115#![warn(clippy::unused_self)]
116#![warn(clippy::use_self)]
117#![warn(clippy::wildcard_dependencies)]
118#![warn(clippy::wildcard_imports)]
119#![warn(clippy::zero_sized_map_values)]
120
121use std::{ffi::OsString, path::PathBuf};
122
123use clap::ValueEnum;
124
125#[derive(Clone, Copy, Debug)]
186#[non_exhaustive]
187pub enum Shell {
188 Bash,
190 #[cfg(feature = "carapace")]
192 Carapace,
193 Elvish,
195 #[cfg(feature = "fig")]
197 Fig,
198 Fish,
200 #[cfg(feature = "nushell")]
202 Nu,
203 PowerShell,
205 Zsh,
207}
208
209impl clap_complete::Generator for Shell {
210 fn file_name(&self, name: &str) -> String {
211 match self {
212 Self::Bash => clap_complete::Shell::Bash.file_name(name),
213 Self::Elvish => clap_complete::Shell::Elvish.file_name(name),
214 Self::Fish => clap_complete::Shell::Fish.file_name(name),
215 Self::PowerShell => clap_complete::Shell::PowerShell.file_name(name),
216 Self::Zsh => clap_complete::Shell::Zsh.file_name(name),
217
218 #[cfg(feature = "carapace")]
219 Self::Carapace => carapace_spec_clap::Spec.file_name(name),
220 #[cfg(feature = "fig")]
221 Self::Fig => clap_complete_fig::Fig.file_name(name),
222 #[cfg(feature = "nushell")]
223 Self::Nu => clap_complete_nushell::Nushell.file_name(name),
224 }
225 }
226
227 fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
228 match self {
229 Self::Bash => clap_complete::Shell::Bash.generate(cmd, buf),
230 Self::Elvish => clap_complete::Shell::Elvish.generate(cmd, buf),
231 Self::Fish => clap_complete::Shell::Fish.generate(cmd, buf),
232 Self::PowerShell => clap_complete::Shell::PowerShell.generate(cmd, buf),
233 Self::Zsh => clap_complete::Shell::Zsh.generate(cmd, buf),
234
235 #[cfg(feature = "carapace")]
236 Self::Carapace => carapace_spec_clap::Spec.generate(cmd, buf),
237 #[cfg(feature = "fig")]
238 Self::Fig => clap_complete_fig::Fig.generate(cmd, buf),
239 #[cfg(feature = "nushell")]
240 Self::Nu => clap_complete_nushell::Nushell.generate(cmd, buf),
241 }
242 }
243}
244
245impl Shell {
246 pub fn generate(self, command: &mut clap::Command, buffer: &mut dyn std::io::Write) {
251 let bin_name = command
252 .get_bin_name()
253 .unwrap_or_else(|| command.get_name())
254 .to_owned();
255 clap_complete::generate(self, command, bin_name, buffer)
256 }
257
258 pub fn generate_to<S>(
263 self,
264 command: &mut clap::Command,
265 out_dir: S,
266 ) -> Result<PathBuf, std::io::Error>
267 where
268 S: Into<OsString>,
269 {
270 let bin_name = command
271 .get_bin_name()
272 .unwrap_or_else(|| command.get_name())
273 .to_owned();
274 clap_complete::generate_to(self, command, bin_name, out_dir)
275 }
276}
277
278impl ValueEnum for Shell {
280 fn value_variants<'a>() -> &'a [Self] {
281 &[
282 Self::Bash,
283 #[cfg(feature = "carapace")]
284 Self::Carapace,
285 Self::Elvish,
286 #[cfg(feature = "fig")]
287 Self::Fig,
288 Self::Fish,
289 #[cfg(feature = "nushell")]
290 Self::Nu,
291 Self::PowerShell,
292 Self::Zsh,
293 ]
294 }
295
296 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
297 Some(match self {
298 Self::Bash => clap::builder::PossibleValue::new("bash"),
299 #[cfg(feature = "carapace")]
300 Self::Carapace => clap::builder::PossibleValue::new("carapace"),
301 Self::Elvish => clap::builder::PossibleValue::new("elvish"),
302 #[cfg(feature = "fig")]
303 Self::Fig => clap::builder::PossibleValue::new("fig"),
304 Self::Fish => clap::builder::PossibleValue::new("fish"),
305 #[cfg(feature = "nushell")]
306 Self::Nu => clap::builder::PossibleValue::new("nushell"),
307 Self::PowerShell => clap::builder::PossibleValue::new("powershell"),
308 Self::Zsh => clap::builder::PossibleValue::new("zsh"),
309 })
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316 use clap::ValueEnum;
317
318 macro_rules! check_shell_value_test {
319 ($test_name:ident, $shell:expr, $value:expr) => {
320 #[test]
321 fn $test_name() {
322 assert_eq!($shell.to_possible_value().unwrap().get_name(), $value);
323 }
324 };
325 }
326
327 check_shell_value_test!(test_shell_value_bash, Shell::Bash, "bash");
328 #[cfg(feature = "carapace")]
329 check_shell_value_test!(test_shell_value_carapace, Shell::Carapace, "carapace");
330 check_shell_value_test!(test_shell_value_elvish, Shell::Elvish, "elvish");
331 #[cfg(feature = "fig")]
332 check_shell_value_test!(test_shell_value_fig, Shell::Fig, "fig");
333 check_shell_value_test!(test_shell_value_fish, Shell::Fish, "fish");
334 #[cfg(feature = "nushell")]
335 check_shell_value_test!(test_shell_value_nushell, Shell::Nu, "nushell");
336 check_shell_value_test!(test_shell_value_powershell, Shell::PowerShell, "powershell");
337 check_shell_value_test!(test_shell_value_zsh, Shell::Zsh, "zsh");
338
339 #[test]
340 fn check_order() {
341 let names = Shell::value_variants()
342 .iter()
343 .map(|shell| shell.to_possible_value().unwrap().get_name().to_owned())
344 .collect::<Vec<_>>();
345
346 let mut sorted = names.clone();
347 sorted.sort_unstable();
348
349 assert_eq!(names, sorted);
350
351 let correct_order = [
352 ("bash", true),
353 ("carapace", cfg!(feature = "carapace")),
354 ("elvish", true),
355 ("fig", cfg!(feature = "fig")),
356 ("fish", true),
357 ("nushell", cfg!(feature = "nushell")),
358 ("powershell", true),
359 ("zsh", true),
360 ]
361 .iter()
362 .filter(|(_, enabled)| *enabled)
363 .map(|(shell, _)| *shell)
364 .collect::<Vec<_>>();
365
366 assert_eq!(names, correct_order);
367 }
368}