forest/cli_shared/cli/
completion_cmd.rs1use crate::cli::subcommands::Cli as ForestCli;
4use crate::daemon::main::Cli as ForestDaemonCli;
5use crate::tool::subcommands::Cli as ForestToolCli;
6use crate::wallet::subcommands::Cli as ForestWalletCli;
7use ahash::HashMap;
8use clap::{Command, CommandFactory};
9use clap_complete::aot::{Shell, generate};
10
11#[derive(Debug, clap::Args)]
13pub struct CompletionCommand {
14 #[arg(value_delimiter = ',')]
17 binaries: Option<Vec<String>>,
18 #[arg(long, default_value = "bash")]
20 shell: Shell,
21}
22
23impl CompletionCommand {
24 pub fn run<W: std::io::Write>(self, writer: &mut W) -> anyhow::Result<()> {
25 let mut bin_cmd_map: HashMap<String, Command> = HashMap::from_iter([
26 ("forest".to_string(), ForestDaemonCli::command()),
27 ("forest-cli".to_string(), ForestCli::command()),
28 ("forest-wallet".to_string(), ForestWalletCli::command()),
29 ("forest-tool".to_string(), ForestToolCli::command()),
30 ]);
31
32 let valid_binaries = bin_cmd_map.keys().cloned().collect::<Vec<_>>();
33 let binaries = self.binaries.unwrap_or_else(|| valid_binaries.clone());
34
35 for b in binaries {
36 let cmd = bin_cmd_map.get_mut(&b).ok_or_else(|| {
37 anyhow::anyhow!(
38 "Unknown binary: '{}'. Valid binaries are: {:?}",
39 b,
40 valid_binaries.join(",")
41 )
42 })?;
43
44 generate(
45 self.shell,
46 cmd,
47 cmd.get_bin_name().unwrap().to_string(),
48 writer,
49 );
50 }
51 Ok(())
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn test_completion_no_binaries_succeeds() {
61 let cmd = CompletionCommand {
62 binaries: None,
63 shell: Shell::Bash,
64 };
65
66 let result = cmd.run(&mut std::io::sink());
68 assert!(
69 result.is_ok(),
70 "Expected command to succeed, got: {result:?}"
71 );
72 }
73
74 #[test]
75 fn test_completion_binaries_succeeds() {
76 let cmd = CompletionCommand {
77 binaries: Some(vec!["forest-cli".to_string(), "forest-tool".to_string()]),
78 shell: Shell::Bash,
79 };
80
81 let result = cmd.run(&mut std::io::sink());
82 assert!(
83 result.is_ok(),
84 "Expected command to succeed, got {result:?}"
85 );
86 }
87
88 #[test]
89 fn test_completion_binaries_fails() {
90 let cmd = CompletionCommand {
91 binaries: Some(vec!["non-existent-binary".to_string()]),
92 shell: Shell::Bash,
93 };
94
95 let result = cmd.run(&mut std::io::sink());
96 assert!(
97 result.is_err(),
98 "Expected command to fail, but it succeeded"
99 );
100
101 let err = result.unwrap_err().to_string();
102 assert!(
103 err.contains("Unknown binary") && err.contains("non-existent-binary"),
104 "Error message '{err}' did not contain expected text"
105 );
106 }
107
108 #[test]
109 fn test_completion_mixed_valid_invalid_fails() {
110 let cmd = CompletionCommand {
112 binaries: Some(vec![
113 "forest-cli".to_string(),
114 "non-existent-binary".to_string(),
115 ]),
116 shell: Shell::Bash,
117 };
118
119 let result = cmd.run(&mut std::io::sink());
120 assert!(
121 result.is_err(),
122 "Expected command to fail, but it succeeded"
123 );
124
125 let err = result.unwrap_err().to_string();
126 assert!(
127 err.contains("Unknown binary") && err.contains("non-existent-binary"),
128 "Error message '{err}' did not contain expected text"
129 );
130 }
131}