Skip to main content

muster/adapter/cli/
completions.rs

1use strum::Display;
2
3/// Shells supported by the dynamic completion engine.
4#[derive(Clone, Copy, Debug, PartialEq, Eq, clap::ValueEnum, Display)]
5#[strum(serialize_all = "lowercase")]
6pub enum CompletionShell {
7    Bash,
8    Zsh,
9    Fish,
10    Elvish,
11    Powershell,
12}
13
14/// Registration hook per shell, as documented by clap_complete's CompleteEnv.
15const BASH_LINE: &str = "source <(COMPLETE=bash muster)";
16const ZSH_LINE: &str = "source <(COMPLETE=zsh muster)";
17const FISH_LINE: &str = "COMPLETE=fish muster | source";
18const ELVISH_LINE: &str = "eval (E:COMPLETE=elvish muster | slurp)";
19const POWERSHELL_LINE: &str = "$env:COMPLETE = \"powershell\"; muster | Out-String | Invoke-Expression; Remove-Item Env:\\COMPLETE";
20
21/// Where each shell's hook line belongs.
22const BASH_DESTINATION: &str = "~/.bashrc";
23const ZSH_DESTINATION: &str = "~/.zshrc";
24const FISH_DESTINATION: &str = "~/.config/fish/completions/muster.fish";
25const ELVISH_DESTINATION: &str = "~/.elvish/rc.elv";
26const POWERSHELL_DESTINATION: &str = "$PROFILE";
27
28/// The shell hook line enabling muster's dynamic completions.
29pub fn registration_line(shell: CompletionShell) -> &'static str {
30    match shell {
31        CompletionShell::Bash => BASH_LINE,
32        CompletionShell::Zsh => ZSH_LINE,
33        CompletionShell::Fish => FISH_LINE,
34        CompletionShell::Elvish => ELVISH_LINE,
35        CompletionShell::Powershell => POWERSHELL_LINE,
36    }
37}
38
39/// The comment naming the destination file, then the hook line itself.
40pub fn registration(shell: CompletionShell) -> [String; 2] {
41    let destination = match shell {
42        CompletionShell::Bash => BASH_DESTINATION,
43        CompletionShell::Zsh => ZSH_DESTINATION,
44        CompletionShell::Fish => FISH_DESTINATION,
45        CompletionShell::Elvish => ELVISH_DESTINATION,
46        CompletionShell::Powershell => POWERSHELL_DESTINATION,
47    };
48    [
49        format!("# add to {destination}:"),
50        registration_line(shell).to_string(),
51    ]
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    /// Each shell yields its documented dynamic-completion hook line.
59    #[test]
60    fn every_shell_has_its_registration_line() {
61        assert_eq!(
62            registration_line(CompletionShell::Bash),
63            "source <(COMPLETE=bash muster)"
64        );
65        assert_eq!(
66            registration_line(CompletionShell::Zsh),
67            "source <(COMPLETE=zsh muster)"
68        );
69        assert_eq!(
70            registration_line(CompletionShell::Fish),
71            "COMPLETE=fish muster | source"
72        );
73        assert_eq!(
74            registration_line(CompletionShell::Elvish),
75            "eval (E:COMPLETE=elvish muster | slurp)"
76        );
77        assert!(registration_line(CompletionShell::Powershell).contains("COMPLETE"));
78    }
79
80    /// The printed output pairs a destination hint with the line.
81    #[test]
82    fn output_names_the_destination_file() {
83        let [hint, line] = registration(CompletionShell::Zsh);
84        assert!(hint.contains(".zshrc"));
85        assert_eq!(line, registration_line(CompletionShell::Zsh));
86    }
87}