Skip to main content

voirs_cli/completion/
mod.rs

1//! Shell completion support for VoiRS CLI
2//!
3//! Provides shell completion scripts for bash, zsh, fish, and PowerShell.
4
5use clap::CommandFactory;
6use clap_complete::{generate, Generator, Shell};
7use std::io::{self, Write};
8use std::path::Path;
9
10use crate::CliApp;
11
12/// Generate shell completion script for the specified shell
13pub fn generate_completion<G: Generator>(
14    shell: G,
15    mut output: impl Write,
16) -> Result<(), io::Error> {
17    let mut app = CliApp::command();
18    generate(shell, &mut app, "voirs", &mut output);
19    Ok(())
20}
21
22/// Generate completion script to a file
23pub fn generate_completion_to_file<P: AsRef<Path>>(
24    shell: Shell,
25    output_path: P,
26) -> Result<(), io::Error> {
27    let file = std::fs::File::create(output_path)?;
28    generate_completion(shell, file)
29}
30
31/// Generate completion script and write to stdout
32pub fn generate_completion_to_stdout(shell: Shell) -> Result<(), io::Error> {
33    let stdout = io::stdout();
34    generate_completion(shell, stdout.lock())
35}
36
37/// Get installation instructions for the generated completion script
38pub fn get_installation_instructions(shell: Shell) -> String {
39    match shell {
40        Shell::Bash => r#"# Installation instructions for Bash completion:
41
421. Save the completion script:
43   voirs generate-completion bash > ~/.local/share/bash-completion/completions/voirs
44
452. Or for system-wide installation:
46   sudo voirs generate-completion bash > /usr/share/bash-completion/completions/voirs
47
483. Restart your shell or source the completion:
49   source ~/.local/share/bash-completion/completions/voirs
50
51Note: Make sure bash-completion is installed on your system."#
52            .to_string(),
53        Shell::Zsh => r#"# Installation instructions for Zsh completion:
54
551. Save the completion script to a directory in your $fpath:
56   voirs generate-completion zsh > ~/.local/share/zsh/site-functions/_voirs
57
582. Or add to your .zshrc:
59   # Add this line to your .zshrc
60   fpath=(~/.local/share/zsh/site-functions $fpath)
61   autoload -Uz compinit && compinit
62
633. Restart your shell or reload completions:
64   exec zsh
65
66Alternative: Place in any directory in your $fpath and run 'compinit'."#
67            .to_string(),
68        Shell::Fish => r#"# Installation instructions for Fish completion:
69
701. Save the completion script:
71   voirs generate-completion fish > ~/.config/fish/completions/voirs.fish
72
732. Restart fish or reload completions:
74   exec fish
75
76Fish will automatically load completions from the completions directory."#
77            .to_string(),
78        Shell::PowerShell => r#"# Installation instructions for PowerShell completion:
79
801. Check your PowerShell profile location:
81   $PROFILE
82
832. Create the profile directory if it doesn't exist:
84   New-Item -ItemType Directory -Path (Split-Path $PROFILE) -Force
85
863. Add the completion to your profile:
87   voirs generate-completion powershell >> $PROFILE
88
894. Restart PowerShell or reload your profile:
90   . $PROFILE
91
92Note: You may need to adjust PowerShell execution policy to load the profile."#
93            .to_string(),
94        Shell::Elvish => r#"# Installation instructions for Elvish completion:
95
961. Save the completion script:
97   voirs generate-completion elvish > ~/.elvish/lib/voirs-completion.elv
98
992. Add to your rc.elv:
100   use ./voirs-completion
101
1023. Restart Elvish:
103   exec elvish"#
104            .to_string(),
105        _ => "Completion installation instructions not available for this shell.".to_string(),
106    }
107}
108
109/// Generate a comprehensive completion installation script
110pub fn generate_install_script() -> String {
111    r#"#!/bin/bash
112# VoiRS CLI Completion Installation Script
113# Automatically installs shell completions for VoiRS CLI
114
115set -e
116
117# Colors for output
118RED='\033[0;31m'
119GREEN='\033[0;32m'
120YELLOW='\033[1;33m'
121BLUE='\033[0;34m'
122NC='\033[0m' # No Color
123
124# Logging functions
125log_info() {
126    echo -e "${BLUE}[INFO]${NC} $1"
127}
128
129log_success() {
130    echo -e "${GREEN}[SUCCESS]${NC} $1"
131}
132
133log_warn() {
134    echo -e "${YELLOW}[WARN]${NC} $1"
135}
136
137log_error() {
138    echo -e "${RED}[ERROR]${NC} $1"
139}
140
141# Check if voirs is available
142check_voirs() {
143    if ! command -v voirs &> /dev/null; then
144        log_error "voirs command not found. Please install VoiRS CLI first."
145        exit 1
146    fi
147    log_info "VoiRS CLI found: $(which voirs)"
148}
149
150# Install bash completion
151install_bash() {
152    log_info "Installing Bash completion..."
153    
154    # Check for bash-completion package
155    if [ -d "/usr/share/bash-completion/completions" ]; then
156        # System-wide installation
157        sudo voirs generate-completion bash > /tmp/voirs_completion_bash
158        sudo mv /tmp/voirs_completion_bash /usr/share/bash-completion/completions/voirs
159        log_success "Bash completion installed system-wide"
160    elif [ -d "$HOME/.local/share/bash-completion/completions" ]; then
161        # User installation
162        mkdir -p "$HOME/.local/share/bash-completion/completions"
163        voirs generate-completion bash > "$HOME/.local/share/bash-completion/completions/voirs"
164        log_success "Bash completion installed for user"
165    else
166        log_warn "Bash completion directory not found. Creating user directory..."
167        mkdir -p "$HOME/.local/share/bash-completion/completions"
168        voirs generate-completion bash > "$HOME/.local/share/bash-completion/completions/voirs"
169        log_success "Bash completion installed for user"
170    fi
171}
172
173# Install zsh completion
174install_zsh() {
175    log_info "Installing Zsh completion..."
176    
177    # Find zsh fpath directory
178    local zsh_dir=""
179    if [ -n "$ZSH" ] && [ -d "$ZSH/completions" ]; then
180        zsh_dir="$ZSH/completions"
181    elif [ -d "/usr/local/share/zsh/site-functions" ]; then
182        zsh_dir="/usr/local/share/zsh/site-functions"
183    elif [ -d "$HOME/.zsh/completions" ]; then
184        zsh_dir="$HOME/.zsh/completions"
185    else
186        mkdir -p "$HOME/.zsh/completions"
187        zsh_dir="$HOME/.zsh/completions"
188    fi
189    
190    voirs generate-completion zsh > "$zsh_dir/_voirs"
191    log_success "Zsh completion installed to $zsh_dir"
192}
193
194# Install fish completion
195install_fish() {
196    log_info "Installing Fish completion..."
197    
198    local fish_dir="$HOME/.config/fish/completions"
199    mkdir -p "$fish_dir"
200    voirs generate-completion fish > "$fish_dir/voirs.fish"
201    log_success "Fish completion installed to $fish_dir"
202}
203
204# Install PowerShell completion
205install_powershell() {
206    log_info "Installing PowerShell completion..."
207    
208    if command -v pwsh &> /dev/null; then
209        local ps_profile=$(pwsh -NoProfile -Command 'Write-Host $PROFILE')
210        local ps_dir=$(dirname "$ps_profile")
211        mkdir -p "$ps_dir"
212        voirs generate-completion powershell > "$ps_dir/voirs_completion.ps1"
213        log_success "PowerShell completion installed to $ps_dir"
214        log_warn "You may need to update your PowerShell profile to source the completion"
215    else
216        log_warn "PowerShell not found. Skipping PowerShell completion."
217    fi
218}
219
220# Install all completions
221install_all() {
222    log_info "Installing completions for all available shells..."
223    
224    command -v bash &> /dev/null && install_bash
225    command -v zsh &> /dev/null && install_zsh
226    command -v fish &> /dev/null && install_fish
227    command -v pwsh &> /dev/null && install_powershell
228    
229    log_success "Installation complete!"
230    log_info "You may need to restart your shell or source the completions."
231}
232
233# Show usage
234show_usage() {
235    echo "VoiRS CLI Completion Installation Script"
236    echo ""
237    echo "Usage: $0 [OPTION]"
238    echo ""
239    echo "Options:"
240    echo "  bash        Install Bash completion"
241    echo "  zsh         Install Zsh completion"
242    echo "  fish        Install Fish completion"
243    echo "  powershell  Install PowerShell completion"
244    echo "  all         Install completions for all available shells"
245    echo "  help        Show this help message"
246    echo ""
247    echo "If no option is provided, 'all' is assumed."
248}
249
250# Main function
251main() {
252    check_voirs
253    
254    case "${1:-all}" in
255        bash)
256            install_bash
257            ;;
258        zsh)
259            install_zsh
260            ;;
261        fish)
262            install_fish
263            ;;
264        powershell)
265            install_powershell
266            ;;
267        all)
268            install_all
269            ;;
270        help)
271            show_usage
272            ;;
273        *)
274            log_error "Unknown option: $1"
275            show_usage
276            exit 1
277            ;;
278    esac
279}
280
281main "$@"
282"#
283    .to_string()
284}
285
286/// Display completion status and installation info
287pub fn display_completion_status() -> String {
288    let mut output = String::new();
289
290    output.push_str("VoiRS CLI Shell Completion Status\n");
291    output.push_str("==================================\n\n");
292
293    let shells = [
294        ("bash", "Bash"),
295        ("zsh", "Zsh"),
296        ("fish", "Fish"),
297        ("pwsh", "PowerShell"),
298    ];
299
300    for (cmd, name) in shells.iter() {
301        let status = if std::process::Command::new(cmd)
302            .arg("--version")
303            .output()
304            .is_ok()
305        {
306            "✓ Available"
307        } else {
308            "✗ Not installed"
309        };
310
311        output.push_str(&format!("{:12} {}\n", name, status));
312    }
313
314    output.push_str("\nTo generate completion scripts:\n");
315    output.push_str("  voirs generate-completion <shell> > completion_file\n\n");
316    output.push_str("Supported shells: bash, zsh, fish, powershell, elvish\n\n");
317    output.push_str("For installation instructions:\n");
318    output.push_str("  voirs generate-completion <shell> --help\n");
319
320    output
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326    use std::io::Cursor;
327
328    #[test]
329    fn test_generate_bash_completion() {
330        let mut output = Cursor::new(Vec::new());
331        generate_completion(Shell::Bash, &mut output).unwrap();
332
333        let result = String::from_utf8(output.into_inner()).unwrap();
334        assert!(result.contains("voirs"));
335        assert!(result.contains("_voirs"));
336    }
337
338    #[test]
339    fn test_installation_instructions() {
340        let bash_instructions = get_installation_instructions(Shell::Bash);
341        assert!(bash_instructions.contains("bash-completion"));
342        assert!(bash_instructions.contains("~/.local/share"));
343
344        let zsh_instructions = get_installation_instructions(Shell::Zsh);
345        assert!(zsh_instructions.contains("$fpath"));
346        assert!(zsh_instructions.contains("_voirs"));
347    }
348
349    #[test]
350    fn test_install_script_generation() {
351        let script = generate_install_script();
352        assert!(script.contains("#!/bin/bash"));
353        assert!(script.contains("voirs generate-completion"));
354        assert!(script.contains("install_bash"));
355        assert!(script.contains("install_zsh"));
356    }
357
358    #[test]
359    fn test_completion_status() {
360        let status = display_completion_status();
361        assert!(status.contains("VoiRS CLI Shell Completion Status"));
362        assert!(status.contains("Bash"));
363        assert!(status.contains("Zsh"));
364        assert!(status.contains("Fish"));
365    }
366}