grr_cli/commands/completions.rs
1//! `grr completions <shell>`: print a shell completion script.
2//!
3//! The script is generated from [`crate::cli::root_command`], the same
4//! composed tree `grr --help` and `grr schema` read, so every generated
5//! service command, flag and alias completes with no separate source to
6//! keep in sync.
7
8use std::io::Write;
9
10use anyhow::Result;
11use clap::Command;
12use clap_complete::Shell;
13
14/// The shells whose generators handle grr's depth. Fish is deliberately
15/// absent: clap_complete's fish generator stops emitting conditions below
16/// two levels of subcommand nesting, and every method sits three to five
17/// levels deep (`grr gmail users messages list`), so its script would offer
18/// the wrong candidates rather than none.
19#[derive(clap::ValueEnum, Clone, Copy, Debug)]
20pub enum CompletionShell {
21 Bash,
22 Elvish,
23 Powershell,
24 Zsh,
25}
26
27impl From<CompletionShell> for Shell {
28 fn from(shell: CompletionShell) -> Self {
29 match shell {
30 CompletionShell::Bash => Shell::Bash,
31 CompletionShell::Elvish => Shell::Elvish,
32 CompletionShell::Powershell => Shell::PowerShell,
33 CompletionShell::Zsh => Shell::Zsh,
34 }
35 }
36}
37
38#[derive(clap::Args, Debug, Clone)]
39pub struct CompletionsArgs {
40 /// Shell to generate the completion script for
41 #[arg(value_enum)]
42 pub shell: CompletionShell,
43}
44
45pub fn handle_completions_cmd(mut root: Command, args: CompletionsArgs) -> Result<()> {
46 let bin_name = root.get_name().to_owned();
47 // Render into memory first: clap_complete panics on a failed write, and
48 // with `panic = "immediate-abort"` a closed pipe (`grr completions zsh |
49 // head`) would abort instead of returning an error.
50 let mut script = Vec::new();
51 clap_complete::generate(Shell::from(args.shell), &mut root, bin_name, &mut script);
52 std::io::stdout().lock().write_all(&script)?;
53 Ok(())
54}