Skip to content

Commit fc3e0f5

Browse files
committed
feat(Settings): adds new setting to stop delimiting values with -- or TrailingVarArg
One can now use `AppSettings::DontDelimitTrailingValues` to stop clap's default behavior of delimiting values, even when `--` or `TrailingVarArg` is used. This is useful when passing other flags and commands through to another program. Closes #511
1 parent 7db45f7 commit fc3e0f5

File tree

3 files changed

+71
-44
lines changed

3 files changed

+71
-44
lines changed

src/app/macros.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -112,27 +112,26 @@ macro_rules! parse_positional {
112112
$_self:ident,
113113
$p:ident,
114114
$arg_os:ident,
115-
$pos_only:ident,
116115
$pos_counter:ident,
117116
$matcher:ident
118117
) => {
119118
debugln!("macro=parse_positional!;");
120119
validate_multiples!($_self, $p, $matcher);
121120

122-
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
123-
return Err(e);
124-
}
125-
if !$pos_only &&
121+
if !$_self.trailing_vals &&
126122
($_self.settings.is_set(AppSettings::TrailingVarArg) &&
127123
$pos_counter == $_self.positionals.len()) {
128-
$pos_only = true;
124+
$_self.trailing_vals = true;
125+
}
126+
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
127+
return Err(e);
129128
}
130129

131130
$matcher.inc_occurrence_of($p.name);
132131
let _ = $_self.groups_for_arg($p.name)
133132
.and_then(|vec| Some($matcher.inc_occurrences_of(&*vec)));
134133
arg_post_processing!($_self, $p, $matcher);
135-
// Only increment the positional counter if it doesn't allow multiples
134+
// Only increment the positional counter if it doesn't allow multiples
136135
if !$p.settings.is_set(ArgSettings::Multiple) {
137136
$pos_counter += 1;
138137
}

src/app/parser.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub struct Parser<'a, 'b>
5151
settings: AppFlags,
5252
pub g_settings: Vec<AppSettings>,
5353
pub meta: AppMeta<'b>,
54+
trailing_vals: bool,
5455
}
5556

5657
impl<'a, 'b> Default for Parser<'a, 'b> {
@@ -72,6 +73,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
7273
g_settings: vec![],
7374
settings: AppFlags::new(),
7475
meta: AppMeta::new(),
76+
trailing_vals: false,
7577
}
7678
}
7779
}
@@ -456,7 +458,6 @@ impl<'a, 'b> Parser<'a, 'b>
456458
// necessary
457459
self.create_help_and_version();
458460

459-
let mut pos_only = false;
460461
let mut subcmd_name: Option<String> = None;
461462
let mut needs_val_of: Option<&str> = None;
462463
let mut pos_counter = 1;
@@ -475,7 +476,7 @@ impl<'a, 'b> Parser<'a, 'b>
475476
};
476477

