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