Skip to content

Commit

Permalink
examples: FlagSubCommand pacman
Browse files Browse the repository at this point in the history
Using `pacman` as an example for `FlagSubCommand` because of clap-rs#1361
  • Loading branch information
NickHackman committed Jun 13, 2020
1 parent c89718f commit 5082971
Showing 1 changed file with 153 additions and 0 deletions.
153 changes: 153 additions & 0 deletions examples/23_flag_subcommands_pacman.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Working with flag subcommands allows behavior similar to the popular Archlinux package manager Pacman.
// Man page: https://jlk.fjfi.cvut.cz/arch/manpages/man/pacman.8
//
// It's suggested that you read examples/20_subcommands.rs prior to learning about `FlagSubCommand`s
//
// This differs from normal subcommands because it allows passing subcommand as `clap::Arg` in short or long args.
//
// Top Level App (pacman) TOP
// |
// ---------------------------------------------------
// / | | | | \ \
// sync database remove files query deptest upgrade LEVEL 1
//
// Given the above hierachy, valid runtime uses would be (not an all inclusive list):
//
// $ pacman -Ss
// ^--- subcommand followed by an arg in its scope.
//
// $ pacman -Qs
//
// $ pacman -Rns
//
// NOTE: Subcommands short flags don't have to be uppercase.
//
// $ pacman --sync --search
// ^--- subcommand
//
// $ pacman sync -s
// ^--- subcommand
//
// NOTE: this isn't valid for pacman, but `clap::FlagSubCommand`
// adds support for both flags and standard subcommands out of the box.
// Allowing your users to make the choice of what feels more intuitive for them.
//
// Notice only one command per "level" may be used. You could not, for example, do:
//
// $ pacman -SQR
//
// 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.
use clap::{App, AppSettings, Arg, FlagSubCommand};

fn main() {
let matches = App::new("pacman")
.about("package manager utility")
.version("5.2.1")
.setting(AppSettings::SubcommandRequiredElseHelp)
.author("Pacman Development Team")
// Query subcommand
//
// Only a few of its arguments are implemented below.
.subcommand(
// When getting the subcommand name the long version is used (in this case "query").
// If you want to use a different name then `FlagSubCommand::with_name("name", 's', "long")`.
//
// NOTE: if the name has been changed then it can be used as that:
//
// $ MyProg name
//
// $ MyProg --long
//
// $ MyProg -s
FlagSubCommand::new('Q', "query")
.about("Query the package database.")
.arg(
Arg::new("search")
.short('s')
.long("search")
.about("search locally-installed packages for matching strings")
.takes_value(true),
)
.arg(
Arg::new("info")
.long("info")
.short('i')
.about("view package information (-ii for backup files)")
.multiple(true),
),
)
// Sync subcommand
//
// Only a few of its arguments are implemented below.
.subcommand(
FlagSubCommand::new('S', "sync")
.about("Synchronize packages.")
.arg(
Arg::new("search")
.short('s')
.long("search")
.about("search remote repositories for matching strings")
.takes_value(true),
)
.arg(
Arg::new("info")
.long("info")
.short('i')
.about("view package information (-ii for extended information)")
.multiple(true),
)
.arg(
Arg::new("package")
.about("package")
.multiple(true)
.takes_value(true),
),
)
.get_matches();

match matches.subcommand() {
("sync", Some(sync_matches)) => {
if sync_matches.is_present("info") {
// Values required here, so it's safe to unwrap
let packages: Vec<_> = sync_matches.values_of("info").unwrap().collect();
println!("Retrieving info for {:?}...", packages);
} else if sync_matches.is_present("search") {
// Values required here, so it's safe to unwrap
let queries: Vec<_> = sync_matches.values_of("search").unwrap().collect();
println!("Searching for {:?}...", queries);
} else {
match sync_matches.values_of("package") {
Some(packages) => {
let pkgs: Vec<_> = packages.collect();
println!("Installing {:?}...", pkgs);
}
None => panic!("No targets specified (use -h for help)"),
}
}
}
("query", Some(query_matches)) => {
if query_matches.is_present("info") {
// Values required here, so it's safe to unwrap
let packages: Vec<_> = query_matches.values_of("info").unwrap().collect();
println!("Retrieving info for {:?}...", packages);
} else if query_matches.is_present("search") {
// Values required here, so it's safe to unwrap
let queries: Vec<_> = query_matches.values_of("search").unwrap().collect();
println!("Searching Locally for {:?}...", queries);
} else {
// Query was called without any arguments
println!("Displaying all locally installed packages...");
}
}
("", None) => panic!("error: no operation specified (use -h for help)"), // If no subcommand was used
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachable
}
}

0 comments on commit 5082971

Please sign in to comment.