aboutsummaryrefslogtreecommitdiff
path: root/clap/examples/20_subcommands.rs
blob: f80f46d713501ac8ad002b3b1609bc0d42d7ffb3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Working with subcommands is simple. There are a few key points to remember when working with
// subcommands in clap. First, SubCommands are really just Apps. This means they can have their own
// settings, version, authors, args, and even their own subcommands. The next thing to remember is
// that subcommands are set up in a tree like hierarchy.
//
// An ASCII art depiction may help explain this better. Using a fictional version of git as the demo
// subject. Imagine the following are all subcommands of git (note, the author is aware these aren't
// actually all subcommands in the real git interface, but it makes explanation easier)
//
//            Top Level App (git)                         TOP
//                           |
//    -----------------------------------------
//   /             |                \          \
// clone          push              add       commit      LEVEL 1
//   |           /    \            /    \       |
//  url      origin   remote    ref    name   message     LEVEL 2
//           /                  /\
//        path            remote  local                   LEVEL 3
//
// Given the above fictional subcommand hierarchy, valid runtime uses would be (not an all inclusive
// list):
//
// $ git clone url
// $ git push origin path
// $ git add ref local
// $ git commit message
//
// Notice only one command per "level" may be used. You could not, for example, do:
//
// $ git clone url push origin path
//
// It's also important to know that subcommands each have their own set of matches and may have args
// with the same name as other subcommands in a different part of the tree hierarchy (i.e. the arg
// names aren't in a flat namespace).
//
// In order to use subcommands in clap, you only need to know which subcommand you're at in your
// tree, and which args are defined on that subcommand.
//
// Let's make a quick program to illustrate. We'll be using the same example as above but for
// brevity sake we won't implement all of the subcommands, only a few.

extern crate clap;

use clap::{App, Arg, SubCommand, AppSettings};

fn main() {

    let matches = App::new("git")
        .about("A fictional versioning CLI")
        .version("1.0")
        .author("Me")
        .subcommand(SubCommand::with_name("clone")
            .about("clones repos")
            .arg(Arg::with_name("repo")
                .help("The repo to clone")
                .required(true)))
        .subcommand(SubCommand::with_name("push")
            .about("pushes things")
            .setting(AppSettings::SubcommandRequiredElseHelp)
            .subcommand(SubCommand::with_name("remote")  // Subcommands can have their own subcommands,
                                                         // which in turn have their own subcommands
                .about("pushes remote things")
                .arg(Arg::with_name("repo")
                    .required(true)
                    .help("The remote repo to push things to")))
            .subcommand(SubCommand::with_name("local")
                .about("pushes local things")))
        .subcommand(SubCommand::with_name("add")
            .about("adds things")
            .author("Someone Else")                     // Subcommands can list different authors
            .version("v2.0 (I'm versioned differently") // or different version from their parents
            .setting(AppSettings::ArgRequiredElseHelp)  // They can even have different settings
            .arg(Arg::with_name("stuff")
                .long("stuff")
                .help("Stuff to add")
                .takes_value(true)
                .multiple(true)))
        .get_matches();

    // At this point, the matches we have point to git. Keep this in mind...

    // You can check if one of git's subcommands was used
    if matches.is_present("clone") {
        println!("'git clone' was run.");
    }

    // You can see which subcommand was used
    if let Some(subcommand) = matches.subcommand_name() {
        println!("'git {}' was used", subcommand);

        // It's important to note, this *only* check's git's DIRECT children, **NOT** it's
        // grandchildren, great grandchildren, etc.
        //
        // i.e. if the command `git push remove --stuff foo` was run, the above will only print out,
        // `git push` was used. We'd need to get push's matches to see further into the tree
    }

    // An alternative to checking the name is matching on known names. Again notice that only the
    // direct children are matched here.
    match matches.subcommand_name() {
        Some("clone") => println!("'git clone' was used"),
        Some("push") => println!("'git push' was used"),
        Some("add") => println!("'git add' was used"),
        None        => println!("No subcommand was used"),
        _           => unreachable!(), // Assuming you've listed all direct children above, this is unreachable
    }

    // You could get the independent subcommand matches, although this is less common
    if let Some(clone_matches) = matches.subcommand_matches("clone") {
        // Now we have a reference to clone's matches
        println!("Cloning repo: {}", clone_matches.value_of("repo").unwrap());
    }

    // The most common way to handle subcommands is via a combined approach using
    // `ArgMatches::subcommand` which returns a tuple of both the name and matches
    match matches.subcommand() {
        ("clone", Some(clone_matches)) =>{
            // Now we have a reference to clone's matches
            println!("Cloning {}", clone_matches.value_of("repo").unwrap());
        },
        ("push", Some(push_matches)) =>{
            // Now we have a reference to push's matches
            match push_matches.subcommand() {
                ("remote", Some(remote_matches)) =>{
                    // Now we have a reference to remote's matches
                    println!("Pushing to {}", remote_matches.value_of("repo").unwrap());
                },
                ("local", Some(_)) =>{
                    println!("'git push local' was used");
                },
                _            => unreachable!(),
            }
        },
        ("add", Some(add_matches)) =>{
            // Now we have a reference to add's matches
            println!("Adding {}", add_matches.values_of("stuff").unwrap().collect::<Vec<_>>().join(", "));
        },
        ("", None)   => println!("No subcommand was used"), // If no subcommand was used it'll match the tuple ("", None)
        _            => unreachable!(), // If all subcommands are defined above, anything else is unreachable!()
    }

    // Continued program logic goes here...
}