04_04_custom_derive/
04_04_custom.rs

1use clap::error::ErrorKind;
2use clap::{CommandFactory, Parser};
3
4#[derive(Parser)]
5#[command(version, about, long_about = None)]
6struct Cli {
7    /// set version manually
8    #[arg(long, value_name = "VER")]
9    set_ver: Option<String>,
10
11    /// auto inc major
12    #[arg(long)]
13    major: bool,
14
15    /// auto inc minor
16    #[arg(long)]
17    minor: bool,
18
19    /// auto inc patch
20    #[arg(long)]
21    patch: bool,
22
23    /// some regular input
24    input_file: Option<String>,
25
26    /// some special input argument
27    #[arg(long)]
28    spec_in: Option<String>,
29
30    #[arg(short)]
31    config: Option<String>,
32}
33
34fn main() {
35    let cli = Cli::parse();
36
37    // Let's assume the old version 1.2.3
38    let mut major = 1;
39    let mut minor = 2;
40    let mut patch = 3;
41
42    // See if --set-ver was used to set the version manually
43    let version = if let Some(ver) = cli.set_ver.as_deref() {
44        if cli.major || cli.minor || cli.patch {
45            let mut cmd = Cli::command();
46            cmd.error(
47                ErrorKind::ArgumentConflict,
48                "Can't do relative and absolute version change",
49            )
50            .exit();
51        }
52        ver.to_string()
53    } else {
54        // Increment the one requested (in a real program, we'd reset the lower numbers)
55        let (maj, min, pat) = (cli.major, cli.minor, cli.patch);
56        match (maj, min, pat) {
57            (true, false, false) => major += 1,
58            (false, true, false) => minor += 1,
59            (false, false, true) => patch += 1,
60            _ => {
61                let mut cmd = Cli::command();
62                cmd.error(
63                    ErrorKind::ArgumentConflict,
64                    "Can only modify one version field",
65                )
66                .exit();
67            }
68        };
69        format!("{major}.{minor}.{patch}")
70    };
71
72    println!("Version: {version}");
73
74    // Check for usage of -c
75    if let Some(config) = cli.config.as_deref() {
76        let input = cli
77            .input_file
78            .as_deref()
79            // 'or' is preferred to 'or_else' here since `Option::as_deref` is 'const'
80            .or(cli.spec_in.as_deref())
81            .unwrap_or_else(|| {
82                let mut cmd = Cli::command();
83                cmd.error(
84                    ErrorKind::MissingRequiredArgument,
85                    "INPUT_FILE or --spec-in is required when using --config",
86                )
87                .exit()
88            });
89        println!("Doing work using input {input} and config {config}");
90    }
91}