forc/cli/commands/
completions.rs

1use std::fmt::Display;
2
3use clap::{Command as ClapCommand, CommandFactory, Parser};
4use clap_complete::{generate, Generator, Shell};
5use forc_util::ForcResult;
6
7#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)]
8enum Target {
9    /// Bourne Again Shell (bash)
10    Bash,
11    /// Elvish shell
12    Elvish,
13    /// Friendly Interactive Shell (fish)
14    Fish,
15    /// PowerShell
16    PowerShell,
17    /// Z Shell (zsh)
18    Zsh,
19    /// Fig
20    Fig,
21}
22
23impl Display for Target {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        write!(
26            f,
27            "{}",
28            match self {
29                Target::Bash => "bash".to_string(),
30                Target::Elvish => "elvish".to_string(),
31                Target::Fish => "fish".to_string(),
32                Target::PowerShell => "powershell".to_string(),
33                Target::Zsh => "zsh".to_string(),
34                Target::Fig => "fig".to_string(),
35            }
36        )
37    }
38}
39
40/// Generate tab-completion scripts for your shell
41#[derive(Debug, Parser)]
42pub struct Command {
43    /// Specify shell to enable tab-completion for
44    ///
45    /// [possible values: zsh, bash, fish, powershell, elvish]
46    ///
47    /// For more info: https://fuellabs.github.io/sway/latest/forc/commands/forc_completions.html
48    #[clap(short = 'T', long, value_enum)]
49    target: Target,
50}
51
52pub(crate) fn exec(command: Command) -> ForcResult<()> {
53    let mut cmd = super::super::Opt::command();
54    match command.target {
55        Target::Fig => print_completions(clap_complete_fig::Fig, &mut cmd),
56        Target::Bash => print_completions(Shell::Bash, &mut cmd),
57        Target::Elvish => print_completions(Shell::Elvish, &mut cmd),
58        Target::PowerShell => print_completions(Shell::PowerShell, &mut cmd),
59        Target::Zsh => print_completions(Shell::Zsh, &mut cmd),
60        Target::Fish => print_completions(Shell::Fish, &mut cmd),
61    }
62    Ok(())
63}
64
65fn print_completions<G: Generator>(gen: G, cmd: &mut ClapCommand) {
66    generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
67}
68
69#[cfg(test)]
70mod test {
71    use super::*;
72    use crate::cli::{Forc, Opt};
73
74    #[test]
75    fn bash() {
76        testsuite::<completest_pty::BashRuntimeBuilder>(Shell::Bash);
77    }
78
79    #[test]
80    fn zsh() {
81        testsuite::<completest_pty::ZshRuntimeBuilder>(Shell::Zsh);
82    }
83
84    #[test]
85    fn fish() {
86        testsuite::<completest_pty::FishRuntimeBuilder>(Shell::Fish);
87    }
88
89    fn testsuite<R>(shell: Shell)
90    where
91        R: completest_pty::RuntimeBuilder,
92    {
93        let bin_root = "/tmp/bin".into();
94        let home = "/tmp/home".into();
95        let runtime = R::new(bin_root, home).expect("runtime");
96        build_script_and_test(runtime, shell, "forc", &Forc::possible_values());
97    }
98
99    fn build_script_and_test<R>(
100        mut runtime: R,
101        shell: Shell,
102        command_to_complete: &str,
103        expectations: &[&str],
104    ) where
105        R: completest_pty::Runtime,
106    {
107        let term = completest_pty::Term::new();
108        let mut cmd = Opt::command();
109        let mut completion_script = Vec::<u8>::new();
110
111        generate(shell, &mut cmd, "forc".to_owned(), &mut completion_script);
112
113        runtime
114            .register("forc", &String::from_utf8_lossy(&completion_script))
115            .expect("register completion script");
116
117        let output =
118            if let Ok(output) = runtime.complete(&format!("{command_to_complete} \t\t"), &term) {
119                output
120            } else {
121                println!("Skipping {shell}");
122                return;
123            };
124
125        for expectation in expectations {
126            assert!(
127                output.contains(expectation),
128                "Failed find {expectation} in {output}"
129            );
130        }
131    }
132}