477478
// Has the user already passed '--'?
478-
if !pos_only {
479+
if !self.trailing_vals {
479480
// Does the arg match a subcommand name, or any of it's aliases (if defined)
480481
let pos_sc = self.subcommands
481482
.iter()
@@ -501,7 +502,7 @@ impl<'a, 'b> Parser<'a, 'b>
501502
if arg_os.len_() == 2 {
502503
// The user has passed '--' which means only positional args follow no
503504
// matter what they start with
504-
pos_only = true;
505+
self.trailing_vals = true;
505506
continue;
506507
}
507508

@@ -564,7 +565,7 @@ impl<'a, 'b> Parser<'a, 'b>
564565
}
565566

566567
if let Some(p) = self.positionals.get(pos_counter) {
567-
parse_positional!(self, p, arg_os, pos_only, pos_counter, matcher);
568+
parse_positional!(self, p, arg_os, pos_counter, matcher);
568569
} else {
569570
if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
570571
let mut sc_m = ArgMatcher::new();
@@ -1131,13 +1132,17 @@ impl<'a, 'b> Parser<'a, 'b>
11311132
{
11321133
debugln!("fn=add_val_to_arg;");
11331134
let mut ret = None;
1134-
if let Some(delim) = arg.val_delim() {
1135-
if val.is_empty_() {
1136-
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
1137-
} else {
1138-
for v in val.split(delim as u32 as u8) {
1139-
ret = try!(self.add_single_val_to_arg(arg, v, matcher));
1135+
if !(self.trailing_vals && self.is_set(AppSettings::DontDelimitTrailingValues)) {
1136+
if let Some(delim) = arg.val_delim() {
1137+
if val.is_empty_() {
1138+
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
1139+
} else {
1140+
for v in val.split(delim as u32 as u8) {
1141+
ret = try!(self.add_single_val_to_arg(arg, v, matcher));
1142+
}
11401143
}
1144+
} else {
1145+
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
11411146
}
11421147
} else {
11431148
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
@@ -1679,6 +1684,7 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
16791684
settings: self.settings.clone(),
16801685
g_settings: self.g_settings.clone(),
16811686
meta: self.meta.clone(),
1687+
trailing_vals: self.trailing_vals,
16821688
}
16831689
}
16841690
}

src/app/settings.rs

+49-27
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,33 @@ use std::ascii::AsciiExt;
33

44
bitflags! {
55
flags Flags: u32 {
6-
const SC_NEGATE_REQS = 0b00000000000000000000000001,
7-
const SC_REQUIRED = 0b00000000000000000000000010,
8-
const A_REQUIRED_ELSE_HELP = 0b00000000000000000000000100,
9-
const GLOBAL_VERSION = 0b00000000000000000000001000,
10-
const VERSIONLESS_SC = 0b00000000000000000000010000,
11-
const UNIFIED_HELP = 0b00000000000000000000100000,
12-
const WAIT_ON_ERROR = 0b00000000000000000001000000,
13-
const SC_REQUIRED_ELSE_HELP= 0b00000000000000000010000000,
14-
const NEEDS_LONG_HELP = 0b00000000000000000100000000,
15-
const NEEDS_LONG_VERSION = 0b00000000000000001000000000,
16-
const NEEDS_SC_HELP = 0b00000000000000010000000000,
17-
const DISABLE_VERSION = 0b00000000000000100000000000,
18-
const HIDDEN = 0b00000000000001000000000000,
19-
const TRAILING_VARARG = 0b00000000000010000000000000,
20-
const NO_BIN_NAME = 0b00000000000100000000000000,
21-
const ALLOW_UNK_SC = 0b00000000001000000000000000,
22-
const UTF8_STRICT = 0b00000000010000000000000000,
23-
const UTF8_NONE = 0b00000000100000000000000000,
24-
const LEADING_HYPHEN = 0b00000001000000000000000000,
25-
const NO_POS_VALUES = 0b00000010000000000000000000,
26-
const NEXT_LINE_HELP = 0b00000100000000000000000000,
27-
const DERIVE_DISP_ORDER = 0b00001000000000000000000000,
28-
const COLORED_HELP = 0b00010000000000000000000000,
29-
const COLOR_ALWAYS = 0b00100000000000000000000000,
30-
const COLOR_AUTO = 0b01000000000000000000000000,
31-
const COLOR_NEVER = 0b10000000000000000000000000,
6+
const SC_NEGATE_REQS = 0b000000000000000000000000001,
7+
const SC_REQUIRED = 0b000000000000000000000000010,
8+
const A_REQUIRED_ELSE_HELP = 0b000000000000000000000000100,
9+
const GLOBAL_VERSION = 0b000000000000000000000001000,
10+
const VERSIONLESS_SC = 0b000000000000000000000010000,
11+
const UNIFIED_HELP = 0b000000000000000000000100000,
12+
const WAIT_ON_ERROR = 0b000000000000000000001000000,
13+
const SC_REQUIRED_ELSE_HELP= 0b000000000000000000010000000,
14+
const NEEDS_LONG_HELP = 0b000000000000000000100000000,
15+
const NEEDS_LONG_VERSION = 0b000000000000000001000000000,
16+
const NEEDS_SC_HELP = 0b000000000000000010000000000,
17+
const DISABLE_VERSION = 0b000000000000000100000000000,
18+
const HIDDEN = 0b000000000000001000000000000,
19+
const TRAILING_VARARG = 0b000000000000010000000000000,
20+
const NO_BIN_NAME = 0b000000000000100000000000000,
21+
const ALLOW_UNK_SC = 0b000000000001000000000000000,
22+
const UTF8_STRICT = 0b000000000010000000000000000,
23+
const UTF8_NONE = 0b000000000100000000000000000,
24+
const LEADING_HYPHEN = 0b000000001000000000000000000,
25+
const NO_POS_VALUES = 0b000000010000000000000000000,
26+
const NEXT_LINE_HELP = 0b000000100000000000000000000,
27+
const DERIVE_DISP_ORDER = 0b000001000000000000000000000,
28+
const COLORED_HELP = 0b000010000000000000000000000,
29+
const COLOR_ALWAYS = 0b000100000000000000000000000,
30+
const COLOR_AUTO = 0b001000000000000000000000000,
31+
const COLOR_NEVER = 0b010000000000000000000000000,
32+
const DONT_DELIM_TRAIL = 0b100000000000000000000000000,
3233
}
3334
}
3435

@@ -79,7 +80,8 @@ impl AppFlags {
7980
DeriveDisplayOrder => DERIVE_DISP_ORDER,
8081
ColorAlways => COLOR_ALWAYS,
8182
ColorAuto => COLOR_AUTO,
82-
ColorNever => COLOR_NEVER
83+
ColorNever => COLOR_NEVER,
84+
DontDelimitTrailingValues => DONT_DELIM_TRAIL
8385
}
8486
}
8587

@@ -551,6 +553,24 @@ pub enum AppSettings {
551553
/// .get_matches();
552554
/// ```
553555
ColorNever,
556+
/// Disables the automatic delimiting of values when `--` or [`AppSettings::TrailingVarArg`]
557+
/// was used.
558+
///
559+
/// **NOTE:** The same thing can be done manually by setting the final positional argument to
560+
/// [`Arg::use_delimiter(false)`]. Using this setting is safer, because it's easier to locate
561+
/// when making changes.
562+
///
563+
/// # Examples
564+
///
565+
/// ```no_run
566+
/// # use clap::{App, Arg, SubCommand, AppSettings};
567+
/// App::new("myprog")
568+
/// .setting(AppSettings::DontDelimitTrailingValues)
569+
/// .get_matches();
570+
/// ```
571+
/// [`AppSettings::TrailingVarArg`]: ./enum.AppSettings.html#variant.TrailingVarArg
572+
/// [`Arg::use_delimiter(false)`]: ./struct.Arg.html#method.use_delimiter
573+
DontDelimitTrailingValues,
554574
#[doc(hidden)]
555575
NeedsLongVersion,
556576
#[doc(hidden)]
@@ -633,6 +653,8 @@ mod test {
633653
AppSettings::ColoredHelp);
634654
assert_eq!("hidden".parse::<AppSettings>().unwrap(),
635655
AppSettings::Hidden);
656+
assert_eq!("dontdelimittrailingvalues".parse::<AppSettings>().unwrap(),
657+
AppSettings::DontDelimitTrailingValues);
636658
assert!("hahahaha".parse::<AppSettings>().is_err());
637659
}
638660
}

0 commit comments

Comments
 (0)