forked from clap-rs/clap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
20_subcommands.rs
157 lines (147 loc) · 6.72 KB
/
20_subcommands.rs
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Working with subcommands is simple. There are a few key points to remember when working with
// subcommands in clap. First, s 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.
use clap::{App, AppSettings, Arg};
fn main() {
let matches = App::new("git")
.about("A fictional versioning CLI")
.version("1.0")
.author("Me")
.subcommand(
App::new("clone")
.about("clones repos")
.license("MIT OR Apache-2.0")
.arg(Arg::new("repo").about("The repo to clone").required(true)),
)
.subcommand(
App::new("push")
.about("pushes things")
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
App::new("remote") // Subcommands can have their own subcommands,
// which in turn have their own subcommands
.about("pushes remote things")
.arg(
Arg::new("repo")
.required(true)
.about("The remote repo to push things to"),
),
)
.subcommand(App::new("local").about("pushes local things")),
)
.subcommand(
App::new("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::new("stuff")
.long("stuff")
.about("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() {
Some(("clone", clone_matches)) => {
// Now we have a reference to clone's matches
println!("Cloning {}", clone_matches.value_of("repo").unwrap());
}
Some(("push", push_matches)) => {
// Now we have a reference to push's matches
match push_matches.subcommand() {
Some(("remote", remote_matches)) => {
// Now we have a reference to remote's matches
println!("Pushing to {}", remote_matches.value_of("repo").unwrap());
}
Some(("local", _)) => {
println!("'git push local' was used");
}
_ => unreachable!(),
}
}
Some(("add", 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 unreachabe!()
}
// Continued program logic goes here...
}