use std::io::{self, Write};
use clap::Args;
use clap_complete::{Generator, Shell};
#[derive(Debug, Args)]
pub struct CompletionArgs {
#[arg(value_enum)]
pub shell: Shell,
}
pub fn run(args: CompletionArgs, mut cmd: clap::Command) -> anyhow::Result<()> {
generate(args.shell, &mut cmd, &mut io::stdout())?;
Ok(())
}
fn generate(shell: Shell, cmd: &mut clap::Command, writer: &mut dyn Write) -> io::Result<()> {
let bin_name = cmd.get_name().to_string();
cmd.set_bin_name(bin_name);
cmd.build();
shell.try_generate(cmd, writer)
}
#[cfg(test)]
mod tests {
use std::io::{self, Write};
use clap::{CommandFactory, Parser};
use super::*;
struct FailingWriter;
impl Write for FailingWriter {
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::BrokenPipe, "closed pipe"))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Parser)]
struct TestCli {
#[command(flatten)]
args: CompletionArgs,
}
fn parse_completion_args(args: &[&str]) -> CompletionArgs {
TestCli::parse_from(std::iter::once("msb").chain(args.iter().copied())).args
}
#[test]
fn parses_each_supported_shell() {
for (name, shell) in [
("bash", Shell::Bash),
("elvish", Shell::Elvish),
("fish", Shell::Fish),
("powershell", Shell::PowerShell),
("zsh", Shell::Zsh),
] {
let args = parse_completion_args(&[name]);
assert_eq!(args.shell, shell);
}
}
#[test]
fn rejects_unknown_shell() {
let result = TestCli::try_parse_from(["msb", "tcsh"]);
assert!(result.is_err());
}
#[test]
fn propagates_output_errors() {
let mut cmd = TestCli::command();
let error = generate(Shell::Bash, &mut cmd, &mut FailingWriter).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
}
}