clap_utilities/
completion_generator.rs

1use clap::{CommandFactory, Parser};
2use clap_complete::{generate, Shell};
3use derive_more::{Display, Error};
4use std::{
5    fs::File,
6    io,
7    path::{Path, PathBuf},
8    process::ExitCode,
9};
10
11/// Generate completions.
12#[derive(Debug, Parser)]
13pub struct CompletionGenerator {
14    /// Bin name.
15    #[clap(long, short)]
16    name: String,
17    /// Target shell.
18    #[clap(long, short, value_enum)]
19    shell: Shell,
20    /// Output file.
21    #[clap(long, short)]
22    output: PathBuf,
23}
24
25/// Error caused by failure to create a file.
26#[derive(Debug, Display, Error)]
27#[display("Failed to create file {}: {error}", path.to_string_lossy())]
28pub struct CreateFileError {
29    /// Path in question.
30    #[error(not(source))]
31    path: PathBuf,
32    /// Emitted error.
33    #[error(source)]
34    error: io::Error,
35}
36
37impl CreateFileError {
38    /// Create the error.
39    fn new(path: PathBuf, error: io::Error) -> Self {
40        Self { path, error }
41    }
42
43    /// Get the path that caused the error.
44    pub fn path(&self) -> &Path {
45        &self.path
46    }
47
48    /// Get the source of the error.
49    pub fn source(&self) -> &io::Error {
50        &self.error
51    }
52}
53
54/// Error of the generator.
55#[derive(Debug, Display, Error)]
56#[non_exhaustive]
57pub enum Error {
58    /// Failed to create a file.
59    CreateFile(CreateFileError),
60}
61
62impl CompletionGenerator {
63    /// Run the generator.
64    pub fn run<App: CommandFactory>(self) -> Result<(), Error> {
65        let CompletionGenerator {
66            name,
67            shell,
68            output,
69        } = self;
70        let mut cmd = App::command();
71        let mut output_file = match File::create(&output) {
72            Ok(output_file) => output_file,
73            Err(error) => return Err(Error::CreateFile(CreateFileError::new(output, error))),
74        };
75        generate(shell, &mut cmd, name, &mut output_file);
76        Ok(())
77    }
78
79    /// The program that generates shell completions.
80    pub fn main<App: CommandFactory>() -> ExitCode {
81        match CompletionGenerator::parse().run::<App>() {
82            Ok(()) => ExitCode::SUCCESS,
83            Err(error) => {
84                eprintln!("{error}");
85                ExitCode::FAILURE
86            }
87        }
88    }
89}