1//! Shell completion machinery
2
3pub mod utils;
4
5use std::ffi::OsString;
6use std::fs::File;
7use std::io::Error;
8use std::io::Write;
9use std::path::PathBuf;
10
11use clap::Command;
12
13/// Generator trait which can be used to write generators
14pub trait Generator {
15 /// Returns the file name that is created when this generator is called during compile time.
16 ///
17 /// # Panics
18 ///
19 /// May panic when called outside of the context of [`generate`] or [`generate_to`]
20 ///
21 /// # Examples
22 ///
23 /// ```
24 /// # use std::io::Write;
25 /// # use clap::Command;
26 /// use clap_complete::Generator;
27 ///
28 /// pub struct Fish;
29 ///
30 /// impl Generator for Fish {
31 /// fn file_name(&self, name: &str) -> String {
32 /// format!("{name}.fish")
33 /// }
34 /// # fn generate(&self, cmd: &Command, buf: &mut dyn Write) {}
35 /// }
36 /// ```
37 fn file_name(&self, name: &str) -> String;
38
39 /// Generates output out of [`clap::Command`](Command).
40 ///
41 /// # Panics
42 ///
43 /// May panic when called outside of the context of [`generate`] or [`generate_to`]
44 ///
45 /// # Examples
46 ///
47 /// The following example generator displays the [`clap::Command`](Command)
48 /// as if it is printed using [`std::println`].
49 ///
50 /// ```
51 /// use std::{io::Write, fmt::write};
52 /// use clap::Command;
53 /// use clap_complete::Generator;
54 ///
55 /// pub struct ClapDebug;
56 ///
57 /// impl Generator for ClapDebug {
58 /// # fn file_name(&self, name: &str) -> String {
59 /// # name.into()
60 /// # }
61 /// fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
62 /// write!(buf, "{cmd}").unwrap();
63 /// }
64 /// }
65 /// ```
66 fn generate(&self, cmd: &Command, buf: &mut dyn Write);
67}
68
69/// Generate a completions file for a specified shell at compile-time.
70///
71/// **NOTE:** to generate the file at compile time you must use a `build.rs` "Build Script" or a
72/// [`cargo-xtask`](https://github.com/matklad/cargo-xtask)
73///
74/// # Examples
75///
76/// The following example generates a bash completion script via a `build.rs` script. In this
77/// simple example, we'll demo a very small application with only a single subcommand and two
78/// args. Real applications could be many multiple levels deep in subcommands, and have tens or
79/// potentially hundreds of arguments.
80///
81/// First, it helps if we separate out our `Command` definition into a separate file. Whether you
82/// do this as a function, or bare Command definition is a matter of personal preference.
83///
84/// ```
85/// // src/cli.rs
86/// # use clap::{Command, Arg, ArgAction};
87/// pub fn build_cli() -> Command {
88/// Command::new("compl")
89/// .about("Tests completions")
90/// .arg(Arg::new("file")
91/// .help("some input file"))
92/// .subcommand(Command::new("test")
93/// .about("tests things")
94/// .arg(Arg::new("case")
95/// .long("case")
96/// .action(ArgAction::Set)
97/// .help("the case to test")))
98/// }
99/// ```
100///
101/// In our regular code, we can simply call this `build_cli()` function, then call
102/// `get_matches()`, or any of the other normal methods directly after. For example:
103///
104/// ```ignore
105/// // src/main.rs
106///
107/// mod cli;
108///
109/// fn main() {
110/// let _m = cli::build_cli().get_matches();
111///
112/// // normal logic continues...
113/// }
114/// ```
115///
116/// Next, we set up our `Cargo.toml` to use a `build.rs` build script.
117///
118/// ```toml
119/// # Cargo.toml
120/// build = "build.rs"
121///
122/// [dependencies]
123/// clap = "*"
124///
125/// [build-dependencies]
126/// clap = "*"
127/// clap_complete = "*"
128/// ```
129///
130/// Next, we place a `build.rs` in our project root.
131///
132/// ```ignore
133/// use clap_complete::{generate_to, shells::Bash};
134/// use std::env;
135/// use std::io::Error;
136///
137/// include!("src/cli.rs");
138///
139/// fn main() -> Result<(), Error> {
140/// let outdir = match env::var_os("OUT_DIR") {
141/// None => return Ok(()),
142/// Some(outdir) => outdir,
143/// };
144///
145/// let mut cmd = build_cli();
146/// let path = generate_to(
147/// Bash,
148/// &mut cmd, // We need to specify what generator to use
149/// "myapp", // We need to specify the bin name manually
150/// outdir, // We need to specify where to write to
151/// )?;
152///
153/// println!("cargo:warning=completion file is generated: {path:?}");
154///
155/// Ok(())
156/// }
157/// ```
158///
159/// Now, once we compile there will be a `{bin_name}.bash` file in the directory.
160/// Assuming we compiled with debug mode, it would be somewhere similar to
161/// `<project>/target/debug/build/myapp-<hash>/out/myapp.bash`.
162///
163/// **NOTE:** Please look at the individual [shells][crate::shells]
164/// to see the name of the files generated.
165///
166/// Using [`ValueEnum::value_variants()`][clap::ValueEnum::value_variants] you can easily loop over
167/// all the supported shell variants to generate all the completions at once too.
168///
169/// ```ignore
170/// use clap::ValueEnum;
171/// use clap_complete::{generate_to, Shell};
172/// use std::env;
173/// use std::io::Error;
174///
175/// include!("src/cli.rs");
176///
177/// fn main() -> Result<(), Error> {
178/// let outdir = match env::var_os("OUT_DIR") {
179/// None => return Ok(()),
180/// Some(outdir) => outdir,
181/// };
182///
183/// let mut cmd = build_cli();
184/// for &shell in Shell::value_variants() {
185/// generate_to(shell, &mut cmd, "myapp", outdir)?;
186/// }
187///
188/// Ok(())
189/// }
190/// ```
191pub fn generate_to<G, S, T>(
192 gen: G,
193 cmd: &mut Command,
194 bin_name: S,
195 out_dir: T,
196) -> Result<PathBuf, Error>
197where
198 G: Generator,
199 S: Into<String>,
200 T: Into<OsString>,
201{
202 cmd.set_bin_name(bin_name);
203
204 let out_dir: PathBuf = PathBuf::from(out_dir.into());
205 let file_name: String = gen.file_name(cmd.get_bin_name().unwrap());
206
207 let path: PathBuf = out_dir.join(path:file_name);
208 let mut file: File = File::create(&path)?;
209
210 _generate::<G>(gen, cmd, &mut file);
211 Ok(path)
212}
213
214/// Generate a completions file for a specified shell at runtime.
215///
216/// Until `cargo install` can install extra files like a completion script, this may be
217/// used e.g. in a command that outputs the contents of the completion script, to be
218/// redirected into a file by the user.
219///
220/// # Examples
221///
222/// Assuming a separate `cli.rs` like the [`generate_to` example](generate_to()),
223/// we can let users generate a completion script using a command:
224///
225/// ```ignore
226/// // src/main.rs
227///
228/// mod cli;
229/// use std::io;
230/// use clap_complete::{generate, shells::Bash};
231///
232/// fn main() {
233/// let matches = cli::build_cli().get_matches();
234///
235/// if matches.is_present("generate-bash-completions") {
236/// generate(Bash, &mut cli::build_cli(), "myapp", &mut io::stdout());
237/// }
238///
239/// // normal logic continues...
240/// }
241///
242/// ```
243///
244/// Usage:
245///
246/// ```console
247/// $ myapp generate-bash-completions > /usr/share/bash-completion/completions/myapp.bash
248/// ```
249pub fn generate<G, S>(gen: G, cmd: &mut Command, bin_name: S, buf: &mut dyn Write)
250where
251 G: Generator,
252 S: Into<String>,
253{
254 cmd.set_bin_name(bin_name);
255 _generate::<G>(gen, cmd, buf)
256}
257
258fn _generate<G: Generator>(gen: G, cmd: &mut Command, buf: &mut dyn Write) {
259 cmd.build();
260 gen.generate(cmd, buf)
261}
262