clap/examples/20_subcommands.rs
Ed Page 7a59dc3da4 Revert "Automatically read license field from Cargo.toml"
This reverts commit 6898fbde33.

PR #2144 added the `license` field but no consumer has been added since
the (like Issue #1768).  Since this is not ready yet, I am pulling it
from the 3.0 release.

So far, our main route for pulling a feature from the release has
been to put it behind a `unstable-*` feature flag and to create a
stablization tracking issue.  I chose to instead remove the feature
because a write-only field with no effect does not provide values for
people to use in as an early access and so doesn't outweight the cost of
the extra documentation noise and code noise it creates.  Additionally,
keeping an `unstable-` feature around when it has such an unknown path
(and time table) to stalbization feels like it violates YAGNI.  I'm
uncertain how much of this feature we can implement and not create a
legal trap for users because the crate's license is insufficient for the
final artifact's license.  I feel our stabliazation process sshould be
about iteration and collecting user feedback which this doesn't line up
with.

When someone is ready to tackle #1768, it will be easy to revert this
commit and pick up the work again.

Fixes #3001
2021-11-12 09:55:37 -06:00

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 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")
.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_occurrences(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...
}