Skip to content

Commit 0e0f335

Browse files
committed
docs(Examples): adds subcommand examples
Closes #766
1 parent 4aec1a5 commit 0e0f335

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

examples/20_subcommands.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Working with subcommands is simple. There are a few key points to remember when working with
2+
// subcommands in clap. First, SubCommands are really just Apps. This means they can have their own
3+
// settings, version, authors, args, and even their own subcommands. The next thing to remember is
4+
// that subcommands are set up in a tree like heirachy.
5+
//
6+
// An ASCII art depiction may help explain this better. Using a fictional version of git as the demo
7+
// subject. Imagine the following are all subcommands of git (note, the author is aware these aren't
8+
// actually all subcommands in the real git interface, but it makes explanation easier)
9+
//
10+
// Top Level App (git) TOP
11+
// |
12+
// -----------------------------------------
13+
// / | \ \
14+
// clone push add commit LEVEL 1
15+
// | / \ / \ |
16+
// url origin remote ref name message LEVEL 2
17+
// / /\
18+
// path remote local LEVEL 3
19+
//
20+
// Given the above fictional subcommand hierarchy, valid runtime uses would be (not an all inclusive
21+
// list):
22+
//
23+
// $ git clone url
24+
// $ git push origin path
25+
// $ git add ref local
26+
// $ git commit message
27+
//
28+
// Notice only one command per "level" may be used. You could not, for example, do:
29+
//
30+
// $ git clone url push origin path
31+
//
32+
// It's also important to know that subcommands each have their own set of matches and may have args
33+
// with the same name as other subcommands in a different part of the tree heirachy (i.e. the arg
34+
// names aren't in a flat namespace).
35+
//
36+
// In order to use subcommands in clap, you only need to know which subcommand you're at in your
37+
// tree, and which args are defined on that subcommand.
38+
//
39+
// Let's make a quick program to illustrate. We'll be using the same example as above but for
40+
// brevity sake we won't implement all of the subcommands, only a few.
41+
42+
extern crate clap;
43+
44+
use clap::{App, Arg, SubCommand, AppSettings};
45+
46+
fn main() {
47+
48+
let matches = App::new("git")
49+
.about("A fictional versioning CLI")
50+
.version("1.0")
51+
.author("Me")
52+
.subcommand(SubCommand::with_name("clone")
53+
.about("clones repos")
54+
.arg(Arg::with_name("repo")
55+
.help("The repo to clone")
56+
.required(true)))
57+
.subcommand(SubCommand::with_name("push")
58+
.about("pushes things")
59+
.setting(AppSettings::SubcommandRequiredElseHelp)
60+
.subcommand(SubCommand::with_name("remote") // Subcommands can have thier own subcommands,
61+
// which in turn have their own subcommands
62+
.about("pushes remote things")
63+
.arg(Arg::with_name("repo")
64+
.required(true)
65+
.help("The remote repo to push things to")))
66+
.subcommand(SubCommand::with_name("local")
67+
.about("pushes local things")))
68+
.subcommand(SubCommand::with_name("add")
69+
.about("adds things")
70+
.author("Someone Else") // Subcommands can list different authors
71+
.version("v2.0 (I'm versioned differently") // or different version from their parents
72+
.setting(AppSettings::ArgRequiredElseHelp) // They can even have different settings
73+
.arg(Arg::with_name("stuff")
74+
.long("stuff")
75+
.help("Stuff to add")
76+
.takes_value(true)
77+
.multiple(true)))
78+
.get_matches();
79+
80+
// At this point, the matches we have point to git. Keep this in mind...
81+
82+
// You can check if one of git's subcommands was used
83+
if matches.is_present("clone") {
84+
println!("'git clone' was run.");
85+
}
86+
87+
// You can see which subcommand was used
88+
if let Some(subcommand) = matches.subcommand_name() {
89+
println!("'git {}' was used", subcommand);
90+
91+
// It's important to note, this *only* check's git's DIRECT children, **NOT** it's
92+
// grandchildren, great grandchildren, etc.
93+
//
94+
// i.e. if the command `git push remove --stuff foo` was run, the above will only print out,
95+
// `git push` was used. We'd need to get push's matches to see futher into the tree
96+
}
97+
98+
// An alternative to checking the name is matching on known names. Again notice that only the
99+
// direct children are matched here.
100+
match matches.subcommand_name() {
101+
Some("clone") => println!("'git clone' was used"),
102+
Some("push") => println!("'git push' was used"),
103+
Some("add") => println!("'git add' was used"),
104+
None => println!("No subcommand was used"),
105+
_ => unreachable!(), // Assuming you've listed all direct children above, this is unreachable
106+
}
107+
108+
// You could get the independent subcommand matches, although this is less common
109+
if let Some(clone_matches) = matches.subcommand_matches("clone") {
110+
// Now we have a reference to clone's matches
111+
println!("Cloning repo: {}", clone_matches.value_of("repo").unwrap());
112+
}
113+
114+
// The most common way to handle subcommands is via a combined approach using
115+
// `ArgMatches::subcommand` which returns a tuple of both the name and matches
116+
match matches.subcommand() {
117+
("clone", Some(clone_matches)) =>{
118+
// Now we have a reference to clone's matches
119+
println!("Cloning {}", clone_matches.value_of("repo").unwrap());
120+
},
121+
("push", Some(push_matches)) =>{
122+
// Now we have a reference to push's matches
123+
match push_matches.subcommand() {
124+
("remote", Some(remote_matches)) =>{
125+
// Now we have a reference to remote's matches
126+
println!("Pushing to {}", remote_matches.value_of("repo").unwrap());
127+
},
128+
("local", Some(local_matches)) =>{
129+
// Now we have a reference to push's matches
130+
println!("'git push local' was used");
131+
},
132+
_ => unreachable!(),
133+
}
134+
},
135+
("add", Some(add_matches)) =>{
136+
// Now we have a reference to add's matches
137+
println!("Adding {}", add_matches.values_of("stuff").unwrap().collect::<Vec<_>>().join(", "));
138+
},
139+
("", None) => println!("No subcommand was used"), // If no subcommand was usd it'll match the tuple ("", None)
140+
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!()
141+
}
142+
143+
// Continued program logic goes here...
144+
}
File renamed without changes.

0 commit comments

Comments
 (0)