1use std::collections::HashMap;
4use std::path::PathBuf;
5
6use anyhow::{bail, Context, Result};
7use clap::{Args, Subcommand};
8use clap_complete::{generator::generate_to, shells::Shell};
9use wash_lib::cli::CommandOutput;
10use wash_lib::config::cfg_dir;
11
12const TOKEN_FILE: &str = ".completion_suggested";
13const COMPLETION_DOC_URL: &str =
14 "https://github.com/wasmCloud/wasmCloud/blob/main/crates/wash-cli/Completions.md";
15const SLACK_URL: &str = "https://slack.wasmcloud.com";
16
17fn instructions() -> String {
18 format!(
19 "🐚 Autocomplete available! To configure autocomplete with your shell, follow the instructions at\n {}",
20 COMPLETION_DOC_URL
21 )
22}
23
24fn feedback() -> String {
25 format!(
26 "📝 Feedback wanted! If you want to suggest an improvement or would like assistance, join the community at\n {}",
27 SLACK_URL
28 )
29}
30
31#[derive(Debug, Clone, Args)]
32pub struct CompletionOpts {
33 #[clap(short = 'd', long = "dir")]
35 dir: Option<PathBuf>,
36
37 #[clap(name = "shell", subcommand)]
39 shell: ShellSelection,
40}
41
42#[derive(Subcommand, Debug, Clone)]
43pub enum ShellSelection {
44 Zsh,
46 Bash,
48 Fish,
50 PowerShell,
52}
53
54pub fn first_run_suggestion() -> Result<Option<String>> {
56 let cfg_dir = cfg_dir()?;
57 let token = cfg_dir.join(TOKEN_FILE);
58 if token.is_file() {
59 return Ok(None);
60 }
61 let _ = std::fs::File::create(token).with_context(|| {
62 format!(
63 "can't create completion first-run token in {}",
64 cfg_dir.display()
65 )
66 })?;
67 Ok(Some(format!(
68 "Congratulations on installing wash!\n\n{}\n\n{}",
69 instructions(),
70 feedback(),
71 )))
72}
73
74pub fn handle_command(
75 opts: CompletionOpts,
76 mut command: clap::builder::Command,
77) -> Result<CommandOutput> {
78 let output_dir = opts.dir.unwrap_or_else(|| PathBuf::from("."));
79
80 let shell = match opts.shell {
81 ShellSelection::Zsh => Shell::Zsh,
82 ShellSelection::Bash => Shell::Bash,
83 ShellSelection::Fish => Shell::Fish,
84 ShellSelection::PowerShell => Shell::PowerShell,
85 };
86
87 match generate_to(shell, &mut command, "wash", &output_dir) {
88 Ok(path) => {
89 let mut map = HashMap::new();
90 map.insert(
91 "path".to_string(),
92 path.to_string_lossy().to_string().into(),
93 );
94 Ok(CommandOutput::new(
95 format!(
96 "Generated completion file: {}. {}",
97 path.display(),
98 instructions()
99 ),
100 map,
101 ))
102 }
103 Err(e) => bail!(
104 "generating shell completion file in folder '{}': {}",
105 output_dir.display(),
106 e
107 ),
108 }
109}