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