use anyhow::Result;
use clap::{CommandFactory, Parser};
use clap_complete::{generate, Shell};
use crate::utils::ErrorContext as EC;
use std::env;
use std::io;
use std::path::Path;
use std::process::ExitCode;
#[derive(Parser)]
pub struct CompletionsArg {
#[arg(value_enum)]
shell: Option<Shell>,
}
pub fn run_shell_completion<C: CommandFactory>(arg: CompletionsArg) -> Result<ExitCode> {
run_shell_completion_impl::<C, _>(arg, &mut io::stdout())
}
fn run_shell_completion_impl<C: CommandFactory, W: io::Write>(
arg: CompletionsArg,
output: &mut W,
) -> Result<ExitCode> {
let Some(shell) = arg.shell.or_else(Shell::from_env) else {
return Err(anyhow::anyhow!(EC::CannotInferShell));
};
let mut cmd = C::command();
let cmd_name = match get_bin_name() {
Some(cmd) => cmd,
None => cmd.get_name().to_string(),
};
generate(shell, &mut cmd, cmd_name, output);
Ok(ExitCode::SUCCESS)
}
fn get_bin_name() -> Option<String> {
let bin_path = env::args().next()?;
let p = Path::new(&bin_path);
let name = p.file_name()?;
Some(name.to_str()?.to_string())
}
#[cfg(test)]
mod test {
use super::*;
use crate::App;
#[test]
fn test_generate_command() {
let mut output = vec![];
let arg = CompletionsArg {
shell: Some(Shell::Bash),
};
run_shell_completion_impl::<App, _>(arg, &mut output).expect("should succeed");
let output = String::from_utf8(output).expect("should be valid");
assert!(output.contains("ast_grep"));
}
}