mirror of
https://github.com/clap-rs/clap
synced 2025-03-04 15:27:16 +00:00
setting(PropagateGlobalValuesDown): adds a setting to allow automatically propagating global args values down through *used* subcommands
Closes #694
This commit is contained in:
parent
20842ed8c2
commit
985536c8eb
4 changed files with 126 additions and 35 deletions
|
@ -1004,6 +1004,12 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// [`io::stdout()`]: https://doc.rust-lang.org/std/io/fn.stdout.html
|
||||
/// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
|
||||
pub fn print_help(&mut self) -> ClapResult<()> {
|
||||
// If there are global arguments, or settings we need to propgate them down to subcommands
|
||||
// before parsing incase we run into a subcommand
|
||||
self.p.propogate_globals();
|
||||
self.p.propogate_settings();
|
||||
self.p.derive_display_order();
|
||||
|
||||
self.p.create_help_and_version();
|
||||
let out = io::stdout();
|
||||
let mut buf_w = BufWriter::new(out.lock());
|
||||
|
@ -1022,7 +1028,13 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
/// app.write_help(&mut out).expect("failed to write to stdout");
|
||||
/// ```
|
||||
/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
|
||||
pub fn write_help<W: Write>(&mut self, w: &mut W) -> ClapResult<()> {
|
||||
// If there are global arguments, or settings we need to propgate them down to subcommands
|
||||
// before parsing incase we run into a subcommand
|
||||
self.p.propogate_globals();
|
||||
self.p.propogate_settings();
|
||||
self.p.derive_display_order();
|
||||
|
||||
self.p.create_help_and_version();
|
||||
Help::write_app_help(w, self)
|
||||
}
|
||||
|
@ -1352,6 +1364,12 @@ impl<'a, 'b> App<'a, 'b> {
|
|||
return Err(e);
|
||||
}
|
||||
|
||||
if self.p.is_set(AppSettings::PropagateGlobalValuesDown) {
|
||||
for a in &self.p.global_args {
|
||||
matcher.propagate(a.name);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(matcher.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ pub struct Parser<'a, 'b>
|
|||
#[doc(hidden)]
|
||||
pub subcommands: Vec<App<'a, 'b>>,
|
||||
groups: HashMap<&'a str, ArgGroup<'a>>,
|
||||
global_args: Vec<Arg<'a, 'b>>,
|
||||
pub global_args: Vec<Arg<'a, 'b>>,
|
||||
overrides: Vec<&'b str>,
|
||||
help_short: Option<char>,
|
||||
version_short: Option<char>,
|
||||
|
|
|
@ -4,39 +4,40 @@ use std::str::FromStr;
|
|||
use std::ops::BitOr;
|
||||
|
||||
bitflags! {
|
||||
flags Flags: u32 {
|
||||
const SC_NEGATE_REQS = 0b00000000000000000000000000000001,
|
||||
const SC_REQUIRED = 0b00000000000000000000000000000010,
|
||||
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000000000000100,
|
||||
const GLOBAL_VERSION = 0b00000000000000000000000000001000,
|
||||
const VERSIONLESS_SC = 0b00000000000000000000000000010000,
|
||||
const UNIFIED_HELP = 0b00000000000000000000000000100000,
|
||||
const WAIT_ON_ERROR = 0b00000000000000000000000001000000,
|
||||
const SC_REQUIRED_ELSE_HELP= 0b00000000000000000000000010000000,
|
||||
const NEEDS_LONG_HELP = 0b00000000000000000000000100000000,
|
||||
const NEEDS_LONG_VERSION = 0b00000000000000000000001000000000,
|
||||
const NEEDS_SC_HELP = 0b00000000000000000000010000000000,
|
||||
const DISABLE_VERSION = 0b00000000000000000000100000000000,
|
||||
const HIDDEN = 0b00000000000000000001000000000000,
|
||||
const TRAILING_VARARG = 0b00000000000000000010000000000000,
|
||||
const NO_BIN_NAME = 0b00000000000000000100000000000000,
|
||||
const ALLOW_UNK_SC = 0b00000000000000001000000000000000,
|
||||
const UTF8_STRICT = 0b00000000000000010000000000000000,
|
||||
const UTF8_NONE = 0b00000000000000100000000000000000,
|
||||
const LEADING_HYPHEN = 0b00000000000001000000000000000000,
|
||||
const NO_POS_VALUES = 0b00000000000010000000000000000000,
|
||||
const NEXT_LINE_HELP = 0b00000000000100000000000000000000,
|
||||
const DERIVE_DISP_ORDER = 0b00000000001000000000000000000000,
|
||||
const COLORED_HELP = 0b00000000010000000000000000000000,
|
||||
const COLOR_ALWAYS = 0b00000000100000000000000000000000,
|
||||
const COLOR_AUTO = 0b00000001000000000000000000000000,
|
||||
const COLOR_NEVER = 0b00000010000000000000000000000000,
|
||||
const DONT_DELIM_TRAIL = 0b00000100000000000000000000000000,
|
||||
const ALLOW_NEG_NUMS = 0b00001000000000000000000000000000,
|
||||
const LOW_INDEX_MUL_POS = 0b00010000000000000000000000000000,
|
||||
const DISABLE_HELP_SC = 0b00100000000000000000000000000000,
|
||||
const DONT_COLLAPSE_ARGS = 0b01000000000000000000000000000000,
|
||||
const ARGS_NEGATE_SCS = 0b10000000000000000000000000000000,
|
||||
flags Flags: u64 {
|
||||
const SC_NEGATE_REQS = 0b000000000000000000000000000000001,
|
||||
const SC_REQUIRED = 0b000000000000000000000000000000010,
|
||||
const A_REQUIRED_ELSE_HELP = 0b000000000000000000000000000000100,
|
||||
const GLOBAL_VERSION = 0b000000000000000000000000000001000,
|
||||
const VERSIONLESS_SC = 0b000000000000000000000000000010000,
|
||||
const UNIFIED_HELP = 0b000000000000000000000000000100000,
|
||||
const WAIT_ON_ERROR = 0b000000000000000000000000001000000,
|
||||
const SC_REQUIRED_ELSE_HELP= 0b000000000000000000000000010000000,
|
||||
const NEEDS_LONG_HELP = 0b000000000000000000000000100000000,
|
||||
const NEEDS_LONG_VERSION = 0b000000000000000000000001000000000,
|
||||
const NEEDS_SC_HELP = 0b000000000000000000000010000000000,
|
||||
const DISABLE_VERSION = 0b000000000000000000000100000000000,
|
||||
const HIDDEN = 0b000000000000000000001000000000000,
|
||||
const TRAILING_VARARG = 0b000000000000000000010000000000000,
|
||||
const NO_BIN_NAME = 0b000000000000000000100000000000000,
|
||||
const ALLOW_UNK_SC = 0b000000000000000001000000000000000,
|
||||
const UTF8_STRICT = 0b000000000000000010000000000000000,
|
||||
const UTF8_NONE = 0b000000000000000100000000000000000,
|
||||
const LEADING_HYPHEN = 0b000000000000001000000000000000000,
|
||||
const NO_POS_VALUES = 0b000000000000010000000000000000000,
|
||||
const NEXT_LINE_HELP = 0b000000000000100000000000000000000,
|
||||
const DERIVE_DISP_ORDER = 0b000000000001000000000000000000000,
|
||||
const COLORED_HELP = 0b000000000010000000000000000000000,
|
||||
const COLOR_ALWAYS = 0b000000000100000000000000000000000,
|
||||
const COLOR_AUTO = 0b000000001000000000000000000000000,
|
||||
const COLOR_NEVER = 0b000000010000000000000000000000000,
|
||||
const DONT_DELIM_TRAIL = 0b000000100000000000000000000000000,
|
||||
const ALLOW_NEG_NUMS = 0b000001000000000000000000000000000,
|
||||
const LOW_INDEX_MUL_POS = 0b000010000000000000000000000000000,
|
||||
const DISABLE_HELP_SC = 0b000100000000000000000000000000000,
|
||||
const DONT_COLLAPSE_ARGS = 0b001000000000000000000000000000000,
|
||||
const ARGS_NEGATE_SCS = 0b010000000000000000000000000000000,
|
||||
const PROPAGATE_VALS_DOWN = 0b100000000000000000000000000000000,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +89,7 @@ impl AppFlags {
|
|||
NeedsLongVersion => NEEDS_LONG_VERSION,
|
||||
NeedsSubcommandHelp => NEEDS_SC_HELP,
|
||||
NoBinaryName => NO_BIN_NAME,
|
||||
PropagateGlobalValuesDown=> PROPAGATE_VALS_DOWN,
|
||||
StrictUtf8 => UTF8_STRICT,
|
||||
SubcommandsNegateReqs => SC_NEGATE_REQS,
|
||||
SubcommandRequired => SC_REQUIRED,
|
||||
|
@ -512,6 +514,44 @@ pub enum AppSettings {
|
|||
/// ```
|
||||
NextLineHelp,
|
||||
|
||||
/// Specifies that the parser should propagate global arg's values down through any *used* child
|
||||
/// subcommands. Meaning, if a subcommand wasn't used, the values won't be propagated down to
|
||||
/// said subcommand.
|
||||
///
|
||||
/// **NOTE:** Values are only propagated *down* through futher child commands, not up
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg, AppSettings, SubCommand};
|
||||
/// let m = App::new("myprog")
|
||||
/// .setting(AppSettings::PropagateGlobalValuesDown)
|
||||
/// .arg(Arg::from_usage("[cmd] 'command to run'")
|
||||
/// .global(true))
|
||||
/// .subcommand(SubCommand::with_name("foo"))
|
||||
/// .get_matches_from(vec!["myprog", "set", "foo"]);
|
||||
///
|
||||
/// assert_eq!(m.value_of("cmd"), Some("set"));
|
||||
///
|
||||
/// let sub_m = m.subcommand_matches("foo").unwrap();
|
||||
/// assert_eq!(sub_m.value_of("cmd"), Some("set"));
|
||||
/// ```
|
||||
/// Now doing the same thing, but *not* using any subcommands will result in the value not being
|
||||
/// propagated down.
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg, AppSettings};
|
||||
/// let m = App::new("myprog")
|
||||
/// .setting(AppSettings::PropagateGlobalValuesDown)
|
||||
/// .global_arg(Arg::from_usage("<cmd> 'command to run'"))
|
||||
/// .subcommand(SubCommand::with_name("foo"))
|
||||
/// .get_matches_from(vec!["myprog", "set"]);
|
||||
///
|
||||
/// assert_eq!(m.value_of("cmd"), Some("set"));
|
||||
///
|
||||
/// assert!(m.subcommand_matches("foo").is_none());
|
||||
/// ```
|
||||
PropagateGlobalValuesDown,
|
||||
|
||||
/// Allows [`SubCommand`]s to override all requirements of the parent command.
|
||||
/// For example if you had a subcommand or top level application with a required argument
|
||||
/// that is only required as long as there is no subcommand present,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
use std::collections::hash_map::{Entry, Iter};
|
||||
use std::ffi::OsStr;
|
||||
use std::ops::Deref;
|
||||
use std::mem;
|
||||
|
||||
// Third Party
|
||||
use vec_map::VecMap;
|
||||
|
@ -22,6 +23,38 @@ impl<'a> Default for ArgMatcher<'a> {
|
|||
impl<'a> ArgMatcher<'a> {
|
||||
pub fn new() -> Self { ArgMatcher::default() }
|
||||
|
||||
pub fn propagate(&mut self, arg: &'a str) {
|
||||
debugln!("ArgMatcher::propagate: arg={}", arg);
|
||||
let vals: VecMap<_> = if let Some(ma) = self.get(arg) {
|
||||
ma.vals.clone()
|
||||
} else {
|
||||
debugln!("ArgMatcher::propagate: arg wasn't used");
|
||||
return;
|
||||
};
|
||||
if let Some(ref mut sc) = self.0.subcommand {
|
||||
{
|
||||
let sma = (*sc).matches.args.entry(arg).or_insert_with(|| {
|
||||
let mut gma = MatchedArg::new();
|
||||
gma.occurs += 1;
|
||||
for (i, v) in &vals {
|
||||
gma.vals.insert(i, v.clone());
|
||||
}
|
||||
gma
|
||||
});
|
||||
if sma.vals.is_empty() {
|
||||
for (i, v) in &vals {
|
||||
sma.vals.insert(i, v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut am = ArgMatcher(mem::replace(&mut sc.matches, ArgMatches::new()));
|
||||
am.propagate(arg);
|
||||
mem::swap(&mut am.0, &mut sc.matches);
|
||||
} else {
|
||||
debugln!("ArgMatcher::propagate: Subcommand wasn't used");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> { self.0.args.get_mut(arg) }
|
||||
|
||||
pub fn get(&self, arg: &str) -> Option<&MatchedArg> { self.0.args.get(arg) }
|
||||
|
|
Loading…
Add table
Reference in a new issue