Skip to content

Commit ab4ec60

Browse files
committed
imp(args): allows consumer of clap to decide if empty values are allowed or not
Closes #122
1 parent 936332f commit ab4ec60

File tree

4 files changed

+82
-10
lines changed

4 files changed

+82
-10
lines changed

src/app.rs

+54-9
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
358358
min_vals: a.min_vals,
359359
max_vals: a.max_vals,
360360
help: a.help,
361+
empty_vals: a.empty_vals
361362
};
362363
if pb.min_vals.is_some() && !pb.multiple {
363364
panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} \
@@ -415,6 +416,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
415416
val_names: a.val_names,
416417
requires: None,
417418
required: a.required,
419+
empty_vals: a.empty_vals
418420
};
419421
if let Some(ref vec) = ob.val_names {
420422
ob.num_vals = Some(vec.len() as u8);
@@ -456,17 +458,18 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
456458
}
457459
self.opts.insert(a.name, ob);
458460
} else {
459-
if a.short.is_none() && a.long.is_none() {
460-
// Could be a posistional constructed from usage string
461-
461+
if !a.empty_vals {
462+
// Empty vals defaults to true, so if it's false it was manually set
463+
panic!("The argument '{}' cannot have empty_values() set because it is a flag. \
464+
Perhaps you mean't to set takes_value(true) as well?", a.name);
462465
}
463466
if a.required {
464-
panic!("Argument \"{}\" cannot be required(true) because it has no index() or \
467+
panic!("The argument '{}' cannot be required(true) because it has no index() or \
465468
takes_value(true)", a.name);
466469
}
467470
if a.possible_vals.is_some() {
468-
panic!("Argument \"{}\" cannot have a specific value set because it doesn't have \
469-
takes_value(true) set", a.name);
471+
panic!("The argument '{}' cannot have a specific value set because it doesn't \
472+
have takes_value(true) set", a.name);
470473
}
471474
// No need to check for index() or takes_value() as that is handled above
472475

@@ -1316,7 +1319,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
13161319
if let Some(ref ma) = matches.args.get(opt.name) {
13171320
if let Some(ref vals) = ma.values {
13181321
if num == vals.len() as u8 && !opt.multiple {
1319-
self.report_error(format!("The argument \"{}\" was found, \
1322+
self.report_error(format!("The argument '{}' was found, \
13201323
but '{}' only expects {} values",
13211324
arg,
13221325
opt,
@@ -1331,6 +1334,17 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
13311334
}
13321335
}
13331336
}
1337+
1338+
if !opt.empty_vals &&
1339+
matches.args.contains_key(opt.name) &&
1340+
arg.is_empty() {
1341+
self.report_error(format!("The argument '{}' does not allow empty \
1342+
values, but one was found.", opt),
1343+
true,
1344+
true,
1345+
Some(matches.args.keys()
1346+
.map(|k| *k).collect()));
1347+
}
13341348
if let Some(ref mut o) = matches.args.get_mut(opt.name) {
13351349
// Options have values, so we can unwrap()
13361350
if let Some(ref mut vals) = o.values {
@@ -1457,7 +1471,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
14571471
if let Some(ref ma) = matches.args.get(p.name) {
14581472
if let Some(ref vals) = ma.values {
14591473
if vals.len() as u8 == num {
1460-
self.report_error(format!("The argument \"{}\" was found, \
1474+
self.report_error(format!("The argument '{}' was found, \
14611475
but '{}' wasn't expecting any more values", arg, p),
14621476
true,
14631477
true,
@@ -1467,6 +1481,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
14671481
}
14681482
}
14691483
}
1484+
if !p.empty_vals && matches.args.contains_key(p.name) && arg.is_empty() {
1485+
self.report_error(format!("The argument '{}' does not allow empty \
1486+
values, but one was found.", p),
1487+
true,
1488+
true,
1489+
Some(matches.args.keys()
1490+
.map(|k| *k).collect()));
1491+
}
14701492
// Check if it's already existing and update if so...
14711493
if let Some(ref mut pos) = matches.args.get_mut(p.name) {
14721494
done = true;
@@ -1483,6 +1505,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
14831505
// Was an update made, or is this the first occurrence?
14841506
if !done {
14851507
let mut bm = BTreeMap::new();
1508+
if !p.empty_vals && arg.is_empty() {
1509+
self.report_error(format!("The argument '{}' does not allow empty \
1510+
values, but one was found.", p),
1511+
true,
1512+
true,
1513+
Some(matches.args.keys()
1514+
.map(|k| *k).collect()));
1515+
}
14861516
bm.insert(1, arg.clone());
14871517
matches.args.insert(p.name, MatchedArg{
14881518
occurrences: 1,
@@ -1742,6 +1772,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
17421772
}
17431773
}
17441774
if arg_val.is_some() {
1775+
if !v.empty_vals && arg.is_empty() && matches.args.contains_key(v.name) {
1776+
self.report_error(format!("The argument '{}' does not allow empty \
1777+
values, but one was found.", v),
1778+
true,
1779+
true,
1780+
Some(matches.args.keys()
1781+
.map(|k| *k).collect()));
1782+
}
17451783
if let Some(ref mut o) = matches.args.get_mut(v.name) {
17461784
o.occurrences += 1;
17471785
if let Some(ref mut vals) = o.values {
@@ -1751,6 +1789,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
17511789
}
17521790
}
17531791
} else {
1792+
if !v.empty_vals && arg_val.is_some() && arg_val.clone().unwrap().is_empty() {
1793+
self.report_error(format!("The argument '{}' does not allow empty \
1794+
values, but one was found.", v),
1795+
true,
1796+
true,
1797+
Some(matches.args.keys()
1798+
.map(|k| *k).collect()));
1799+
}
17541800
matches.args.insert(v.name, MatchedArg{
17551801
occurrences: if arg_val.is_some() { 1 } else { 0 },
17561802
values: if arg_val.is_some() {
@@ -1951,7 +1997,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
19511997
}
19521998
} else {
19531999
matches.args.insert(v.name, MatchedArg{
1954-
// name: v.name.to_owned(),
19552000
// occurrences will be incremented on getting a value
19562001
occurrences: 0,
19572002
values: Some(BTreeMap::new())

src/args/arg.rs

+25
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> {
8787
pub max_vals: Option<u8>,
8888
#[doc(hidden)]
8989
pub min_vals: Option<u8>,
90+
#[doc(hidden)]
91+
pub empty_vals: bool
9092
}
9193

9294
impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
@@ -129,6 +131,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
129131
val_names: None,
130132
max_vals: None,
131133
min_vals: None,
134+
empty_vals: true,
132135
}
133136
}
134137

@@ -168,6 +171,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
168171
max_vals: None,
169172
val_names: None,
170173
group: None,
174+
empty_vals: true
171175
}
172176
}
173177

@@ -324,6 +328,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
324328
max_vals: None,
325329
min_vals: None,
326330
group: None,
331+
empty_vals: true
327332
}
328333
}
329334

@@ -641,6 +646,26 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
641646
self
642647
}
643648

649+
/// Allows an argument to accept explicit empty values. An empty value must be specified at the
650+
/// command line with an explicit `""`, or `''`
651+
///
652+
/// **NOTE:** Defaults to `true` (Explicit empty values are allowed)
653+
///
654+
///
655+
/// # Example
656+
///
657+
/// ```no_run
658+
/// # use clap::{App, Arg};
659+
/// # let matches = App::new("myprog")
660+
/// # .arg(
661+
/// # Arg::with_name("debug")
662+
/// .empty_values(true)
663+
/// # ).get_matches();
664+
pub fn empty_values(mut self, ev: bool) -> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
665+
self.empty_vals = ev;
666+
self
667+
}
668+
644669
/// Specifies a list of possible values for this argument. At runtime, clap verifies that only
645670
/// one of the specified values was used, or fails with a usage string.
646671
///

src/args/argbuilder/option.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ pub struct OptBuilder<'n> {
2828
pub num_vals: Option<u8>,
2929
pub min_vals: Option<u8>,
3030
pub max_vals: Option<u8>,
31-
pub val_names: Option<Vec<&'n str>>
31+
pub val_names: Option<Vec<&'n str>>,
32+
pub empty_vals: bool
3233
}
3334

3435
impl<'n> Display for OptBuilder<'n> {

src/args/argbuilder/positional.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct PosBuilder<'n> {
2626
pub num_vals: Option<u8>,
2727
pub max_vals: Option<u8>,
2828
pub min_vals: Option<u8>,
29+
pub empty_vals: bool
2930
}
3031

3132
impl<'n> Display for PosBuilder<'n> {

0 commit comments

Comments
 (0)