Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
3 contributors

Users who have contributed to this file

@mgeisler @kbknapp @waywardmonkeys
144 lines (131 sloc) 6.51 KB
// 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...
}
You can’t perform that action at this time.