Skip to content

Commit c0d70fe

Browse files
committed
feat: allows specifying AllowLeadingHyphen style values, but only for specific args vice command wide
One can now use `Arg::allow_hyphen_values(true)` which will enable `--opt -val` style values only for the specific arg and not command wide. Closes #742
1 parent cf9d6ce commit c0d70fe

File tree

10 files changed

+74
-109
lines changed

10 files changed

+74
-109
lines changed

justfile

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
run-test TEST:
1212
cargo test --test {{TEST}}
1313

14+
debug TEST:
15+
cargo test --test {{TEST}} --features debug
16+
1417
run-tests:
1518
cargo test --features "yaml unstable"
1619

src/app/macros.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,7 @@ macro_rules! parse_positional {
142142
$pos_counter == $_self.positionals.len()) {
143143
$_self.trailing_vals = true;
144144
}
145-
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
146-
return Err(e);
147-
}
145+
try!($_self.add_val_to_arg($p, &$arg_os, $matcher));
148146

149147
$matcher.inc_occurrence_of($p.b.name);
150148
let _ = $_self.groups_for_arg($p.b.name)
@@ -217,7 +215,7 @@ macro_rules! find_name_from {
217215
}};
218216
}
219217

220-
// Finds an option by name
218+
// Finds an arg by name
221219
macro_rules! find_by_name {
222220
($_self:ident, $name:expr, $what:ident, $how:ident) => {
223221
$_self.$what.$how().find(|o| &o.b.name == $name)

src/app/mod.rs

+28-81
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,13 @@ impl<'a, 'b> App<'a, 'b> {
7979
/// let prog = App::new("My Program")
8080
/// # ;
8181
/// ```
82-
pub fn new<S: Into<String>>(n: S) -> Self {
83-
App { p: Parser::with_name(n.into()) }
84-
}
82+
pub fn new<S: Into<String>>(n: S) -> Self { App { p: Parser::with_name(n.into()) } }
8583

8684
/// Get the name of the app
87-
pub fn get_name(&self) -> &str {
88-
&self.p.meta.name
89-
}
85+
pub fn get_name(&self) -> &str { &self.p.meta.name }
9086

9187
/// Get the name of the binary
92-
pub fn get_bin_name(&self) -> Option<&str> {
93-
self.p.meta.bin_name.as_ref().map(|s| s.as_str())
94-
}
88+
pub fn get_bin_name(&self) -> Option<&str> { self.p.meta.bin_name.as_ref().map(|s| s.as_str()) }
9589

9690
/// Creates a new instance of an application requiring a name, but uses the [`crate_authors!`]
9791
/// and [`crate_version!`] macros to fill in the [`App::author`] and [`App::version`] fields.
@@ -155,9 +149,7 @@ impl<'a, 'b> App<'a, 'b> {
155149
/// [`examples/17_yaml.yml`]: https://github.com/kbknapp/clap-rs/blob/master/examples/17_yaml.yml
156150
/// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html
157151
#[cfg(feature = "yaml")]
158-
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> {
159-
App::from(yaml)
160-
}
152+
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> { App::from(yaml) }
161153

162154
/// Sets a string of author(s) that will be displayed to the user when they
163155
/// request the help information with `--help` or `-h`.
@@ -1193,9 +1185,7 @@ impl<'a, 'b> App<'a, 'b> {
11931185
/// .get_matches();
11941186
/// ```
11951187
/// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html
1196-
pub fn get_matches(self) -> ArgMatches<'a> {
1197-
self.get_matches_from(&mut env::args_os())
1198-
}
1188+
pub fn get_matches(self) -> ArgMatches<'a> { self.get_matches_from(&mut env::args_os()) }
11991189

12001190
/// Starts the parsing process. This method will return a [`clap::Result`] type instead of exiting
12011191
/// the process on failed parse. By default this method gets matches from [`env::args_os`]
@@ -1508,78 +1498,37 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
15081498
}
15091499

15101500
impl<'a, 'b> Clone for App<'a, 'b> {
1511-
fn clone(&self) -> Self {
1512-
App { p: self.p.clone() }
1513-
}
1501+
fn clone(&self) -> Self { App { p: self.p.clone() } }
15141502
}
15151503

15161504
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
15171505
fn name(&self) -> &'n str {
15181506
unreachable!("App struct does not support AnyArg::name, this is a bug!")
15191507
}
1520-
fn kind(&self) -> ArgKind {
1521-
ArgKind::Subcmd
1522-
}
1523-
fn overrides(&self) -> Option<&[&'e str]> {
1524-
None
1525-
}
1526-
fn requires(&self) -> Option<&[&'e str]> {
1527-
None
1528-
}
1529-
fn blacklist(&self) -> Option<&[&'e str]> {
1530-
None
1531-
}
1532-
fn required_unless(&self) -> Option<&[&'e str]> {
1533-
None
1534-
}
1535-
fn val_names(&self) -> Option<&VecMap<&'e str>> {
1536-
None
1537-
}
1538-
fn is_set(&self, _: ArgSettings) -> bool {
1539-
false
1540-
}
1508+
fn id(&self) -> usize { self.p.id }
1509+
fn kind(&self) -> ArgKind { ArgKind::Subcmd }
1510+
fn overrides(&self) -> Option<&[&'e str]> { None }
1511+
fn requires(&self) -> Option<&[&'e str]> { None }
1512+
fn blacklist(&self) -> Option<&[&'e str]> { None }
1513+
fn required_unless(&self) -> Option<&[&'e str]> { None }
1514+
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
1515+
fn is_set(&self, _: ArgSettings) -> bool { false }
15411516
fn set(&mut self, _: ArgSettings) {
15421517
unreachable!("App struct does not support AnyArg::set, this is a bug!")
15431518
}
1544-
fn has_switch(&self) -> bool {
1545-
false
1546-
}
1547-
fn max_vals(&self) -> Option<u64> {
1548-
None
1549-
}
1550-
fn num_vals(&self) -> Option<u64> {
1551-
None
1552-
}
1553-
fn possible_vals(&self) -> Option<&[&'e str]> {
1554-
None
1555-
}
1556-
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
1557-
None
1558-
}
1559-
fn min_vals(&self) -> Option<u64> {
1560-
None
1561-
}
1562-
fn short(&self) -> Option<char> {
1563-
None
1564-
}
1565-
fn long(&self) -> Option<&'e str> {
1566-
None
1567-
}
1568-
fn val_delim(&self) -> Option<char> {
1569-
None
1570-
}
1571-
fn takes_value(&self) -> bool {
1572-
true
1573-
}
1574-
fn help(&self) -> Option<&'e str> {
1575-
self.p.meta.about
1576-
}
1577-
fn default_val(&self) -> Option<&'n str> {
1578-
None
1579-
}
1580-
fn longest_filter(&self) -> bool {
1581-
true
1582-
}
1519+
fn has_switch(&self) -> bool { false }
1520+
fn max_vals(&self) -> Option<u64> { None }
1521+
fn num_vals(&self) -> Option<u64> { None }
1522+
fn possible_vals(&self) -> Option<&[&'e str]> { None }
1523+
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
1524+
fn min_vals(&self) -> Option<u64> { None }
1525+
fn short(&self) -> Option<char> { None }
1526+
fn long(&self) -> Option<&'e str> { None }
1527+
fn val_delim(&self) -> Option<char> { None }
1528+
fn takes_value(&self) -> bool { true }
1529+
fn help(&self) -> Option<&'e str> { self.p.meta.about }
1530+
fn default_val(&self) -> Option<&'n str> { None }
1531+
fn longest_filter(&self) -> bool { true }
15831532
fn aliases(&self) -> Option<Vec<&'e str>> {
15841533
if let Some(ref aliases) = self.p.meta.aliases {
15851534
let vis_aliases: Vec<_> =
@@ -1596,7 +1545,5 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
15961545
}
15971546

15981547
impl<'n, 'e> fmt::Display for App<'n, 'e> {
1599-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1600-
write!(f, "{}", self.p.meta.name)
1601-
}
1548+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.p.meta.name) }
16021549
}

