use clap::CommandFactory;
use clap_complete::{Shell, generate};
use crate::cli::CliArgs;
use crate::error::{ConfigError, MongoshError, Result};
pub fn generate_completion(shell_name: &str) -> Result<()> {
let shell = parse_shell(shell_name)?;
match shell {
Shell::Bash => generate_bash_completion(),
Shell::Zsh => generate_zsh_completion(),
Shell::Fish => generate_fish_completion(),
_ => Err(MongoshError::Config(ConfigError::Generic(format!(
"Unsupported shell. Supported shells: bash, zsh, fish"
)))),
}
}
fn parse_shell(shell_name: &str) -> Result<Shell> {
match shell_name.to_lowercase().as_str() {
"bash" => Ok(Shell::Bash),
"zsh" => Ok(Shell::Zsh),
"fish" => Ok(Shell::Fish),
_ => Err(MongoshError::Config(ConfigError::Generic(format!(
"Unsupported shell: {}. Supported shells: bash, zsh, fish",
shell_name
)))),
}
}
fn generate_bash_completion() -> Result<()> {
let mut cmd = CliArgs::command();
let mut buffer = Vec::new();
generate(Shell::Bash, &mut cmd, "mongosh", &mut buffer);
let basic_completion = String::from_utf8_lossy(&buffer);
let custom_completion = format!(
r#"{}
# Custom completion for datasource names
_mongosh_list_datasources() {{
# Use mongosh itself to list datasources
mongosh config --list-datasources 2>/dev/null
}}
# Override completion for -d/--datasource flag
_mongosh_complete_datasource() {{
local cur="$1"
local datasources=$(_mongosh_list_datasources)
if [ -n "$datasources" ]; then
COMPREPLY=($(compgen -W "$datasources" -- "$cur"))
fi
}}
# Enhance the completion function
_mongosh_enhanced() {{
local cur prev words cword
_init_completion || return
# Check if previous word is -d or --datasource
if [[ "$prev" == "-d" || "$prev" == "--datasource" ]]; then
_mongosh_complete_datasource "$cur"
return 0
fi
# Fall back to default completion
_mongosh "$@"
}}
# Replace the completion function
complete -F _mongosh_enhanced mongosh
"#,
basic_completion
);
print!("{}", custom_completion);
Ok(())
}
fn generate_zsh_completion() -> Result<()> {
let mut cmd = CliArgs::command();
let mut buffer = Vec::new();
generate(Shell::Zsh, &mut cmd, "mongosh", &mut buffer);
let basic_completion = String::from_utf8_lossy(&buffer);
let custom_completion = format!(
r#"{}
# Custom completion for datasource names
_mongosh_list_datasources() {{
# Use mongosh itself to list datasources
mongosh config --list-datasources 2>/dev/null
}}
# Datasource completion function
_mongosh_datasources() {{
local -a datasources
datasources=($(_mongosh_list_datasources))
_describe 'datasources' datasources
}}
# Get original mongosh completion function
_mongosh_original() {{
_mongosh "$@"
}}
# Enhanced completion function
_mongosh_enhanced() {{
local curcontext="$curcontext" state line
typeset -A opt_args
# Check if we're completing the datasource argument
if [[ ${{words[CURRENT-1]}} == "-d" || ${{words[CURRENT-1]}} == "--datasource" ]]; then
_mongosh_datasources
return 0
fi
# Otherwise use original completion
_mongosh_original "$@"
}}
# Replace the completion function
compdef _mongosh_enhanced mongosh
"#,
basic_completion
);
print!("{}", custom_completion);
Ok(())
}
fn generate_fish_completion() -> Result<()> {
let mut cmd = CliArgs::command();
let mut buffer = Vec::new();
generate(Shell::Fish, &mut cmd, "mongosh", &mut buffer);
let basic_completion = String::from_utf8_lossy(&buffer);
let custom_completion = format!(
r#"{}
# Custom completion for datasource names
function __mongosh_list_datasources
# Use mongosh itself to list datasources
mongosh config --list-datasources 2>/dev/null
end
# Add dynamic completion for -d/--datasource
complete -c mongosh -s d -l datasource -f -a "(__mongosh_list_datasources)" -d "Datasource name from config file"
"#,
basic_completion
);
print!("{}", custom_completion);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_shell() {
assert!(matches!(parse_shell("bash"), Ok(Shell::Bash)));
assert!(matches!(parse_shell("zsh"), Ok(Shell::Zsh)));
assert!(matches!(parse_shell("fish"), Ok(Shell::Fish)));
assert!(parse_shell("invalid").is_err());
}
#[test]
fn test_parse_shell_case_insensitive() {
assert!(matches!(parse_shell("BASH"), Ok(Shell::Bash)));
assert!(matches!(parse_shell("Zsh"), Ok(Shell::Zsh)));
assert!(matches!(parse_shell("FiSh"), Ok(Shell::Fish)));
}
}