Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flag subcommands #1974

Merged
merged 31 commits into from
Jul 18, 2020
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c89718f
tests: FlagSubCommand
NickHackman Jun 13, 2020
5082971
examples: FlagSubCommand pacman
NickHackman Jun 13, 2020
0b179fe
api: FlagSubCommand, subcommand flags #1361
NickHackman Jun 13, 2020
a9091d1
chore: removed debug print
NickHackman Jun 13, 2020
458736b
imp: Improved Flag Subcommand API
NickHackman Jun 16, 2020
acb2f75
api: short_flag_aliases and corresponding methods
NickHackman Jun 16, 2020
383606e
tests: `short_flag_alias` methods
NickHackman Jun 16, 2020
02ebf4f
fix: display visible short flags, visible flags
NickHackman Jun 16, 2020
806d748
example: updated 23 docs & refractor
NickHackman Jun 17, 2020
bf3d947
fix: Flag Subcommands conflicts panic
NickHackman Jun 17, 2020
b09afcf
fix: Gate `fmt::Display` for Flag w/ debug_asserts
NickHackman Jun 17, 2020
f7c5b09
fix: duplicate flags in Flag Subcommand test
NickHackman Jun 17, 2020
32db427
fix: duplicate short flags in Flag Subcommand test
NickHackman Jun 17, 2020
d785ebc
fix: Flag Subcommands tests behind debug_asserts
NickHackman Jun 17, 2020
8a68f99
example: more pacman-like and refractor
NickHackman Jul 8, 2020
5db25e6
refractor: improved parser ergonomics
NickHackman Jul 8, 2020
59a14f8
fix: no longer cloning all Apps and Args for debug
NickHackman Jul 8, 2020
1ea7178
refractor: find_*_subcmd macro -> fn
NickHackman Jul 8, 2020
ec35ab8
feat: long flag subcommand infer
NickHackman Jul 8, 2020
1127ca6
feat: Usage displays short, long, and normal scs
NickHackman Jul 9, 2020
a3f8ddb
doc: removed '[``]' from documentation
NickHackman Jul 9, 2020
2744132
Merge branch 'master' into flag-subcommands
NickHackman Jul 10, 2020
0a266f8
fix: merge related issues
NickHackman Jul 10, 2020
f5aabfa
style: rename short, long -> short_flag, long_flag
NickHackman Jul 10, 2020
5118cec
fix: long_flag_aliases instead of using alias
NickHackman Jul 10, 2020
cf9265d
fix: failing doc test
NickHackman Jul 10, 2020
432be5b
tests: conflicts with flag sc and arg aliases
NickHackman Jul 11, 2020
6ddf940
fix: conflicts between flag scs and args alias
NickHackman Jul 11, 2020
0ddd58c
fix: clippy lint warning
NickHackman Jul 11, 2020
2143639
docs: improved flag subcommand documentation
NickHackman Jul 16, 2020
6c6b9db
fix: clippy lint warning - name change
NickHackman Jul 16, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions examples/23_flag_subcommands_pacman.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// 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 subcommands in the same fashion 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 can be uppercase or lowercase.
//
// $ pacman --sync --search
// ^--- subcommand
//
// $ pacman sync -s
// ^--- subcommand
//
// NOTE: this isn't valid for pacman, but is done implicitly by Clap which
// 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
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
//
// 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};

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(
App::new("query")
.short_flag('Q')
.long_flag("query")
.about("Query the package database.")
.arg(
Arg::new("search")
.short('s')
.long("search")
.about("search locally-installed packages for matching strings")
.multiple(true)
.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(
App::new("sync")
.short_flag('S')
.long_flag("sync")
.about("Synchronize packages.")
.arg(
Arg::new("search")
.short('s')
.long("search")
.about("search remote repositories for matching strings")
.multiple(true)
.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();
let comma_sep = packages.join(", ");
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
println!("Retrieving info for {}...", comma_sep);
} 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();
let comma_sep = queries.join(", ");
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
println!("Searching for {}...", comma_sep);
} else {
// Sync was called without any arguments
match sync_matches.values_of("package") {
Some(packages) => {
let pkgs: Vec<_> = packages.collect();
let comma_sep = pkgs.join(", ");
println!("Installing {}...", comma_sep);
}
None => panic!("No targets specified (use -h for help)"),
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
("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();
let comma_sep = packages.join(", ");
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
println!("Retrieving info for {}...", comma_sep);
} 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();
let comma_sep = queries.join(", ");
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
println!("Searching Locally for {}...", comma_sep);
} 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
NickHackman marked this conversation as resolved.
Show resolved Hide resolved
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachable
}
}
Loading