mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
156 lines
6.7 KiB
Rust
156 lines
6.7 KiB
Rust
// 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 heirachy.
|
|
//
|
|
// 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 heirachy (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, AppSettings, Arg, SubCommand};
|
|
|
|
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 thier 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 futher 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 usd it'll match the tuple ("", None)
|
|
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!()
|
|
}
|
|
|
|
// Continued program logic goes here...
|
|
}
|