cli_xtask/subcommand/
dist_build_completion.rs1use std::fmt;
2
3use cargo_metadata::camino::{Utf8Path, Utf8PathBuf};
4use clap_complete::Generator;
5
6use crate::{config::Config, fs::ToRelative, Result, Run};
7
8#[cfg_attr(doc, doc = include_str!("../../doc/cargo-xtask-dist-build-completion.md"))]
10#[derive(Debug, Clone, Default, clap::Args)]
11#[non_exhaustive]
12pub struct DistBuildCompletion {}
13
14impl Run for DistBuildCompletion {
15 fn run(&self, config: &Config) -> Result<()> {
16 self.run(config)
17 }
18}
19
20#[derive(Debug, Clone, Copy)]
21enum Shell {
22 Bash,
23 Elvish,
24 Fish,
25 #[allow(clippy::enum_variant_names)]
26 PowerShell,
27 Zsh,
28 Nushell,
29}
30
31impl fmt::Display for Shell {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 Shell::Bash => fmt::Display::fmt(&clap_complete::Shell::Bash, f),
35 Shell::Elvish => fmt::Display::fmt(&clap_complete::Shell::Elvish, f),
36 Shell::Fish => fmt::Display::fmt(&clap_complete::Shell::Fish, f),
37 Shell::PowerShell => fmt::Display::fmt(&clap_complete::Shell::PowerShell, f),
38 Shell::Zsh => fmt::Display::fmt(&clap_complete::Shell::Zsh, f),
39 Shell::Nushell => fmt::Display::fmt("nushell", f),
40 }
41 }
42}
43
44impl Generator for Shell {
45 fn file_name(&self, name: &str) -> String {
46 match self {
47 Shell::Bash => Generator::file_name(&clap_complete::Shell::Bash, name),
48 Shell::Elvish => Generator::file_name(&clap_complete::Shell::Elvish, name),
49 Shell::Fish => Generator::file_name(&clap_complete::Shell::Fish, name),
50 Shell::PowerShell => Generator::file_name(&clap_complete::Shell::PowerShell, name),
51 Shell::Zsh => Generator::file_name(&clap_complete::Shell::Zsh, name),
52 Shell::Nushell => Generator::file_name(&clap_complete_nushell::Nushell, name),
53 }
54 }
55
56 fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
57 match self {
58 Shell::Bash => Generator::generate(&clap_complete::Shell::Bash, cmd, buf),
59 Shell::Elvish => Generator::generate(&clap_complete::Shell::Elvish, cmd, buf),
60 Shell::Fish => Generator::generate(&clap_complete::Shell::Fish, cmd, buf),
61 Shell::PowerShell => Generator::generate(&clap_complete::Shell::PowerShell, cmd, buf),
62 Shell::Zsh => Generator::generate(&clap_complete::Shell::Zsh, cmd, buf),
63 Shell::Nushell => Generator::generate(&clap_complete_nushell::Nushell, cmd, buf),
64 }
65 }
66}
67
68impl DistBuildCompletion {
69 #[tracing::instrument(name = "dist-build-completion", skip_all, err)]
71 pub fn run(&self, config: &Config) -> Result<()> {
72 tracing::info!("Building shell completion files...");
73
74 let Self {} = self;
75 let config = config.dist()?;
76
77 let out_dir = config.dist_working_directory(None).join("completion");
78 crate::fs::remove_dir(&out_dir)?;
79
80 let shells = [
81 Shell::Bash,
82 Shell::Elvish,
83 Shell::Fish,
84 Shell::PowerShell,
85 Shell::Zsh,
86 Shell::Nushell,
87 ];
88
89 for package in config.packages() {
90 for target in package.targets() {
91 let target_name = target.name();
92 if let Some(cmd) = target.command() {
93 for shell in shells {
94 generate(shell, cmd, target_name, &out_dir)?;
95 }
96 }
97 }
98 }
99
100 Ok(())
101 }
102}
103
104fn generate(
105 shell: Shell,
106 cmd: &clap::Command,
107 bin_name: &str,
108 out_dir: &Utf8Path,
109) -> Result<Utf8PathBuf> {
110 crate::fs::create_dir(out_dir)?;
111 let path = clap_complete::generate_to(shell, &mut cmd.clone(), bin_name, out_dir)?;
112 let path = Utf8PathBuf::try_from(path)?;
113 tracing::info!("Generated {shell} completion file: {}", path.to_relative());
114 Ok(path)
115}