Issues rollup (#637)

* feat: adds App::with_defaults to automatically use crate_authors! and crate_version! macros

One can now use

```rust
let a = App::with_defaults("My Program");

// same as
let a2 = App::new("My Program")
	.version(crate_version!())
	.author(crate_authors!());
```

Closes #600

* imp(YAML Errors): vastly improves error messages when using YAML

When errors are made while developing, the panic error messages have
been improved instead of relying on the default panic message which is
extremely unhelpful.

Closes #574

* imp(Completions): uses standard conventions for bash completion files, namely '{bin}.bash-completion'

Closes #567

* imp(Help): automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long

Now `clap` will check if it should automatically place long help
messages on the next line after the flag/option. This is determined by
checking to see if the space taken by flag/option plus spaces and values
doesn't leave enough room for the entirety of the help message, with the
single exception of of if the flag/option/spaces/values is less than 25%
of the width.

Closes #597

* tests: updates help tests to new forced new line rules

* fix(Groups): fixes some usage strings that contain both args in groups and ones that conflict with each other

Args that conflict *and* are in a group will now only display in the
group and not in the usage string itself.

Closes #616

* chore: updates dep graph

Closes #633

* chore: clippy run

* style: changes debug header to match other Rust projects

* chore: increase version
This commit is contained in:
Kevin K 2016-08-27 23:42:31 -04:00 committed by GitHub
parent 13fe03b033
commit b7793a2f4d
16 changed files with 298 additions and 210 deletions

View file

@ -1,3 +1,27 @@
<a name="v2.11.0"></a>
### v2.11.0 (2016-08-28)
#### Bug Fixes
* **Groups:** fixes some usage strings that contain both args in groups and ones that conflict with each other ([3d782def](https://github.com/kbknapp/clap-rs/commit/3d782def57725e2de26ca5a5bc5cc2e40ddebefb), closes [#616](https://github.com/kbknapp/clap-rs/issues/616))
#### Documentation
* moves docs to docs.rs ([03209d5e](https://github.com/kbknapp/clap-rs/commit/03209d5e1300906f00bafec1869c2047a92e5071), closes [#634](https://github.com/kbknapp/clap-rs/issues/634))
#### Improvements
* **Completions:** uses standard conventions for bash completion files, namely '{bin}.bash-completion' ([27f5bbfb](https://github.com/kbknapp/clap-rs/commit/27f5bbfbcc9474c2f57c2b92b1feb898ae46ee70), closes [#567](https://github.com/kbknapp/clap-rs/issues/567))
* **Help:** automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long ([150964c4](https://github.com/kbknapp/clap-rs/commit/150964c4e7124d54476c9d9b4b3f2406f0fd00e5), closes [#597](https://github.com/kbknapp/clap-rs/issues/597))
* **YAML Errors:** vastly improves error messages when using YAML ([f43b7c65](https://github.com/kbknapp/clap-rs/commit/f43b7c65941c53adc0616b8646a21dc255862eb2), closes [#574](https://github.com/kbknapp/clap-rs/issues/574))
#### Features
* adds App::with_defaults to automatically use crate_authors! and crate_version! macros ([5520bb01](https://github.com/kbknapp/clap-rs/commit/5520bb012c127dfd299fd55699443c744d8dcd5b), closes [#600](https://github.com/kbknapp/clap-rs/issues/600))
<a name="v2.10.4"></a>
### v2.10.4 (2016-08-25)

View file

@ -1,7 +1,7 @@
[package]
name = "clap"
version = "2.10.4"
version = "2.11.0"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
repository = "https://github.com/kbknapp/clap-rs.git"
@ -21,7 +21,7 @@ libc = { version = "~0.2.9", optional = true }
ansi_term = { version = "~0.8.0", optional = true }
strsim = { version = "~0.5.1", optional = true }
yaml-rust = { version = "~0.3.2", optional = true }
clippy = { version = "~0.0.79", optional = true }
clippy = { version = "~0.0.85", optional = true }
unicode-width = "~0.1.3"
unicode-segmentation = "~0.1.2"
term_size = { version = "~0.1.0", optional = true }

View file

@ -39,6 +39,17 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## What's New
Here's the highlights for v2.11.0
* Adds the ability to wrap help text intelligently on Windows!
* Moves docs to [docs.rs!](https://docs.rs/clap/)
* Fixes some usage strings that contain both args in groups and ones that conflict with each other
* Uses standard conventions for bash completion files, namely `{bin}.bash-completion`
* Automatically moves help text to the next line and wraps when term width is determined to be too small, or help text is too long
* Vastly improves *development* error messages when using YAML
* Adds `App::with_defaults` to automatically use `crate_authors!` and `crate_version!` macros
* Other minor improvements and bug fixes
Here's the highlights for v2.10.4
* Fixes a bug where help is wrapped incorrectly and causing a panic with some non-English characters

View file

@ -1,55 +1,41 @@
digraph dependencies {
N0[label="clap"];
N1[label="ansi_term",style=dashed];
N1[label="ansi_term"];
N2[label="bitflags"];
N3[label="clippy",color=blue,style=dashed];
N4[label="libc",style=dashed];
N5[label="strsim",style=dashed];
N6[label="unicode-width",style=dashed];
N7[label="vec_map"];
N8[label="yaml-rust",style=dashed,color=red];
N10[label="aho-corasick",color=blue,style=dashed];
N11[label="memchr",color=blue,style=dashed];
N12[label="regex",color=blue,style=dashed];
N13[label="quine-mc_cluskey",color=blue,style=dashed];
N14[label="regex-syntax",color=blue,style=dashed];
N15[label="semver",color=blue,style=dashed];
N16[label="toml",color=blue,style=dashed];
N17[label="unicode-normalization",color=blue,style=dashed];
N18[label="kernel32-sys",color=blue,style=dashed];
N19[label="winapi",color=blue,style=dashed];
N20[label="winapi-build",color=blue,style=dashed];
N21[label="nom",color=blue,style=dashed];
N22[label="thread_local",color=blue,style=dashed];
N23[label="utf8-ranges",color=blue,style=dashed];
N24[label="rustc-serialize",color=blue,style=dashed];
N25[label="thread-id"color=blue,style=dashed];
N3[label="clippy",color=blue];
N4[label="strsim"];
N5[label="term_size"];
N6[label="unicode-segmentation"];
N7[label="unicode-width"];
N8[label="vec_map"];
N9[label="yaml-rust",color=red];
N10[label="clippy_lints",color=blue];
N11[label="matches",color=blue];
N12[label="quine-mc_cluskey",color=blue];
N13[label="regex-syntax",color=red];
N14[label="rustc-serialize",color=blue];
N15[label="semver",color=blue];
N16[label="toml",color=blue];
N17[label="unicode-normalization",color=blue];
N0 -> N1[label="",style=dashed];
N0 -> N2[label=""];
N0 -> N3[label="",style=dashed,color=blue];
N0 -> N4[label="",style=dashed];
N0 -> N5[label="",style=dashed];
N0 -> N6[label="",style=dashed];
N0 -> N6[label=""];
N0 -> N7[label=""];
N0 -> N8[label="",style=dashed,color=red];
N0 -> N12[label="",style=dashed,color=blue];
N3 -> N13[label="",style=dashed,color=blue];
N3 -> N14[label="",style=dashed,color=blue];
N3 -> N15[label="",style=dashed,color=blue];
N3 -> N16[label="",style=dashed,color=blue];
N3 -> N17[label="",style=dashed,color=blue];
N0 -> N8[label=""];
N0 -> N9[label="",style=dashed,color=red];
N3 -> N10[label="",style=dashed,color=blue];
N5 -> N3[label="",style=dashed,color=blue];
N9 -> N3[label="",style=dashed,color=blue];
N9 -> N13[label="",style=dashed,color=red];
N10 -> N11[label="",style=dashed,color=blue];
N11 -> N4[label="",style=dashed,color=blue];
N12 -> N10[label="",style=dashed,color=blue];
N12 -> N11[label="",style=dashed,color=blue];
N12 -> N14[label="",style=dashed,color=blue];
N12 -> N22[label="",style=dashed,color=blue];
N12 -> N23[label="",style=dashed,color=blue];
N15 -> N21[label="",style=dashed,color=blue];
N16 -> N24[label="",style=dashed,color=blue];
N18 -> N19[label="",style=dashed,color=blue];
N18 -> N20[label="",style=dashed,color=blue];
N22 -> N25[label="",style=dashed,color=blue];
N25 -> N4[label="",style=dashed,color=blue];
N25 -> N18[label="",style=dashed,color=blue];
N10 -> N12[label="",style=dashed,color=blue];
N10 -> N13[label="",style=dashed,color=blue];
N10 -> N14[label="",style=dashed,color=blue];
N10 -> N15[label="",style=dashed,color=blue];
N10 -> N16[label="",style=dashed,color=blue];
N10 -> N17[label="",style=dashed,color=blue];
N16 -> N14[label="",style=dashed,color=blue];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View file

@ -1,5 +1,5 @@
name: yml_app
version: 1.0
version: "1.0"
about: an example using a .yml file to build a CLI
author: Kevin K. <kbknapp@gmail.com>
@ -69,7 +69,7 @@ subcommands:
# Rust code later
- subcmd:
about: demos subcommands from yaml
version: 0.1
version: "0.1"
author: Kevin K. <kbknapp@gmail.com>
# Subcommand args are exactly like App args
args:

View file

@ -339,8 +339,16 @@ impl<'a> Help<'a> {
} else {
try!(color!(self, "{}", arg, good));
}
let spec_vals = self.spec_vals(arg);
let h = arg.help().unwrap_or("");
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
let width = self.term_w;
let taken = (longest + 12) + str_width(&*spec_vals);
let force_next_line = !nlh && width >= taken && str_width(h) > (width - taken) && (taken as f32 / width as f32) > 0.25;
if arg.has_switch() {
if !(self.next_line_help || arg.is_set(ArgSettings::NextLineHelp)) {
if !(nlh || force_next_line) {
let self_len = arg.to_string().len();
// subtract ourself
let mut spcs = longest - self_len;
@ -356,7 +364,7 @@ impl<'a> Help<'a> {
write_nspaces!(self.writer, spcs);
}
} else if !(self.next_line_help || arg.is_set(ArgSettings::NextLineHelp)) {
} else if !(nlh || force_next_line) {
write_nspaces!(self.writer, longest + 4 - (arg.to_string().len()));
}
Ok(())
@ -416,19 +424,30 @@ impl<'a> Help<'a> {
let spec_vals = self.spec_vals(arg);
let mut help = String::new();
let h = arg.help().unwrap_or("");
let spcs = if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) {
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
debugln!("Next Line...{:?}", nlh);
// determine if our help fits or needs to wrap
let width = self.term_w;
debugln!("Term width...{}", width);
// We calculate with longest+12 since if it's already NLH we don't care
let taken = (longest + 12) + str_width(&*spec_vals);
let force_next_line = !nlh && width >= taken && str_width(h) > (width - taken) && (taken as f32 / width as f32) > 0.25;
debugln!("Force Next Line...{:?}", force_next_line);
debugln!("Force Next Line math (help_len > (width - flags/opts/spcs))...{} > ({} - {})", str_width(h), width, taken);
let spcs = if nlh || force_next_line {
8 // "tab" + "tab"
} else {
longest + 12
};
// determine if our help fits or needs to wrap
let width = self.term_w;
debugln!("Term width...{}", width);
let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= width;
debugln!("Spaces: {}", spcs);
// Is help on next line, if so newline + 2x tab
if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) {
if nlh || force_next_line {
try!(write!(self.writer, "\n{}{}", TAB, TAB));
}
@ -470,7 +489,7 @@ impl<'a> Help<'a> {
}
for part in help.split("{n}").skip(1) {
try!(write!(self.writer, "\n"));
if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) {
if nlh || force_next_line {
try!(write!(self.writer, "{}{}", TAB, TAB));
} else if arg.has_switch() {
write_nspaces!(self.writer, longest + 12);
@ -916,6 +935,7 @@ impl<'a> Help<'a> {
}
}
#[cfg_attr(feature = "lints", allow(explicit_counter_loop))]
fn wrap_help(help: &mut String, longest_w: usize, avail_chars: usize) {
debugln!("fn=wrap_help;longest_w={},avail_chars={}", longest_w, avail_chars);
debug!("Enough space to wrap...");

View file

@ -82,6 +82,27 @@ impl<'a, 'b> App<'a, 'b> {
App { p: Parser::with_name(n.into()) }
}
/// Creates a new instance of an application requiring a name, but uses the [`crate_authors!`]
/// and [`crate_version!`] macros to fill in the [`App::author`] and [`App::version`] fields.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// let prog = App::with_defaults("My Program")
/// # ;
/// ```
/// [`crate_authors!`]: ./macro.crate_authors!.html
/// [`crate_version!`]: ./macro.crate_version!.html
/// [`App::author`]: ./struct.App.html#method.author
/// [`App::version`]: ./struct.App.html#method.author
pub fn with_defaults<S: Into<String>>(n: S) -> Self {
let mut a = App { p: Parser::with_name(n.into()) };
a.p.meta.author = Some(crate_authors!());
a.p.meta.version = Some(crate_version!());
a
}
/// Creates a new instace of [`App`] from a .yml (YAML) file. A full example of supported YAML
/// objects can be found in [`examples/17_yaml.rs`] and [`examples/17_yaml.yml`]. One great use
/// for using YAML is when supporting multiple languages and dialects, as each language could
@ -1060,9 +1081,11 @@ impl<'a, 'b> App<'a, 'b> {
/// env!("OUT_DIR")); // Then say where write the completions to
/// }
/// ```
/// Now, once we combile there will be a `bash.sh` file in the directory. Assuming we compiled
/// with debug mode, it would be somewhere similar to
/// `<project>/target/debug/build/myapp-<hash>/out/myapp_bash.sh`
/// Now, once we combile there will be a `{bin_name}.bash-completion` file in the directory.
/// Assuming we compiled with debug mode, it would be somewhere similar to
/// `<project>/target/debug/build/myapp-<hash>/out/myapp.bash-completion`.
///
/// Fish shell completions will use the file format `{bin_name}.fish`
pub fn gen_completions<T: Into<OsString>, S: Into<String>>(&mut self, bin_name: S, for_shell: Shell, out_dir: T) {
self.p.meta.bin_name = Some(bin_name.into());
self.p.gen_completions(for_shell, out_dir.into());
@ -1324,54 +1347,57 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
} else {
yaml
};
if let Some(v) = yaml["version"].as_str() {
a = a.version(v);
}
if let Some(v) = yaml["author"].as_str() {
a = a.author(v);
}
if let Some(v) = yaml["bin_name"].as_str() {
a = a.bin_name(v);
}
if let Some(v) = yaml["about"].as_str() {
a = a.about(v);
}
if let Some(v) = yaml["before_help"].as_str() {
a = a.before_help(v);
}
if let Some(v) = yaml["template"].as_str() {
a = a.template(v);
}
if let Some(v) = yaml["after_help"].as_str() {
a = a.after_help(v);
macro_rules! yaml_str {
($a:ident, $y:ident, $i:ident) => {
if let Some(v) = $y[stringify!($i)].as_str() {
$a = $a.$i(v);
} else if $y[stringify!($i)] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to a string", $y[stringify!($i)]);
}
};
}
yaml_str!(a, yaml, version);
yaml_str!(a, yaml, bin_name);
yaml_str!(a, yaml, about);
yaml_str!(a, yaml, before_help);
yaml_str!(a, yaml, after_help);
yaml_str!(a, yaml, template);
yaml_str!(a, yaml, usage);
yaml_str!(a, yaml, help);
yaml_str!(a, yaml, help_short);
yaml_str!(a, yaml, version_short);
yaml_str!(a, yaml, alias);
yaml_str!(a, yaml, visible_alias);
if let Some(v) = yaml["display_order"].as_i64() {
a = a.display_order(v as usize);
}
if let Some(v) = yaml["usage"].as_str() {
a = a.usage(v);
}
if let Some(v) = yaml["help"].as_str() {
a = a.help(v);
}
if let Some(v) = yaml["help_short"].as_str() {
a = a.help_short(v);
}
if let Some(v) = yaml["version_short"].as_str() {
a = a.version_short(v);
} else if yaml["display_order"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to a u64", yaml["display_order"]);
}
if let Some(v) = yaml["setting"].as_str() {
a = a.setting(v.parse().ok().expect("unknown AppSetting found in YAML file"));
a = a.setting(v.parse().expect("unknown AppSetting found in YAML file"));
} else if yaml["setting"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to an AppSetting", yaml["setting"]);
}
if let Some(v) = yaml["settings"].as_vec() {
for ys in v {
if let Some(s) = ys.as_str() {
a = a.setting(s.parse().ok().expect("unknown AppSetting found in YAML file"));
a = a.setting(s.parse().expect("unknown AppSetting found in YAML file"));
}
}
} else {
if let Some(v) = yaml["settings"].as_str() {
a = a.setting(v.parse().expect("unknown AppSetting found in YAML file"));
} else if yaml["settings"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to a string", yaml["settings"]);
}
}
if let Some(v) = yaml["global_setting"].as_str() {
a = a.setting(v.parse().ok().expect("unknown AppSetting found in YAML file"));
} else if yaml["global_setting"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to an AppSetting", yaml["setting"]);
}
if let Some(v) = yaml["global_settings"].as_vec() {
for ys in v {
@ -1379,27 +1405,40 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
a = a.global_setting(s.parse().ok().expect("unknown AppSetting found in YAML file"));
}
}
}
if let Some(v) = yaml["alias"].as_str() {
a = a.alias(v);
}
if let Some(v) = yaml["aliases"].as_vec() {
for ys in v {
if let Some(s) = ys.as_str() {
a = a.alias(s);
}
} else {
if let Some(v) = yaml["global_settings"].as_str() {
a = a.global_setting(v.parse().expect("unknown AppSetting found in YAML file"));
} else if yaml["global_settings"] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to a string", yaml["global_settings"]);
}
}
if let Some(v) = yaml["visible_alias"].as_str() {
a = a.visible_alias(v);
}
if let Some(v) = yaml["visible_aliases"].as_vec() {
for ys in v {
if let Some(s) = ys.as_str() {
a = a.visible_alias(s);
macro_rules! vec_or_str {
($a:ident, $y:ident, $as_vec:ident, $as_single:ident) => {{
let maybe_vec = $y[stringify!($as_vec)].as_vec();
if let Some(vec) = maybe_vec {
for ys in vec {
if let Some(s) = ys.as_str() {
$a = $a.$as_single(s);
} else {
panic!("Failed to convert YAML value {:?} to a string", ys);
}
}
} else {
if let Some(s) = $y[stringify!($as_vec)].as_str() {
$a = $a.$as_single(s);
} else if $y[stringify!($as_vec)] != Yaml::BadValue {
panic!("Failed to convert YAML value {:?} to either a vec or string", $y[stringify!($as_vec)]);
}
}
$a
}
}
};
}
a = vec_or_str!(a, yaml, aliases, alias);
a = vec_or_str!(a, yaml, visible_aliases, visible_alias);
if let Some(v) = yaml["args"].as_vec() {
for arg_yaml in v {
a = a.arg(Arg::from_yaml(&arg_yaml.as_hash().unwrap()));

View file

@ -114,7 +114,7 @@ impl<'a, 'b> Parser<'a, 'b>
let out_dir = PathBuf::from(od);
let suffix = match for_shell {
Shell::Bash => "_bash.sh",
Shell::Bash => ".bash-completion",
Shell::Fish => ".fish",
};
@ -371,17 +371,17 @@ impl<'a, 'b> Parser<'a, 'b>
ret_val.push_back(s);
}
macro_rules! write_arg {
($i:expr, $m:ident, $v:ident, $r:ident) => {
($i:expr, $m:ident, $v:ident, $r:ident, $aig:ident) => {
for f in $v.into_iter() {
if $m.is_some() && $m.as_ref().unwrap().contains(f) {
if $m.is_some() && $m.as_ref().unwrap().contains(f) || $aig.contains(&f) {
continue;
}
$r.push_back($i.filter(|flg| &flg.name == &f).next().unwrap().to_string());
}
}
}
write_arg!(self.flags.iter(), matcher, c_flags, ret_val);
write_arg!(self.opts.iter(), matcher, c_opt, ret_val);
write_arg!(self.flags.iter(), matcher, c_flags, ret_val, args_in_groups);
write_arg!(self.opts.iter(), matcher, c_opt, ret_val, args_in_groups);
let mut g_vec = vec![];
for g in grps.into_iter() {
let g_string = self.args_in_group(g)

View file

@ -145,71 +145,52 @@ impl<'a, 'b> Arg<'a, 'b> {
let mut a = Arg::with_name(name_str);
let arg_settings = y.get(name_yml).unwrap().as_hash().unwrap();
macro_rules! vec_or_str {
($v:ident, $a:ident, $c:ident) => {{
let maybe_vec = $v.as_vec();
if let Some(vec) = maybe_vec {
for ys in vec {
if let Some(s) = ys.as_str() {
$a = $a.$c(s);
}
}
} else {
if let Some(s) = $v.as_str() {
$a = $a.$c(s);
}
}
$a
}
};
}
for (k, v) in arg_settings.iter() {
a = match k.as_str().unwrap() {
"short" => a.short(v.as_str().unwrap()),
"long" => a.long(v.as_str().unwrap()),
"help" => a.help(v.as_str().unwrap()),
"required" => a.required(v.as_bool().unwrap()),
"takes_value" => a.takes_value(v.as_bool().unwrap()),
"index" => a.index(v.as_i64().unwrap() as u64),
"global" => a.global(v.as_bool().unwrap()),
"multiple" => a.multiple(v.as_bool().unwrap()),
"hidden" => a.hidden(v.as_bool().unwrap()),
"next_line_help" => a.next_line_help(v.as_bool().unwrap()),
"empty_values" => a.empty_values(v.as_bool().unwrap()),
"group" => a.group(v.as_str().unwrap()),
"number_of_values" => a.number_of_values(v.as_i64().unwrap() as u64),
"max_values" => a.max_values(v.as_i64().unwrap() as u64),
"min_values" => a.min_values(v.as_i64().unwrap() as u64),
"value_name" => a.value_name(v.as_str().unwrap()),
"use_delimiter" => a.use_delimiter(v.as_bool().unwrap()),
"value_delimiter" => a.value_delimiter(v.as_str().unwrap()),
"required_unless" => a.required_unless(v.as_str().unwrap()),
"display_order" => a.display_order(v.as_i64().unwrap() as usize),
"default_value" => a.default_value(v.as_str().unwrap()),
"short" => yaml_to_str!(a, v, short),
"long" => yaml_to_str!(a, v, long),
"help" => yaml_to_str!(a, v, help),
"required" => yaml_to_bool!(a, v, required),
"takes_value" => yaml_to_bool!(a, v, takes_value),
"index" => yaml_to_u64!(a, v, index),
"global" => yaml_to_bool!(a, v, global),
"multiple" => yaml_to_bool!(a, v, multiple),
"hidden" => yaml_to_bool!(a, v, hidden),
"next_line_help" => yaml_to_bool!(a, v, next_line_help),
"empty_values" => yaml_to_bool!(a, v, empty_values),
"group" => yaml_to_str!(a, v, group),
"number_of_values" => yaml_to_u64!(a, v, number_of_values),
"max_values" => yaml_to_u64!(a, v, max_values),
"min_values" => yaml_to_u64!(a, v, min_values),
"value_name" => yaml_to_str!(a, v, value_name),
"use_delimiter" => yaml_to_bool!(a, v, use_delimiter),
"value_delimiter" => yaml_to_str!(a, v, value_delimiter),
"required_unless" => yaml_to_str!(a, v, required_unless),
"display_order" => yaml_to_usize!(a, v, display_order),
"default_value" => yaml_to_str!(a, v, default_value),
"value_names" => {
vec_or_str!(v, a, value_name)
yaml_vec_or_str!(v, a, value_name)
}
"groups" => {
vec_or_str!(v, a, group)
yaml_vec_or_str!(v, a, group)
}
"requires" => {
vec_or_str!(v, a, requires)
yaml_vec_or_str!(v, a, requires)
}
"conflicts_with" => {
vec_or_str!(v, a, conflicts_with)
yaml_vec_or_str!(v, a, conflicts_with)
}
"overrides_with" => {
vec_or_str!(v, a, overrides_with)
yaml_vec_or_str!(v, a, overrides_with)
}
"possible_values" => {
vec_or_str!(v, a, possible_value)
yaml_vec_or_str!(v, a, possible_value)
}
"required_unless_one" => {
vec_or_str!(v, a, required_unless)
yaml_vec_or_str!(v, a, required_unless)
}
"required_unless_all" => {
a = vec_or_str!(v, a, required_unless);
a = yaml_vec_or_str!(v, a, required_unless);
a.setb(ArgSettings::RequiredUnlessAll);
a
}

View file

@ -458,7 +458,7 @@ impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
let mut a = ArgGroup::default();
let group_settings = if b.len() == 1 {
let name_yml = b.keys().nth(0).expect("failed to get name");
let name_str = name_yml.as_str().expect("failed to convert name to str");
let name_str = name_yml.as_str().expect("failed to convert arg YAML name to str");
a.name = name_str;
b.get(name_yml)
.expect("failed to get name_str")
@ -472,12 +472,7 @@ impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
a = match k.as_str().unwrap() {
"required" => a.required(v.as_bool().unwrap()),
"args" => {
for ys in v.as_vec().unwrap() {
if let Some(s) = ys.as_str() {
a = a.arg(s);
}
}
a
yaml_vec_or_str!(v, a, arg)
}
"arg" => {
if let Some(ys) = v.as_str() {
@ -486,20 +481,10 @@ impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
a
}
"requires" => {
for ys in v.as_vec().unwrap() {
if let Some(s) = ys.as_str() {
a = a.requires(s);
}
}
a
yaml_vec_or_str!(v, a, requires)
}
"conflicts_with" => {
for ys in v.as_vec().unwrap() {
if let Some(s) = ys.as_str() {
a = a.conflicts_with(s);
}
}
a
yaml_vec_or_str!(v, a, conflicts_with)
}
"name" => {
if let Some(ys) = v.as_str() {

47
src/args/macros.rs Normal file
View file

@ -0,0 +1,47 @@
macro_rules! yaml_vec_or_str {
($v:ident, $a:ident, $c:ident) => {{
let maybe_vec = $v.as_vec();
if let Some(vec) = maybe_vec {
for ys in vec {
if let Some(s) = ys.as_str() {
$a = $a.$c(s);
} else {
panic!("Failed to convert YAML value {:?} to a string", ys);
}
}
} else {
if let Some(s) = $v.as_str() {
$a = $a.$c(s);
} else {
panic!("Failed to convert YAML value {:?} to either a vec or string", $v);
}
}
$a
}
};
}
macro_rules! yaml_to_str {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_str().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)))
}};
}
macro_rules! yaml_to_bool {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_bool().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)))
}};
}
macro_rules! yaml_to_u64 {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as u64)
}};
}
macro_rules! yaml_to_usize {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as usize)
}};
}

View file

@ -8,6 +8,8 @@ pub use self::group::ArgGroup;
pub use self::any_arg::{AnyArg, DispOrder};
pub use self::settings::ArgSettings;
#[macro_use]
mod macros;
mod arg;
pub mod any_arg;
mod arg_matches;

View file

@ -576,16 +576,16 @@ macro_rules! werr(
#[cfg_attr(feature = "debug", macro_use)]
mod debug_macros {
macro_rules! debugln {
($fmt:expr) => (println!(concat!("**DEBUG** ", $fmt)));
($fmt:expr, $($arg:tt)*) => (println!(concat!("**DEBUG** ",$fmt), $($arg)*));
($fmt:expr) => (println!(concat!("*DEBUG:clap: ", $fmt)));
($fmt:expr, $($arg:tt)*) => (println!(concat!("*DEBUG:clap: ",$fmt), $($arg)*));
}
macro_rules! sdebugln {
($fmt:expr) => (println!($fmt));
($fmt:expr, $($arg:tt)*) => (println!($fmt, $($arg)*));
}
macro_rules! debug {
($fmt:expr) => (print!(concat!("**DEBUG** ", $fmt)));
($fmt:expr, $($arg:tt)*) => (print!(concat!("**DEBUG** ",$fmt), $($arg)*));
($fmt:expr) => (print!(concat!("*DEBUG:clap: ", $fmt)));
($fmt:expr, $($arg:tt)*) => (print!(concat!("*DEBUG:clap: ",$fmt), $($arg)*));
}
macro_rules! sdebug {
($fmt:expr) => (print!($fmt));

View file

@ -1,5 +1,5 @@
name: claptests
version: 1.0
version: "1.0"
about: tests clap library
author: Kevin K. <kbknapp@gmail.com>
settings:
@ -81,7 +81,7 @@ arg_groups:
subcommands:
- subcmd:
about: tests subcommands
version: 0.1
version: "0.1"
author: Kevin K. <kbknapp@gmail.com>
args:
- scoption:

View file

@ -91,15 +91,13 @@ FLAGS:
-V, --version Prints version information
OPTIONS:
-c, --cafe <FILE> A coffeehouse, coffee shop, or café is an
establishment which primarily serves hot
coffee, related coffee beverages (e.g.,
café latte, cappuccino, espresso), tea,
and other hot beverages. Some
coffeehouses also serve cold beverages
such as iced coffee and iced tea. Many
cafés also serve some type of food, such
as light snacks, muffins, or pastries.";
-c, --cafe <FILE>
A coffeehouse, coffee shop, or café is an establishment which
primarily serves hot coffee, related coffee beverages
(e.g., café latte, cappuccino, espresso), tea, and other
hot beverages. Some coffeehouses also serve cold beverages
such as iced coffee and iced tea. Many cafés also serve
some type of food, such as light snacks, muffins, or pastries.";
static ISSUE_626_PANIC: &'static str = "ctest 0.1
@ -111,19 +109,14 @@ FLAGS:
-V, --version Prints version information
OPTIONS:
-c, --cafe <FILE> La culture du café est
très développée dans de
nombreux pays à climat
chaud d'Amérique,
d'Afrique et d'Asie,
dans des plantations
qui sont cultivées pour
les marchés
d'exportation. Le café
est souvent une
contribution majeure
aux exportations des
régions productrices.";
-c, --cafe <FILE>
La culture du café est très développée dans
de nombreux pays à climat chaud
d\'Amérique, d\'Afrique et d\'Asie, dans
des plantations qui sont cultivées pour
les marchés d\'exportation. Le café est
souvent une contribution majeure aux
exportations des régions productrices.";
#[test]
fn help_short() {