Skip to content

Commit

Permalink
Auto merge of #797 - kbknapp:three-new-settings-and-a-fix, r=kbknapp
Browse files Browse the repository at this point in the history
Three new settings and a fix
  • Loading branch information
homu committed Dec 31, 2016
2 parents 7eb949b + 12026f6 commit 805b9b6
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 86 deletions.
4 changes: 3 additions & 1 deletion .clog.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ Performance = ["perf"]
Improvements = ["impr", "im", "imp"]
Documentation = ["docs"]
Deprecations = ["depr"]
Examples = ["examples"]
Examples = ["examples"]
"New Settings" = ["setting", "settings"]
"API Additions" = ["add", "api"]
65 changes: 38 additions & 27 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,6 @@

Contributions are always welcome! Please use the following guidelines when contributing to `clap`

1. Fork `clap`
2. Clone your fork (`git clone https://github.com/$YOUR_USERNAME/clap-rs && cd clap-rs`)
3. Create new branch (`git checkout -b new-branch`)
4. Make your changes, and commit (`git commit -am "your message"`)
* I use a [conventional](https://github.com/ajoslin/conventional-changelog/blob/a5505865ff3dd710cf757f50530e73ef0ca641da/conventions/angular.md) changelog format so I can update my changelog using [clog](https://github.com/clog-tool/clog-cli)
* In addition to the conventions defined above, I also use `imp`, `wip`, `examples`.
* Format your commit subject line using the following format: `TYPE(COMPONENT): MESSAGE` where `TYPE` is one of the following:
- `feat` - A new feature
- `imp` - An improvement to an existing feature
- `perf` - A performance improvement
- `docs` - Changes to documentation only
- `tests` - Changes to the testing framework or tests only
- `fix` - A bug fix
- `refactor` - Code functionality doesn't change, but underlying structure may
- `style` - Stylistic changes only, no functionality changes
- `wip` - A work in progress commit (Should typically be `git rebase`'ed away)
- `chore` - Catch all or things that have to do with the build system, etc
- `examples` - Changes to existing example, or a new example
* The `COMPONENT` is optional, and may be a single file, directory, or logical component. Can be omitted if commit applies globally
5. Run the tests (`cargo test --features "yaml unstable"`)
5. Run the lints (`cargo build --features lints`) (requires a nightly compiler)
6. `git rebase` into concise commits and remove `--fixup`s (`git rebase -i HEAD~NUM` where `NUM` is number of commits back)
7. Push your changes back to your fork (`git push origin $your-branch`)
8. Create a pull request! (You can also create the pull request first, and we'll merge when ready. This a good way to discuss proposed changes.)

Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell me. I'm all about giving credit where credit is due :)

## Goals

There are a few goals of `clap` that I'd like to maintain throughout contributions.
Expand All @@ -43,3 +16,41 @@ There are a few goals of `clap` that I'd like to maintain throughout contributio
* Try to be cognizant of memory usage
- Once parsing is complete, the memory footprint of `clap` should be low since the main program is the star of the show
* `panic!` on *developer* error, exit gracefully on *end-user* error

### Commit Messages

I use a [conventional](https://github.com/ajoslin/conventional-changelog/blob/a5505865ff3dd710cf757f50530e73ef0ca641da/conventions/angular.md) changelog format so I can update my changelog automatically using [clog](https://github.com/clog-tool/clog-cli)

* Please format your commit subject line using the following format: `TYPE(COMPONENT): MESSAGE` where `TYPE` is one of the following:
- `api` - An addition to the API
- `setting` - A new `AppSettings` variant
- `feat` - A new feature of an existing API
- `imp` - An improvement to an existing feature/API
- `perf` - A performance improvement
- `docs` - Changes to documentation only
- `tests` - Changes to the testing framework or tests only
- `fix` - A bug fix
- `refactor` - Code functionality doesn't change, but underlying structure may
- `style` - Stylistic changes only, no functionality changes
- `wip` - A work in progress commit (Should typically be `git rebase`'ed away)
- `chore` - Catch all or things that have to do with the build system, etc
- `examples` - Changes to existing example, or a new example
* The `COMPONENT` is optional, and may be a single file, directory, or logical component. Parenthesis can be omitted if you are opting not to use the `COMPONENT`.

### Tests and Documentation

1. Create tests for your changes
2. **Ensure the tests are passing.** Run the tests (`cargo test --features "yaml unstable"`), alternatively `just run-tests` if you have `just` installed.
3. **Optional** Run the lints (`cargo build --features lints`) (requires a nightly compiler), alternatively `just lint`
4. Ensure your changes contain documentation if adding new APIs or features.

### Preparing the PR

1. `git rebase` into concise commits and remove `--fixup`s or `wip` commits (`git rebase -i HEAD~NUM` where `NUM` is number of commits back to start the rebase)
2. Push your changes back to your fork (`git push origin $your-branch`)
3. Create a pull request against `master`! (You can also create the pull request first, and we'll merge when ready. This a good way to discuss proposed changes.)

### Other ways to contribute

Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell me. I'm all about giving credit where credit is due :)

2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ language: rust
cache: cargo
rust:
- nightly
- nightly-2016-11-20
- nightly-2016-12-29
- beta
- stable
- 1.11.0
Expand Down
20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ A simple to use, efficient, and full featured Command Line Argument Parser
"""

[dependencies]
bitflags = "~0.7"
vec_map = "~0.6"
unicode-width = "~0.1.3"
unicode-segmentation = "~0.1.2"
strsim = { version = "~0.5.1", optional = true }
ansi_term = { version = "~0.9.0", optional = true }
term_size = { version = "~0.2.0", optional = true }
libc = { version = "~0.2.9", optional = true }
yaml-rust = { version = "~0.3.2", optional = true }
clippy = { version = "~0.0.100", optional = true }
bitflags = "=0.7.0"
vec_map = "=0.6.0"
unicode-width = "=0.1.4"
unicode-segmentation = "1.0.1"
strsim = { version = "=0.6.0", optional = true }
ansi_term = { version = "=0.9.0", optional = true }
term_size = { version = "=0.2.0", optional = true }
libc = { version = "=0.2.18", optional = true }
yaml-rust = { version = "=0.3.5", optional = true }
clippy = { version = "~0.0.104", optional = true }

[dev-dependencies]
regex = "~0.1.80"
Expand Down
61 changes: 43 additions & 18 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ pub struct Parser<'a, 'b>
settings: AppFlags,
pub g_settings: Vec<AppSettings>,
pub meta: AppMeta<'b>,
trailing_vals: bool,
pub id: usize,
trailing_vals: bool,
valid_neg_num: bool,
// have we found a valid arg yet
valid_arg: bool,
}

impl<'a, 'b> Default for Parser<'a, 'b> {
Expand All @@ -87,6 +89,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
trailing_vals: false,
id: 0,
valid_neg_num: false,
valid_arg: false,
}
}
}
Expand Down Expand Up @@ -240,7 +243,7 @@ impl<'a, 'b> Parser<'a, 'b>
debug!("Parser::add_subcommand: Is help...");
if subcmd.p.meta.name == "help" {
sdebugln!("Yes");
self.settings.set(AppSettings::NeedsSubcommandHelp);
self.settings.unset(AppSettings::NeedsSubcommandHelp);
} else {
sdebugln!("No");
}
Expand Down Expand Up @@ -448,19 +451,32 @@ impl<'a, 'b> Parser<'a, 'b>
count += 1;
debugln!("Parser::get_args_tag:iter: {} Args not required", count);
}
if count > 1 || self.positionals.len() > 1 {
if !self.is_set(AppSettings::DontCollapseArgsInUsage) &&
(count > 1 || self.positionals.len() > 1) {
return None; // [ARGS]
} else if count == 1 {
let p = self.positionals.values().next().expect(INTERNAL_ERROR_MSG);
let p = self.positionals
.values()
.filter(|p| !p.is_set(ArgSettings::Required))
.next()
.expect(INTERNAL_ERROR_MSG);
return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()));
} else if self.is_set(AppSettings::DontCollapseArgsInUsage) &&
!self.positionals.is_empty() {
return Some(self.positionals
.values()
.filter(|p| !p.is_set(ArgSettings::Required))
.map(|p| format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()))
.collect::<Vec<_>>()
.join(""));
}
Some("".into())
}

// Determines if we need the `[FLAGS]` tag in the usage string
pub fn needs_flags_tag(&self) -> bool {
debugln!("Parser::needs_flags_tag;");
'outer: for f in self.flags.iter() {
'outer: for f in &self.flags {
debugln!("Parser::needs_flags_tag:iter: f={};", f.b.name);
if let Some(l) = f.s.long {
if l == "help" || l == "version" {
Expand Down Expand Up @@ -596,6 +612,9 @@ impl<'a, 'b> Parser<'a, 'b>
#[inline]
fn possible_subcommand(&self, arg_os: &OsStr) -> bool {
debugln!("Parser::possible_subcommand;");
if self.is_set(AppSettings::ArgsNegateSubcommands) && self.valid_arg {
return false;
}
self.subcommands
.iter()
.any(|s| {
Expand Down Expand Up @@ -823,6 +842,8 @@ impl<'a, 'b> Parser<'a, 'b>
}
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
break;
} else if self.is_set(AppSettings::ArgsNegateSubcommands) {
();
} else if let Some(cdate) =
suggestions::did_you_mean(&*arg_os.to_string_lossy(),
self.subcommands
Expand All @@ -842,6 +863,7 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::get_matches_with: Positional counter...{}", pos_counter);
debug!("Parser::get_matches_with: Checking for low index multiples...");
if self.is_set(AppSettings::LowIndexMultiplePositional) &&
!self.positionals.is_empty() &&
pos_counter == (self.positionals.len() - 1) {
sdebugln!("Found");
if let Some(na) = it.peek() {
Expand All @@ -864,6 +886,7 @@ impl<'a, 'b> Parser<'a, 'b>
}
if let Some(p) = self.positionals.get(pos_counter) {
parse_positional!(self, p, arg_os, pos_counter, matcher);
self.valid_arg = true;
} else if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
// Get external subcommand name
let sc_name = match arg_os.to_str() {
Expand Down Expand Up @@ -909,17 +932,15 @@ impl<'a, 'b> Parser<'a, 'b>
let sc_name = &*self.subcommands
.iter()
.filter(|sc| sc.p.meta.aliases.is_some())
.filter_map(|sc| if sc.p
.filter(|sc| sc.p
.meta
.aliases
.as_ref()
.unwrap()
.expect(INTERNAL_ERROR_MSG)
.iter()
.any(|&(a, _)| &a == &&*pos_sc_name) {
Some(sc.p.meta.name.clone())
} else {
None
})
.any(|&(a, _)| &a == &&*pos_sc_name)
)
.map(|sc| sc.p.meta.name.clone())
.next()
.expect(INTERNAL_ERROR_MSG);
try!(self.parse_subcommand(sc_name, matcher, it));
Expand Down Expand Up @@ -1204,7 +1225,8 @@ impl<'a, 'b> Parser<'a, 'b>
self.long_list.push("version");
self.flags.insert(id, arg);
}
if !self.subcommands.is_empty() && self.is_set(AppSettings::NeedsSubcommandHelp) {
if !self.subcommands.is_empty() && !self.is_set(AppSettings::DisableHelpSubcommand) &&
self.is_set(AppSettings::NeedsSubcommandHelp) {
debugln!("Parser::create_help_and_version: Building help");
self.subcommands
.push(App::new("help")
Expand Down Expand Up @@ -1307,12 +1329,14 @@ impl<'a, 'b> Parser<'a, 'b>

if let Some(opt) = find_by_long!(self, &arg, opts) {
debugln!("Parser::parse_long_arg: Found valid opt '{}'", opt.to_string());
self.valid_arg = true;
let ret = try!(self.parse_opt(val, opt, matcher));
arg_post_processing!(self, opt, matcher);

return Ok(ret);
} else if let Some(flag) = find_by_long!(self, &arg, flags) {
debugln!("Parser::parse_long_arg: Found valid flag '{}'", flag.to_string());
self.valid_arg = true;
// Only flags could be help or version, and we need to check the raw long
// so this is the first point to check
try!(self.check_for_help_and_version_str(arg));
Expand Down Expand Up @@ -1369,6 +1393,7 @@ impl<'a, 'b> Parser<'a, 'b>
.iter()
.find(|&o| o.s.short.is_some() && o.s.short.unwrap() == c) {
debugln!("Parser::parse_short_arg:iter: Found valid short opt -{} in '{}'", c, arg);
self.valid_arg = true;
// Check for trailing concatenated value
let p: Vec<_> = arg.splitn(2, c).collect();
debugln!("Parser::parse_short_arg:iter: arg: {:?}, arg_os: {:?}, full_arg: {:?}",
Expand Down Expand Up @@ -1396,6 +1421,7 @@ impl<'a, 'b> Parser<'a, 'b>
.iter()
.find(|&f| f.s.short.is_some() && f.s.short.unwrap() == c) {
debugln!("Parser::parse_short_arg:iter: Found valid short flag -{}", c);
self.valid_arg = true;
// Only flags can be help or version
try!(self.check_for_help_and_version_char(c));
try!(self.parse_flag(flag, matcher));
Expand Down Expand Up @@ -1789,12 +1815,10 @@ impl<'a, 'b> Parser<'a, 'b>

// Validate the conditionally required args
for &(a, v, r) in &self.r_ifs {
if let Some(ref ma) = matcher.get(a) {
if let Some(ma) = matcher.get(a) {
for val in ma.vals.values() {
if v == val {
if matcher.get(r).is_none() {
return self.missing_required_error(matcher);
}
if v == val && matcher.get(r).is_none() {
return self.missing_required_error(matcher);
}
}
}
Expand Down Expand Up @@ -2152,6 +2176,7 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
id: self.id,
valid_neg_num: self.valid_neg_num,
r_ifs: self.r_ifs.clone(),
valid_arg: self.valid_arg,
}
}
}
Loading

0 comments on commit 805b9b6

Please sign in to comment.