clap_utilities/
completion_generator.rs

1use clap::{CommandFactory, Parser};
2use clap_complete::{generate, Shell};
3use std::{fs::File, io, path::PathBuf, process::ExitCode};
4use thiserror::Error;
5
6/// Generate completions.
7#[derive(Debug, Parser)]
8pub struct CompletionGenerator {
9    /// Bin name.
10    #[clap(long, short)]
11    name: String,
12    /// Target shell.
13    #[clap(long, short, value_enum)]
14    shell: Shell,
15    /// Output file.
16    #[clap(long, short)]
17    output: PathBuf,
18}
19
20/// Error of the generator.
21#[derive(Debug, Error)]
22pub enum Error {
23    /// Error caused by filesystem operation.
24    #[error("{}: {error}", path.to_string_lossy())]
25    FileSystem {
26        /// Path in question.
27        path: PathBuf,
28        /// Emitted error.
29        #[source]
30        error: io::Error,
31    },
32}
33
34impl CompletionGenerator {
35    /// Run the generator.
36    pub fn run<App: CommandFactory>(self) -> Result<(), Error> {
37        let CompletionGenerator {
38            name,
39            shell,
40            output,
41        } = self;
42        let mut cmd = App::command();
43        let mut output_file = File::create(&output).map_err(|error| Error::FileSystem {
44            path: output.clone(),
45            error,
46        })?;
47        generate(shell, &mut cmd, name, &mut output_file);
48        Ok(())
49    }
50
51    /// The program that generates shell completions.
52    pub fn main<App: CommandFactory>() -> ExitCode {
53        match CompletionGenerator::parse().run::<App>() {
54            Ok(()) => ExitCode::SUCCESS,
55            Err(error) => {
56                eprintln!("{error}");
57                ExitCode::FAILURE
58            }
59        }
60    }
61}