Skip to content

Commit

Permalink
Auto merge of #493 - kbknapp:v2.4.0, r=kbknapp
Browse files Browse the repository at this point in the history
V2.4.0
  • Loading branch information
homu committed May 3, 2016
2 parents 84381b5 + 5b01200 commit bad46d3
Show file tree
Hide file tree
Showing 15 changed files with 479 additions and 22 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
<a name="v2.4.0"></a>
## v2.4.0 (2016-05-02)


#### Features

* **Help:** adds support for displaying info before help message ([29fbfa3b](https://github.com/kbknapp/clap-rs/commit/29fbfa3b963f2f3ca7704bf5d3e1201531baa373))
* **Required:** adds allowing args that are required unless certain args are present ([af1f7916](https://github.com/kbknapp/clap-rs/commit/af1f79168390ea7da4074d0d9777de458ea64971))

#### Documentation

* hides formatting from docs ([cb708093](https://github.com/kbknapp/clap-rs/commit/cb708093a7cd057f08c98b7bd1ed54c2db86ae7e))
* **required_unless:** adds docs and examples for required_unless ([ca727b52](https://github.com/kbknapp/clap-rs/commit/ca727b52423b9883acd88b2f227b2711bc144573))

#### Bug Fixes

* **Required Args:** fixes issue where missing required args are sometimes duplicatd in error messages ([3beebd81](https://github.com/kbknapp/clap-rs/commit/3beebd81e7bc2faa4115ac109cf570e512c5477f), closes [#492](https://github.com/kbknapp/clap-rs/issues/492))


<a name="v2.3.0"></a>
## v2.3.0 (2016-04-18)

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

name = "clap"
version = "2.3.0"
version = "2.4.0"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "*.png", "clap-perf/*"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)

## What's New

Here's the highlights from v2.4.0

* **Before Help:** adds support for displaying info before help message
* **Required Unless:** adds support for allowing args that are required unless certain other args are present
* Bug fixes

Here's the highlights from v2.3.0

* **New Help Template Engine!**: Now you have full control over the layout of your help message. Major thanks to @hgrecco
Expand Down
10 changes: 7 additions & 3 deletions src/app/help.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@

use std::io::{self, Cursor, Read, Write};
use std::collections::BTreeMap;
use std::fmt::Display;
use std::cmp;
use std::str;

use vec_map::VecMap;

Expand Down Expand Up @@ -694,7 +692,8 @@ impl<'a> Help<'a> {
/// * `{options}` - Help for options.
/// * `{positionals}` - Help for positionals arguments.
/// * `{subcommands}` - Help for subcommands.
/// * `{after-help}` - Help for flags.
/// * `{after-help}` - Info to be displayed after the help message.
/// * `{before-help}` - Info to be displayed before the help message.
///
/// The template system is, on purpose, very simple. Therefore the tags have to writen
/// in the lowercase and without spacing.
Expand Down Expand Up @@ -770,6 +769,11 @@ impl<'a> Help<'a> {
"{}",
parser.meta.more_help.unwrap_or("unknown after-help")));
}
b"before-help" => {
try!(write!(self.writer,
"{}",
parser.meta.pre_help.unwrap_or("unknown before-help")));
}
// Unknown tag, write it back.
ref r => {
try!(self.writer.write(b"{"));
Expand Down
3 changes: 3 additions & 0 deletions src/app/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub struct AppMeta<'b> {
pub version: Option<&'b str>,
pub about: Option<&'b str>,
pub more_help: Option<&'b str>,
pub pre_help: Option<&'b str>,
pub usage_str: Option<&'b str>,
pub usage: Option<String>,
pub help_str: Option<&'b str>,
Expand All @@ -21,6 +22,7 @@ impl<'b> Default for AppMeta<'b> {
author: None,
about: None,
more_help: None,
pre_help: None,
version: None,
usage_str: None,
usage: None,
Expand Down Expand Up @@ -49,6 +51,7 @@ impl<'b> Clone for AppMeta<'b> {
author: self.author,
about: self.about,
more_help: self.more_help,
pre_help: self.pre_help,
version: self.version,
usage_str: self.usage_str,
usage: self.usage.clone(),
Expand Down
18 changes: 18 additions & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@ impl<'a, 'b> App<'a, 'b> {
self
}

/// Adds additional help information to be displayed in addition to auto-generated help. This
/// information is displayed **before** the auto-generated help information. This is often used
/// for header information.
///
/// # Examples
///
/// ```no_run
/// # use clap::App;
/// App::new("myprog")
/// .before_help("Some info I'd like to appear before the help info")
/// # ;
/// ```
pub fn before_help<S: Into<&'b str>>(mut self, help: S) -> Self {
self.p.meta.pre_help = Some(help.into());
self
}

/// Sets a string of the version number to be displayed when displaying version or help
/// information.
///
Expand Down Expand Up @@ -936,6 +953,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
fn overrides(&self) -> Option<&[&'e str]> { None }
fn requires(&self) -> Option<&[&'e str]> { None }
fn blacklist(&self) -> Option<&[&'e str]> { None }
fn required_unless(&self) -> Option<&[&'e str]> { None }
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
fn is_set(&self, _: ArgSettings) -> bool { false }
fn set(&mut self, _: ArgSettings) {
Expand Down
44 changes: 36 additions & 8 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,9 +850,11 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> {
debug!("Checking if --{} is help or version...", arg.to_str().unwrap());
if arg == "help" && self.settings.is_set(AppSettings::NeedsLongHelp) {
sdebugln!("Help");
try!(self._help());
}
if arg == "version" && self.settings.is_set(AppSettings::NeedsLongVersion) {
sdebugln!("Version");
try!(self._version());
}
sdebugln!("Neither");
Expand All @@ -862,8 +864,14 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {

fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> {
debug!("Checking if -{} is help or version...", arg);
if let Some(h) = self.help_short { if arg == h { try!(self._help()); } }
if let Some(v) = self.version_short { if arg == v { try!(self._version()); } }
if let Some(h) = self.help_short {
sdebugln!("Help");
if arg == h { try!(self._help()); }
}
if let Some(v) = self.version_short {
sdebugln!("Help");
if arg == v { try!(self._version()); }
}
sdebugln!("Neither");
Ok(())
}
Expand Down Expand Up @@ -1245,17 +1253,19 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
continue 'outer;
}
if let Some(a) = self.flags.iter().filter(|f| &f.name == name).next() {
if self._validate_blacklist_required(a, matcher) { continue 'outer; }
if self.is_missing_required_ok(a, matcher) { continue 'outer; }
} else if let Some(a) = self.opts.iter().filter(|o| &o.name == name).next() {
if self._validate_blacklist_required(a, matcher) { continue 'outer; }
if self.is_missing_required_ok(a, matcher) { continue 'outer; }
} else if let Some(a) = self.positionals.values().filter(|p| &p.name == name).next() {
if self._validate_blacklist_required(a, matcher) { continue 'outer; }
if self.is_missing_required_ok(a, matcher) { continue 'outer; }
}
let err = if self.settings.is_set(AppSettings::ArgRequiredElseHelp) && matcher.is_empty() {
self._help().unwrap_err()
} else {
let mut reqs = self.required.iter().map(|&r| &*r).collect::<Vec<_>>();
reqs.dedup();
Error::missing_required_argument(
&*self.get_required_from(&*self.required.iter().map(|&r| &*r).collect::<Vec<_>>(), Some(matcher))
&*self.get_required_from(&*reqs, Some(matcher))
.iter()
.fold(String::new(),
|acc, s| acc + &format!("\n {}", Format::Error(s))[..]),
Expand All @@ -1266,7 +1276,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
Ok(())
}

fn _validate_blacklist_required<A>(&self, a: &A, matcher: &ArgMatcher) -> bool where A: AnyArg<'a, 'b> {
fn is_missing_required_ok<A>(&self, a: &A, matcher: &ArgMatcher) -> bool where A: AnyArg<'a, 'b> {
if let Some(bl) = a.blacklist() {
for n in bl.iter() {
if matcher.contains(n)
Expand All @@ -1276,6 +1286,20 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
return true;
}
}
} else if let Some(ru) = a.required_unless() {
for n in ru.iter() {
if matcher.contains(n)
|| self.groups
.get(n)
.map_or(false, |g| g.args.iter().any(|an| matcher.contains(an))) {
if !a.is_set(ArgSettings::RequiredUnlessAll) {
return true;
}
} else {
return false;
}
}
return true;
}
false
}
Expand Down Expand Up @@ -1323,6 +1347,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
// after all arguments were parsed, but before any subcommands have been parsed
// (so as to give subcommands their own usage recursively)
pub fn create_usage_no_title(&self, used: &[&str]) -> String {
debugln!("fn=create_usage_no_title;");
let mut usage = String::with_capacity(75);
if let Some(u) = self.meta.usage_str {
usage.push_str(&*u);
Expand All @@ -1332,7 +1357,8 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
.unwrap_or(self.meta.bin_name
.as_ref()
.unwrap_or(&self.meta.name)));
let reqs: Vec<&str> = self.required().map(|r| &**r).collect();
let mut reqs: Vec<&str> = self.required().map(|r| &**r).collect();
reqs.dedup();
let req_string = self.get_required_from(&reqs, None)
.iter()
.fold(String::new(), |a, s| a + &format!(" {}", s)[..]);
Expand Down Expand Up @@ -1381,8 +1407,10 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
// Creates a context aware usage string, or "smart usage" from currently used
// args, and requirements
fn smart_usage(&self, usage: &mut String, used: &[&str]) {
debugln!("fn=smart_usage;");
let mut hs: Vec<&str> = self.required().map(|s| &**s).collect();
hs.extend_from_slice(used);

let r_string = self.get_required_from(&hs, None)
.iter()
.fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
Expand Down
1 change: 1 addition & 0 deletions src/args/any_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub trait AnyArg<'n, 'e> {
fn overrides(&self) -> Option<&[&'e str]>;
fn requires(&self) -> Option<&[&'e str]>;
fn blacklist(&self) -> Option<&[&'e str]>;
fn required_unless(&self) -> Option<&[&'e str]>;
fn is_set(&self, ArgSettings) -> bool;
fn set(&mut self, ArgSettings);
fn has_switch(&self) -> bool;
Expand Down

0 comments on commit bad46d3

Please sign in to comment.