From 0e0f33547a6901425afc1d9fbe19f7ae3832d9a4 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 6 Dec 2016 16:54:38 -0500 Subject: [PATCH] docs(Examples): adds subcommand examples Closes #766 --- examples/20_subcommands.rs | 144 ++++++++++++++++++++++ examples/{20_aliases.rs => 21_aliases.rs} | 0 2 files changed, 144 insertions(+) create mode 100644 examples/20_subcommands.rs rename examples/{20_aliases.rs => 21_aliases.rs} (100%) diff --git a/examples/20_subcommands.rs b/examples/20_subcommands.rs new file mode 100644 index 00000000000..8afdf5ad173 --- /dev/null +++ b/examples/20_subcommands.rs @@ -0,0 +1,144 @@ +// 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, 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 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(local_matches)) =>{ + // Now we have a reference to push's matches + 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::>().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... +} diff --git a/examples/20_aliases.rs b/examples/21_aliases.rs similarity index 100% rename from examples/20_aliases.rs rename to examples/21_aliases.rs