Skip to content

Commit

Permalink
Auto merge of #551 - kbknapp:issues-546,549, r=kbknapp
Browse files Browse the repository at this point in the history
Issues 546,549
  • Loading branch information
homu committed Jun 30, 2016
2 parents 40017ed + e5ba93a commit 3a000d6
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 99 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
<a name="v2.8.0"></a>
## v2.8.0 (2016-06-30)


#### Features

* **Arg:** adds new setting `Arg::require_delimiter` which requires val delimiter to parse multiple values ([920b5595](https://github.com/kbknapp/clap-rs/commit/920b5595ed72abfb501ce054ab536067d8df2a66))

#### Bug Fixes

* Declare term::Winsize as repr(C) ([5d663d90](https://github.com/kbknapp/clap-rs/commit/5d663d905c9829ce6e7a164f1f0896cdd70236dd))

#### Documentation

* **Arg:** adds docs for ([49af4e38](https://github.com/kbknapp/clap-rs/commit/49af4e38a5dae2ab0a7fc3b4147e2c053d532484))



<a name="v2.7.1"></a>
### v2.7.1 (2016-06-29)

Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "clap"
version = "2.7.1"
version = "2.8.0"
authors = ["Kevin K. <[email protected]>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
Expand All @@ -20,6 +20,7 @@ strsim = { version = "~0.4.0", optional = true }
yaml-rust = { version = "~0.3.2", optional = true }
clippy = { version = "~0.0.74", optional = true }
unicode-width = { version = "~0.1.3", optional = true }
term_size = { version = "~0.1.0", optional = true }

[dev-dependencies]
regex = "~0.1.69"
Expand All @@ -29,7 +30,7 @@ default = ["suggestions", "color", "wrap_help"]
suggestions = ["strsim"]
color = ["ansi_term", "libc"]
yaml = ["yaml-rust"]
wrap_help = ["libc", "unicode-width"]
wrap_help = ["libc", "unicode-width", "term_size"]
lints = ["clippy", "nightly"]
nightly = [] # for building with nightly and unstable features
unstable = [] # for building with unstable features on stable Rust
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)

## What's New

Here's the highlights for v2.8.0

* **Arg:** adds new optional setting `Arg::require_delimiter` which requires val delimiter to parse multiple values
* The terminal sizing portion has been factored out into a separate crate, [term_size](https://crates.io/crates/term_size)
* Minor bug fixes


Here's the highlights for v2.7.1

* **Options:**
Expand Down
11 changes: 9 additions & 2 deletions src/app/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ use app::{App, AppSettings};
use app::parser::Parser;
use fmt::{Format, Colorizer};

use term;
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
use term_size;
#[cfg(any(not(feature = "wrap_help"), target_os = "windows"))]
mod term_size {
pub fn dimensions() -> Option<(usize, usize)> {
None
}
}

#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
use unicode_width::UnicodeWidthStr;
Expand Down Expand Up @@ -96,7 +103,7 @@ impl<'a> Help<'a> {
hide_pv: hide_pv,
term_w: match term_w {
Some(width) => width,
None => term::dimensions().map(|(w, _)| w).unwrap_or(120),
None => term_size::dimensions().map(|(w, _)| w).unwrap_or(120),
},
color: color,
cizer: cizer,
Expand Down
4 changes: 2 additions & 2 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1256,9 +1256,9 @@ impl<'a, 'b> Parser<'a, 'b>
ret = try!(self.add_single_val_to_arg(arg, v, matcher));
}
// If there was a delimiter used, we're not looking for more values
if val.contains_byte(delim as u32 as u8) {
if val.contains_byte(delim as u32 as u8) || arg.is_set(ArgSettings::RequireDelimiter) {
ret = None;
}
}
}
} else {
ret = try!(self.add_single_val_to_arg(arg, val, matcher));
Expand Down
82 changes: 82 additions & 0 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1919,6 +1919,88 @@ impl<'a, 'b> Arg<'a, 'b> {
}
}

/// Specifies that *multiple values* may only be set using the delimiter. This means if an
/// if an option is encountered, and no delimiter is found, it automatically assumed that no
/// additional values for that option follow. This is unlike the default, where it is generally
/// assumed that more values will follow regardless of whether or not a delimiter is used.
///
/// **NOTE:** The default is `false`.
///
/// **NOTE:** It's a good idea to inform the user that use of a delimiter is required, either
/// through help text or other means.
///
/// # Examples
///
/// These examples demonstrate what happens when `require_delimiter(true)` is used. Notice
/// everything works in this first example, as we use a delimiter, as expected.
///
/// ```rust
/// # use clap::{App, Arg};
/// let delims = App::new("reqdelims")
/// .arg(Arg::with_name("opt")
/// .short("o")
/// .takes_value(true)
/// .multiple(true)
/// .require_delimiter(true))
/// // Simulate "$ reqdelims -o val1,val2,val3"
/// .get_matches_from(vec![
/// "reqdelims", "-o", "val1,val2,val3",
/// ]);
///
/// assert!(delims.is_present("opt"));
/// assert_eq!(delims.values_of("opt").unwrap().collect::<Vec<_>>(), ["val1", "val2", "val3"]);
/// ```
/// In this next example, we will *not* use a delimiter. Notice it's now an error.
///
/// ```rust
/// # use clap::{App, Arg, ErrorKind};
/// let res = App::new("reqdelims")
/// .arg(Arg::with_name("opt")
/// .short("o")
/// .takes_value(true)
/// .multiple(true)
/// .require_delimiter(true))
/// // Simulate "$ reqdelims -o val1 val2 val3"
/// .get_matches_from_safe(vec![
/// "reqdelims", "-o", "val1", "val2", "val3",
/// ]);
///
/// assert!(res.is_err());
/// let err = res.unwrap_err();
/// assert_eq!(err.kind, ErrorKind::UnknownArgument);
/// ```
/// What's happening is `-o` is getting `val1`, and because delimiters are required yet none
/// were present, it stops parsing `-o`. At this point it reaches `val2` and because no
/// positional arguments have been defined, it's an error of an unexpected argument.
///
/// In this final example, we contrast the above with `clap`'s default behavior where the above
/// is *not* an error.
///
/// ```rust
/// # use clap::{App, Arg};
/// let delims = App::new("reqdelims")
/// .arg(Arg::with_name("opt")
/// .short("o")
/// .takes_value(true)
/// .multiple(true))
/// // Simulate "$ reqdelims -o val1 val2 val3"
/// .get_matches_from(vec![
/// "reqdelims", "-o", "val1", "val2", "val3",
/// ]);
///
/// assert!(delims.is_present("opt"));
/// assert_eq!(delims.values_of("opt").unwrap().collect::<Vec<_>>(), ["val1", "val2", "val3"]);
/// ```
pub fn require_delimiter(mut self, d: bool) -> Self {
if d {
self.setb(ArgSettings::UseValueDelimiter);
self.set(ArgSettings::RequireDelimiter)
} else {
self.unsetb(ArgSettings::UseValueDelimiter);
self.unset(ArgSettings::RequireDelimiter)
}
}

/// Specifies the separator to use when values are clumped together, defaults to `,` (comma).
///
/// **NOTE:** implicitly sets [`Arg::use_delimiter(true)`]
Expand Down
25 changes: 15 additions & 10 deletions src/args/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ use std::ascii::AsciiExt;

bitflags! {
flags Flags: u16 {
const REQUIRED = 0b000000001,
const MULTIPLE = 0b000000010,
const EMPTY_VALS = 0b000000100,
const GLOBAL = 0b000001000,
const HIDDEN = 0b000010000,
const TAKES_VAL = 0b000100000,
const USE_DELIM = 0b001000000,
const NEXT_LINE_HELP = 0b010000000,
const R_UNLESS_ALL = 0b100000000,
const REQUIRED = 0b0000000001,
const MULTIPLE = 0b0000000010,
const EMPTY_VALS = 0b0000000100,
const GLOBAL = 0b0000001000,
const HIDDEN = 0b0000010000,
const TAKES_VAL = 0b0000100000,
const USE_DELIM = 0b0001000000,
const NEXT_LINE_HELP = 0b0010000000,
const R_UNLESS_ALL = 0b0100000000,
const REQ_DELIM = 0b1000000000,
}
}

Expand All @@ -33,7 +34,8 @@ impl ArgFlags {
TakesValue => TAKES_VAL,
UseValueDelimiter => USE_DELIM,
NextLineHelp => NEXT_LINE_HELP,
RequiredUnlessAll => R_UNLESS_ALL
RequiredUnlessAll => R_UNLESS_ALL,
RequireDelimiter => REQ_DELIM
}
}

Expand Down Expand Up @@ -67,6 +69,8 @@ pub enum ArgSettings {
UseValueDelimiter,
/// Prints the help text on the line after the argument
NextLineHelp,
/// Requires the use of a value delimiter for all multiple values
RequireDelimiter,
#[doc(hidden)]
RequiredUnlessAll,
}
Expand All @@ -84,6 +88,7 @@ impl FromStr for ArgSettings {
"usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter),
"nextlinehelp" => Ok(ArgSettings::NextLineHelp),
"requiredunlessall" => Ok(ArgSettings::RequiredUnlessAll),
"requiredelimiter" => Ok(ArgSettings::RequireDelimiter),
_ => Err("unknown ArgSetting, cannot convert from str".to_owned()),
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ extern crate unicode_width;
#[macro_use]
extern crate bitflags;
extern crate vec_map;
#[cfg(feature = "wrap_help")]
extern crate term_size;

#[cfg(feature = "yaml")]
pub use yaml_rust::YamlLoader;
Expand All @@ -431,7 +433,6 @@ mod fmt;
mod suggestions;
mod errors;
mod osstringext;
mod term;
mod strext;

const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \
Expand Down
81 changes: 0 additions & 81 deletions src/term.rs

This file was deleted.

27 changes: 26 additions & 1 deletion tests/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern crate regex;

include!("../clap-test.rs");

use clap::{App, Arg};
use clap::{App, Arg, ErrorKind};

#[test]
fn stdin_char() {
Expand Down Expand Up @@ -200,6 +200,31 @@ fn multiple_vals_pos_arg_delim() {
assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn require_delims_no_delim() {
let r = App::new("mvae")
.arg( Arg::from_usage("-o [opt]... 'some opt'").require_delimiter(true) )
.arg( Arg::from_usage("[file] 'some file'") )
.get_matches_from_safe(vec!["mvae", "-o", "1", "2", "some"]);
assert!(r.is_err());
let err = r.unwrap_err();
assert_eq!(err.kind, ErrorKind::UnknownArgument);
}

#[test]
fn require_delims() {
let r = App::new("mvae")
.arg( Arg::from_usage("-o [opt]... 'some opt'").require_delimiter(true) )
.arg( Arg::from_usage("[file] 'some file'") )
.get_matches_from_safe(vec!["", "-o", "1,2", "some"]);
assert!(r.is_ok());
let m = r.unwrap();
assert!(m.is_present("o"));
assert_eq!(m.values_of("o").unwrap().collect::<Vec<_>>(), &["1", "2"]);
assert!(m.is_present("file"));
assert_eq!(m.value_of("file").unwrap(), "some");
}

#[test]
fn did_you_mean() {
test::check_err_output(test::complex_app(), "clap-test --optio=foo",
Expand Down

0 comments on commit 3a000d6

Please sign in to comment.