use crate::cli::args::Cli;
use clap::CommandFactory;
use clap_complete::{
generate,
shells::{Bash, Fish, Zsh},
};
use std::io::{self};
use tracing::{debug, instrument};
#[instrument(level = "debug")]
pub fn generate_completion(shell: &str) -> io::Result<()> {
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
match shell.to_lowercase().as_str() {
"bash" => {
debug!("Generating bash completion");
generate(Bash, &mut cmd, bin_name, &mut io::stdout());
Ok(())
}
"zsh" => {
debug!("Generating zsh completion");
generate(Zsh, &mut cmd, bin_name, &mut io::stdout());
Ok(())
}
"fish" => {
debug!("Generating fish completion");
generate(Fish, &mut cmd, bin_name, &mut io::stdout());
Ok(())
}
_ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"Unsupported shell: {}. Supported shells: bash, zsh, fish",
shell
),
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn given_bash_shell_when_generate_completion_then_creates_valid_completion_script(
) -> io::Result<()> {
let mut buffer = Vec::new();
{
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
generate(Bash, &mut cmd, bin_name, &mut buffer);
}
assert!(!buffer.is_empty());
let content = String::from_utf8_lossy(&buffer);
assert!(
content.contains("complete")
|| content.contains("compgen")
|| content.contains("COMPREPLY")
);
Ok(())
}
#[test]
fn given_zsh_shell_when_generate_completion_then_creates_valid_completion_script(
) -> io::Result<()> {
let mut buffer = Vec::new();
{
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
generate(Zsh, &mut cmd, bin_name, &mut buffer);
}
assert!(!buffer.is_empty());
let content = String::from_utf8_lossy(&buffer);
assert!(
content.contains("#compdef")
|| content.contains("_arguments")
|| content.contains("compdef")
);
Ok(())
}
#[test]
fn given_fish_shell_when_generate_completion_then_creates_valid_completion_script(
) -> io::Result<()> {
let mut buffer = Vec::new();
{
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
generate(Fish, &mut cmd, bin_name, &mut buffer);
}
assert!(!buffer.is_empty());
let content = String::from_utf8_lossy(&buffer);
assert!(content.contains("complete") && content.contains("-c bkmr"));
Ok(())
}
#[test]
fn given_invalid_shell_when_generate_completion_then_returns_error() {
let result = generate_completion("invalid");
assert!(result.is_err());
if let Err(e) = result {
assert_eq!(e.kind(), io::ErrorKind::InvalidInput);
assert!(e.to_string().contains("Unsupported shell"));
}
}
}