src/args/any_arg.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use args::ArgKind;
1212
#[doc(hidden)]
1313
pub trait AnyArg<'n, 'e>: std_fmt::Display {
1414
fn name(&self) -> &'n str;
15+
fn id(&self) -> usize;
1516
fn overrides(&self) -> Option<&[&'e str]>;
1617
fn aliases(&self) -> Option<Vec<&'e str>>;
1718
fn requires(&self) -> Option<&[&'e str]>;

src/args/arg.rs

+7
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,13 @@ impl<'a, 'b> Arg<'a, 'b> {
639639

640640
/// Allows values which start with a leading hyphen (`-`)
641641
///
642+
/// **WARNING**: When building your CLIs, consider the effects of allowing leading hyphens and
643+
/// the user passing in a value that matches a valid short. For example `prog -opt -F` where
644+
/// `-F` is supposed to be a value, yet `-F` is *also* a valid short for anther arg. Care should
645+
/// should be taken when designing these args. This is compounded by the ability to "stack"
646+
/// short args. I.e. if `-val` is supposed to be a value, but `-v`, `-a`, and `-l` are all valid
647+
/// shorts.
648+
///
642649
/// # Examples
643650
///
644651
/// ```rust

src/args/arg_builder/flag.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
4949

5050
impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
5151
fn name(&self) -> &'n str { self.b.name }
52+
fn id(&self) -> usize { self.b.id }
5253
fn kind(&self) -> ArgKind { ArgKind::Flag }
5354
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
5455
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
@@ -98,31 +99,31 @@ mod test {
9899
#[test]
99100
fn flagbuilder_display() {
100101
let mut f = FlagBuilder::new("flg");
101-
f.settings.set(ArgSettings::Multiple);
102-
f.long = Some("flag");
102+
f.b.settings.set(ArgSettings::Multiple);
103+
f.s.long = Some("flag");
103104

104105
assert_eq!(&*format!("{}", f), "--flag");
105106

106107
let mut f2 = FlagBuilder::new("flg");
107-
f2.short = Some('f');
108+
f2.s.short = Some('f');
108109

109110
assert_eq!(&*format!("{}", f2), "-f");
110111
}
111112

112113
#[test]
113114
fn flagbuilder_display_single_alias() {
114115
let mut f = FlagBuilder::new("flg");
115-
f.long = Some("flag");
116-
f.aliases = Some(vec![("als", true)]);
116+
f.s.long = Some("flag");
117+
f.s.aliases = Some(vec![("als", true)]);
117118

118119
assert_eq!(&*format!("{}", f), "--flag");
119120
}
120121

121122
#[test]
122123
fn flagbuilder_display_multiple_aliases() {
123124
let mut f = FlagBuilder::new("flg");
124-
f.short = Some('f');
125-
f.aliases =
125+
f.s.short = Some('f');
126+
f.s.aliases =
126127
Some(vec![("alias_not_visible", false), ("f2", true), ("f3", true), ("f4", true)]);
127128
assert_eq!(&*format!("{}", f), "-f");
128129
}

src/args/arg_builder/option.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
8888

8989
impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
9090
fn name(&self) -> &'n str { self.b.name }
91+
fn id(&self) -> usize { self.b.id }
9192
fn kind(&self) -> ArgKind { ArgKind::Opt }
9293
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
9394
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
@@ -140,8 +141,8 @@ mod test {
140141
#[test]
141142
fn optbuilder_display1() {
142143
let mut o = OptBuilder::new("opt");
143-
o.long = Some("option");
144-
o.settings.set(ArgSettings::Multiple);
144+
o.s.long = Some("option");
145+
o.b.settings.set(ArgSettings::Multiple);
145146

146147
assert_eq!(&*format!("{}", o), "--option <opt>...");
147148
}
@@ -153,8 +154,8 @@ mod test {
153154
v_names.insert(1, "name");
154155

155156
let mut o2 = OptBuilder::new("opt");
156-
o2.short = Some('o');
157-
o2.val_names = Some(v_names);
157+
o2.s.short = Some('o');
158+
o2.v.val_names = Some(v_names);
158159

159160
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
160161
}
@@ -166,27 +167,27 @@ mod test {
166167
v_names.insert(1, "name");
167168

168169
let mut o2 = OptBuilder::new("opt");
169-
o2.short = Some('o');
170-
o2.val_names = Some(v_names);
171-
o2.settings.set(ArgSettings::Multiple);
170+
o2.s.short = Some('o');
171+
o2.v.val_names = Some(v_names);
172+
o2.b.settings.set(ArgSettings::Multiple);
172173

173174
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
174175
}
175176

176177
#[test]
177178
fn optbuilder_display_single_alias() {
178179
let mut o = OptBuilder::new("opt");
179-
o.long = Some("option");
180-
o.aliases = Some(vec![("als", true)]);
180+
o.s.long = Some("option");
181+
o.s.aliases = Some(vec![("als", true)]);
181182

182183
assert_eq!(&*format!("{}", o), "--option <opt>");
183184
}
184185

185186
#[test]
186187
fn optbuilder_display_multiple_aliases() {
187188
let mut o = OptBuilder::new("opt");
188-
o.long = Some("option");
189-
o.aliases =
189+
o.s.long = Some("option");
190+
o.s.aliases =
190191
Some(vec![("als_not_visible", false), ("als2", true), ("als3", true), ("als4", true)]);
191192
assert_eq!(&*format!("{}", o), "--option <opt>");
192193
}

src/args/arg_builder/positional.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> {
9595

9696
impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
9797
fn name(&self) -> &'n str { self.b.name }
98+
fn id(&self) -> usize { self.b.id }
9899
fn kind(&self) -> ArgKind { ArgKind::Pos }
99100
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
100101
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
@@ -134,15 +135,15 @@ mod test {
134135
#[test]
135136
fn display_mult() {
136137
let mut p = PosBuilder::new("pos", 1);
137-
p.settings.set(ArgSettings::Multiple);
138+
p.b.settings.set(ArgSettings::Multiple);
138139

139140
assert_eq!(&*format!("{}", p), "<pos>...");
140141
}
141142

142143
#[test]
143144
fn display_required() {
144145
let mut p2 = PosBuilder::new("pos", 1);
145-
p2.settings.set(ArgSettings::Required);
146+
p2.b.settings.set(ArgSettings::Required);
146147

147148
assert_eq!(&*format!("{}", p2), "<pos>");
148149
}
@@ -153,19 +154,19 @@ mod test {
153154
let mut vm = VecMap::new();
154155
vm.insert(0, "file1");
155156
vm.insert(1, "file2");
156-
p2.val_names = Some(vm);
157+
p2.v.val_names = Some(vm);
157158

158159
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
159160
}
160161

161162
#[test]
162163
fn display_val_names_req() {
163164
let mut p2 = PosBuilder::new("pos", 1);
164-
p2.settings.set(ArgSettings::Required);
165+
p2.b.settings.set(ArgSettings::Required);
165166
let mut vm = VecMap::new();
166167
vm.insert(0, "file1");
167168
vm.insert(1, "file2");
168-
p2.val_names = Some(vm);
169+
p2.v.val_names = Some(vm);
169170

170171
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
171172
}

0 commit comments

Comments
 (0)