mirror of
https://github.com/clap-rs/clap
synced 2025-03-04 23:37:32 +00:00
Auto merge of #339 - kbknapp:cleanup, r=Vinatorul
Huge Cleanup Various cleanups and refactorings, the biggest of which (so far) is error handling, although I'm not 100% sure if I'm happy with the implementation yet or not. It's a big improvement from previous versions, but I think it can get better. ~~**DO NOT MERGE**~~
This commit is contained in:
commit
e4182ad950
33 changed files with 4221 additions and 4304 deletions
|
@ -14,6 +14,7 @@ script:
|
|||
cargo test --features yaml &&
|
||||
make -C clap-tests test &&
|
||||
travis-cargo --only stable doc -- --features yaml
|
||||
travis-cargo --only nightly bench
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
name = "clap"
|
||||
version = "1.4.7"
|
||||
authors = ["Kevin K. <kbknapp@gmail.com>"]
|
||||
exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "clap.png"]
|
||||
exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "*.png", "clap-perf/*"]
|
||||
description = "A simple to use, efficient, and full featured Command Line Argument Parser"
|
||||
repository = "https://github.com/kbknapp/clap-rs.git"
|
||||
documentation = "http://kbknapp.github.io/clap-rs"
|
||||
|
@ -12,8 +12,9 @@ license = "MIT"
|
|||
keywords = ["argument", "command", "arg", "parser", "parse"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "0.3.2"
|
||||
ansi_term = { version = "~0.6.3", optional = true }
|
||||
bitflags = "0.3.2"
|
||||
vec_map = "0.3"
|
||||
ansi_term = { version = "~0.7", optional = true }
|
||||
strsim = { version = "~0.4.0", optional = true }
|
||||
yaml-rust = { version = "~0.2.2", optional = true }
|
||||
clippy = { version = "~0.0.22", optional = true }
|
||||
|
|
22
README.md
22
README.md
|
@ -70,33 +70,23 @@ First, let me say that these comparisons are highly subjective, and not meant in
|
|||
|
||||
#### How does `clap` compare to [getopts](https://github.com/rust-lang-nursery/getopts)?
|
||||
|
||||
`getopts` is a very basic, fairly minimalist argument parsing library. This isn't a bad thing, sometimes you don't need tons of features, you just want to parse some simple arguments, and have some help text generated for you based on valid arguments you specify. When using `getopts` you must manually implement most of the common features (such as checking to display help messages, usage strings, etc.). If you want a highly custom argument parser, and don't mind writing most the argument parser yourself, `getopts` is an excellent base.
|
||||
`getopts` is a very basic, fairly minimalist argument parsing library. This isn't a bad thing, sometimes you don't need tons of features, you just want to parse some simple arguments, and have some help text generated for you based on valid arguments you specify. The downside to this approach is that you must manually implement most of the common features (such as checking to display help messages, usage strings, etc.). If you want a highly custom argument parser, and don't mind writing the majority of the functionality yourself, `getopts` is an excellent base.
|
||||
|
||||
Due to it's lack of features, `getopts` also doesn't allocate much, or at all. This gives it somewhat of a performance boost. Although, as you start implementing those features you need manually, that boost quickly disappears.
|
||||
`getopts` also doesn't allocate much, or at all. This gives it somewhat of a performance boost. Although, as you start implementing additional features, that boost quickly disappears.
|
||||
|
||||
Personally, I find many, many people that use `getopts` are manually implementing features that `clap` has by default. Using `clap` simplifies your codebase allowing you to focus on your application, and not argument parsing.
|
||||
|
||||
Reasons to use `getopts` instead of `clap`
|
||||
|
||||
* You need a few allocations as possible, don't plan on implementing any additional features
|
||||
* You want a highly custom argument parser, but want to use an established parser as a base
|
||||
|
||||
#### How does `clap` compare to [docopt.rs](https://github.com/docopt/docopt.rs)?
|
||||
|
||||
I first want to say I'm a big a fan of BurntSushi's work, the creator of `Docopt.rs`. I aspire to produce the quality of libraries that this man does! When it comes to comparing these two libraries they are very different. `docopt` tasks you with writing a help message, and then it parsers that message for you to determine all valid arguments and their use. Some people LOVE this, others not so much. If you're willing to write a detailed help message, it's nice that you can stick that in your program and have `docopt` do the rest. On the downside, it's somewhat less flexible than other options out there, and requires the help message change if you need to make changes.
|
||||
I first want to say I'm a big a fan of BurntSushi's work, the creator of `Docopt.rs`. I aspire to produce the quality of libraries that this man does! When it comes to comparing these two libraries they are very different. `docopt` tasks you with writing a help message, and then it parsers that message for you to determine all valid arguments and their use. Some people LOVE this approach, others not so much. If you're willing to write a detailed help message, it's nice that you can stick that in your program and have `docopt` do the rest. On the downside, it's somewhat less flexible, and requires you to change the help message if you need to make changes.
|
||||
|
||||
`docopt` is also excellent at translating arguments into Rust types automatically. There is even a syntax extension which will do all this for you, if you're willing to use a nightly compiler (use of a stable compiler requires you to manually translate from arguments to Rust types). To use BurntSushi's words, `docopt` is also somewhat of a black box. You get what you get, and it's hard to tweak implementation or customise your experience for your use case.
|
||||
`docopt` is also excellent at translating arguments into Rust types automatically. There is even a syntax extension which will do all this for you, if you're willing to use a nightly compiler (use of a stable compiler requires you to somewhat manually translate from arguments to Rust types). To use BurntSushi's words, `docopt` is also a sort of black box. You get what you get, and it's hard to tweak implementation or customise the experience for your use case.
|
||||
|
||||
Because `docopt` is doing a ton of work to parse your help messages and determine what you were trying to communicate as valid arguments, it's also one of the more heavy weight parsers performance-wise. For most applications this isn't a concern, but it's something to keep in mind.
|
||||
|
||||
Reasons to use `docopt` instead of `clap`
|
||||
* You want automatic translation from arguments to Rust types, and are using a nightly compiler
|
||||
* Performance isn't a concern
|
||||
* You don't have any complex relationships between arguments
|
||||
Because `docopt` is doing a ton of work to parse your help messages and determine what you were trying to communicate as valid arguments, it's also one of the more heavy weight parsers performance-wise. For most applications this isn't a concern and this isn't to say `docopt` is slow, in fact from it. This is just something to keep in mind while comparing.
|
||||
|
||||
#### All else being equal, what are some reasons to use `clap`?
|
||||
|
||||
`clap` is fast, and as lightweight as possible while still giving all the features you'd expect from a modern argument parser. If you use `clap` when just need some simple arguments parsed, you'll find it a walk in the park. But `clap` also makes it possible to represent extremely complex, and advanced requirements, without too much thought. `clap` aims to be intuitive, easy to use, and fully capable for wide variety use cases and needs.
|
||||
`clap` is as fast, and as lightweight as possible while still giving all the features you'd expect from a modern argument parser. In fact, for the amount and type of features `clap` offers the fact it remains about as fast as `getopts` is great. If you use `clap` when just need some simple arguments parsed, you'll find it a walk in the park. But `clap` also makes it possible to represent extremely complex, and advanced requirements, without too much thought. `clap` aims to be intuitive, easy to use, and fully capable for wide variety use cases and needs.
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ fn parse_sc_positional(b: &mut Bencher) {
|
|||
|
||||
#[bench]
|
||||
fn parse_complex1(b: &mut Bencher) {
|
||||
b.iter(|| create_app!().get_matches_from(vec!["", "-ff", "-o", "option1", "arg1", "-O", "fast", "arg2", "--multvals", "one", "two", "three"]));
|
||||
b.iter(|| create_app!().get_matches_from(vec!["", "-ff", "-o", "option1", "arg1", "-O", "fast", "arg2", "--multvals", "one", "two", "emacs"]));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
7
clap-perf/clap_perf.dat
Normal file
7
clap-perf/clap_perf.dat
Normal file
|
@ -0,0 +1,7 @@
|
|||
#Version Date Builder Err Usage Err Parse1 Err Parse2 Err
|
||||
1.0 2015-07-07 12408 840 16830 229 21235 725 27387 910
|
||||
1.1 2015-07-16 11885 191 16670 595 20919 252 26868 457
|
||||
1.2 2015-08-14 12563 587 17190 311 22421 233 28232 624
|
||||
1.3 2015-09-01 10534 131 14648 874 18213 1070 24101 361
|
||||
1.4 2015-09-09 10223 852 13203 749 18924 1216 23492 944
|
||||
1.5 2015-11-12 5422 416 7974 680 9723 792 13389 1151
|
BIN
clap-perf/clap_perf.png
Normal file
BIN
clap-perf/clap_perf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
26
clap-perf/plot_perf.gp
Normal file
26
clap-perf/plot_perf.gp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/gnuplot
|
||||
reset
|
||||
set terminal png
|
||||
set output "clap_perf.png"
|
||||
|
||||
set xlabel "Version"
|
||||
set xrange [0.9:1.6]
|
||||
set ylabel "Time (ns)"
|
||||
set yrange [0:35000]
|
||||
|
||||
set title "clap-rs Performance by Version"
|
||||
set key inside left bottom
|
||||
set grid
|
||||
set style line 1 lc rgb '#0060ad' lt 1 lw 1 pt 7 ps .5 # --- blue
|
||||
set style line 2 lc rgb '#dd181f' lt 1 lw 1 pt 5 ps .5 # --- red
|
||||
set style line 3 lc rgb '#18dd00' lt 1 lw 1 pt 7 ps .5 # --- green
|
||||
set style line 4 lc rgb '#000000' lt 1 lw 1 pt 5 ps .5 # --- black
|
||||
|
||||
plot "clap_perf.dat" u 1:3:4 notitle w yerrorbars ls 1, \
|
||||
"" u 1:3 t "Create Parser Using Builder" w lines ls 1, \
|
||||
"" u 1:5:6 notitle w yerrorbars ls 2, \
|
||||
"" u 1:5 t "Create Parser Usage String" w lines ls 2, \
|
||||
"" u 1:7:8 notitle "Parse Complex Args" w yerrorbars ls 3, \
|
||||
"" u 1:7 t "Parse Complex Args" w lines ls 3, \
|
||||
"" u 1:9:10 notitle w yerrorbars ls 4, \
|
||||
"" u 1:9 t "Parse Very Complex Args" w lines ls 4
|
|
@ -2,6 +2,7 @@
|
|||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
import difflib
|
||||
|
||||
failed = False
|
||||
|
||||
|
@ -216,14 +217,14 @@ option NOT present
|
|||
positional present with value: too
|
||||
subcmd NOT present'''
|
||||
|
||||
_mult_vals_more = '''error: The argument '--multvals' was supplied more than once, but does not support multiple values
|
||||
_mult_vals_more = '''error: The argument '--multvals <one> <two>' was supplied more than once, but does not support multiple occurrences
|
||||
|
||||
USAGE:
|
||||
\tclaptests --multvals <one> <two>
|
||||
|
||||
For more information try --help'''
|
||||
|
||||
_mult_vals_few = '''error: The argument '--multvals <one> <two>' requires a value but none was supplied
|
||||
_mult_vals_few = '''error: The argument '--multvals <one> <two>' requires 2 values, but 1 was provided
|
||||
|
||||
USAGE:
|
||||
\tclaptests --multvals <one> <two>
|
||||
|
@ -293,6 +294,9 @@ def pass_fail(name, cmd, check, good):
|
|||
return 0
|
||||
print('Fail')
|
||||
print('\n\n{}\n# Should be:\n$ {}\n{}\n\n{}\n# But is:\n$ {}\n{}\n\n'.format('#'*25, cmd, good, '#'*25, cmd, check))
|
||||
for line in difflib.context_diff(good.splitlines(), check.splitlines(), fromfile="Should Be", tofile="Currently Is", lineterm=""):
|
||||
print(line)
|
||||
print()
|
||||
return 1
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
format_strings = false
|
||||
reorder_imports = true
|
||||
chain_overflow_last = false
|
||||
same_line_if_else = true
|
||||
|
|
3683
src/app/app.rs
3683
src/app/app.rs
File diff suppressed because it is too large
Load diff
|
@ -1,296 +0,0 @@
|
|||
use std::process;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// Command line argument parser error types
|
||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||
pub enum ClapErrorType {
|
||||
/// Error occurs when some possible values were set, but clap found unexpected value
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .possible_value("fast")
|
||||
/// .possible_value("slow"))
|
||||
/// .get_matches_from_safe(vec!["", "other"]);
|
||||
/// ```
|
||||
InvalidValue,
|
||||
/// Error occurs when clap found unexpected flag or option
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::from_usage("-f, --flag 'some flag'"))
|
||||
/// .get_matches_from_safe(vec!["", "--other"]);
|
||||
/// ```
|
||||
InvalidArgument,
|
||||
/// Error occurs when clap found unexpected subcommand
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, SubCommand};
|
||||
/// let result = App::new("myprog")
|
||||
/// .subcommand(SubCommand::with_name("config")
|
||||
/// .about("Used for configuration")
|
||||
/// .arg(Arg::with_name("config_file")
|
||||
/// .help("The configuration file to use")
|
||||
/// .index(1)))
|
||||
/// .get_matches_from_safe(vec!["", "other"]);
|
||||
/// ```
|
||||
InvalidSubcommand,
|
||||
/// Error occurs when option does not allow empty values but some was found
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .empty_values(false))
|
||||
/// .arg(Arg::with_name("color"))
|
||||
/// .get_matches_from_safe(vec!["", "--debug", "--color"]);
|
||||
/// ```
|
||||
EmptyValue,
|
||||
/// Option fails validation of a custom validator
|
||||
ValueValidationError,
|
||||
/// Parser inner error
|
||||
ArgumentError,
|
||||
/// Error occurs when an application got more arguments then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .max_values(2))
|
||||
/// .get_matches_from_safe(vec!["", "too", "much", "values"]);
|
||||
/// ```
|
||||
TooManyArgs,
|
||||
/// Error occurs when argument got more values then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .max_values(2))
|
||||
/// .get_matches_from_safe(vec!["", "too", "much", "values"]);
|
||||
/// ```
|
||||
TooManyValues,
|
||||
/// Error occurs when argument got less values then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .min_values(3))
|
||||
/// .get_matches_from_safe(vec!["", "too", "few"]);
|
||||
/// ```
|
||||
TooFewValues,
|
||||
/// Error occurs when argument got a different number of values then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .max_values(2))
|
||||
/// .get_matches_from_safe(vec!["", "too", "much", "values"]);
|
||||
/// ```
|
||||
WrongNumValues,
|
||||
/// Error occurs when clap find two ore more conflicting arguments
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .conflicts_with("color"))
|
||||
/// .get_matches_from_safe(vec!["", "--debug", "--color"]);
|
||||
/// ```
|
||||
ArgumentConflict,
|
||||
/// Error occurs when one or more required arguments missing
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .required(true))
|
||||
/// .get_matches_from_safe(vec![""]);
|
||||
/// ```
|
||||
MissingRequiredArgument,
|
||||
/// Error occurs when required subcommand missing
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, AppSettings, SubCommand};
|
||||
/// let result = App::new("myprog")
|
||||
/// .setting(AppSettings::SubcommandRequired)
|
||||
/// .subcommand(SubCommand::with_name("config")
|
||||
/// .about("Used for configuration")
|
||||
/// .arg(Arg::with_name("config_file")
|
||||
/// .help("The configuration file to use")
|
||||
/// .index(1)))
|
||||
/// .get_matches_from_safe(vec![""]);
|
||||
/// ```
|
||||
MissingSubcommand,
|
||||
/// Occurs when no argument or subcommand has been supplied and
|
||||
/// `AppSettings::ArgRequiredElseHelp` was used
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, AppSettings, SubCommand};
|
||||
/// let result = App::new("myprog")
|
||||
/// .setting(AppSettings::ArgRequiredElseHelp)
|
||||
/// .subcommand(SubCommand::with_name("config")
|
||||
/// .about("Used for configuration")
|
||||
/// .arg(Arg::with_name("config_file")
|
||||
/// .help("The configuration file to use")
|
||||
/// .index(1)))
|
||||
/// .get_matches_from_safe(vec![""]);
|
||||
/// ```
|
||||
MissingArgumentOrSubcommand,
|
||||
/// Error occurs when clap find argument while is was not expecting any
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App};
|
||||
/// let result = App::new("myprog")
|
||||
/// .get_matches_from_safe(vec!["", "--arg"]);
|
||||
/// ```
|
||||
UnexpectedArgument,
|
||||
/// Error occurs when argument was used multiple times and was not set as multiple.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .multiple(false))
|
||||
/// .get_matches_from_safe(vec!["", "--debug", "--debug"]);
|
||||
/// ```
|
||||
UnexpectedMultipleUsage,
|
||||
/// Error occurs when argument contains invalid unicode characters
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// # use std::os::unix::ffi::OsStringExt;
|
||||
/// # use std::ffi::OsString;
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .short("u")
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from_safe(vec![OsString::from_vec(vec![0x20]),
|
||||
/// OsString::from_vec(vec![0xE9])]);
|
||||
/// assert!(result.is_err());
|
||||
/// ```
|
||||
InvalidUnicode,
|
||||
/// Not a true 'error' as it means `--help` or similar was used. The help message will be sent
|
||||
/// to `stdout` unless the help is displayed due to an error (such as missing subcommands) at
|
||||
/// which point it will be sent to `stderr`
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// # use clap::ClapErrorType;
|
||||
/// let result = App::new("myprog")
|
||||
/// .get_matches_from_safe(vec!["", "--help"]);
|
||||
/// assert!(result.is_err());
|
||||
/// assert_eq!(result.unwrap_err().error_type, ClapErrorType::HelpDisplayed);
|
||||
/// ```
|
||||
HelpDisplayed,
|
||||
/// Not a true 'error' as it means `--version` or similar was used. The message will be sent
|
||||
/// to `stdout`
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// # use clap::ClapErrorType;
|
||||
/// let result = App::new("myprog")
|
||||
/// .get_matches_from_safe(vec!["", "--version"]);
|
||||
/// assert!(result.is_err());
|
||||
/// assert_eq!(result.unwrap_err().error_type, ClapErrorType::VersionDisplayed);
|
||||
/// ```
|
||||
VersionDisplayed,
|
||||
/// Represents an internal error, please consider filing a bug report if this happens!
|
||||
InternalError,
|
||||
}
|
||||
|
||||
/// Command line argument parser error
|
||||
#[derive(Debug)]
|
||||
pub struct ClapError {
|
||||
/// Formated error message
|
||||
pub error: String,
|
||||
/// Command line argument parser error type
|
||||
pub error_type: ClapErrorType,
|
||||
}
|
||||
|
||||
impl ClapError {
|
||||
/// Prints the error to `stderr` and exits with a status of `1`
|
||||
pub fn exit(&self) -> ! {
|
||||
if self.use_stderr() {
|
||||
wlnerr!("{}", self.error);
|
||||
process::exit(1);
|
||||
}
|
||||
println!("{}", self.error);
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
fn use_stderr(&self) -> bool {
|
||||
match self.error_type {
|
||||
ClapErrorType::HelpDisplayed | ClapErrorType::VersionDisplayed => false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ClapError {
|
||||
fn description(&self) -> &str {
|
||||
&*self.error
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClapError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
130
src/app/macros.rs
Normal file
130
src/app/macros.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
macro_rules! remove_overriden {
|
||||
($me:ident, $name:expr) => ({
|
||||
if let Some(ref o) = $me.opts.iter().filter(|o| &o.name == $name).next() {
|
||||
if let Some(ref ora) = o.requires {
|
||||
for a in ora {
|
||||
vec_remove!($me.required, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.blacklist {
|
||||
for a in ora {
|
||||
vec_remove!($me.blacklist, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.overrides {
|
||||
for a in ora {
|
||||
vec_remove!($me.overrides, a);
|
||||
}
|
||||
}
|
||||
} else if let Some(ref o) = $me.flags.iter().filter(|f| &f.name == $name).next() {
|
||||
if let Some(ref ora) = o.requires {
|
||||
for a in ora {
|
||||
vec_remove!($me.required, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.blacklist {
|
||||
for a in ora {
|
||||
vec_remove!($me.blacklist, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.overrides {
|
||||
for a in ora {
|
||||
vec_remove!($me.overrides, a);
|
||||
}
|
||||
}
|
||||
} else if let Some(p) = $me.positionals.values().filter(|p| &&p.name == &$name).next() {
|
||||
if let Some(ref ora) = p.requires {
|
||||
for a in ora {
|
||||
vec_remove!($me.required, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = p.blacklist {
|
||||
for a in ora {
|
||||
vec_remove!($me.blacklist, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = p.overrides {
|
||||
for a in ora {
|
||||
vec_remove!($me.overrides, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! arg_post_processing(
|
||||
($me:ident, $arg:ident, $matcher:ident) => ({
|
||||
use args::AnyArg;
|
||||
// Handle POSIX overrides
|
||||
if $me.overrides.contains(&$arg.name()) {
|
||||
if let Some(ref name) = $me.overriden_from($arg.name(), $matcher) {
|
||||
$matcher.remove(name);
|
||||
remove_overriden!($me, name);
|
||||
}
|
||||
}
|
||||
if let Some(or) = $arg.overrides() {
|
||||
for pa in or {
|
||||
$matcher.remove(pa);
|
||||
remove_overriden!($me, pa);
|
||||
$me.overrides.push(pa);
|
||||
vec_remove!($me.required, pa);
|
||||
}
|
||||
}
|
||||
// Handle conflicts
|
||||
if let Some(bl) = $arg.blacklist() {
|
||||
for name in bl {
|
||||
$me.blacklist.push(name);
|
||||
vec_remove!($me.overrides, name);
|
||||
vec_remove!($me.required, name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all required args which aren't already found in matcher to the master
|
||||
// list
|
||||
if let Some(reqs) = $arg.requires() {
|
||||
for n in reqs {
|
||||
if $matcher.contains(n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$me.required.push(n);
|
||||
}
|
||||
}
|
||||
|
||||
_handle_group_reqs!($me, $arg);
|
||||
})
|
||||
);
|
||||
|
||||
macro_rules! _handle_group_reqs{
|
||||
($me:ident, $arg:ident) => ({
|
||||
use args::AnyArg;
|
||||
for grp in $me.groups.values() {
|
||||
let mut found = false;
|
||||
for name in grp.args.iter() {
|
||||
if name == &$arg.name() {
|
||||
vec_remove!($me.required, name);
|
||||
if let Some(ref reqs) = grp.requires {
|
||||
for r in reqs {
|
||||
$me.required.push(r);
|
||||
}
|
||||
}
|
||||
if let Some(ref bl) = grp.conflicts {
|
||||
for b in bl {
|
||||
$me.blacklist.push(b);
|
||||
}
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if found {
|
||||
for name in grp.args.iter() {
|
||||
if name == &$arg.name() { continue }
|
||||
vec_remove!($me.required, name);
|
||||
|
||||
$me.blacklist.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
2817
src/app/mod.rs
2817
src/app/mod.rs
File diff suppressed because it is too large
Load diff
19
src/args/any_arg.rs
Normal file
19
src/args/any_arg.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use args::settings::ArgSettings;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AnyArg<'n> {
|
||||
fn name(&self) -> &'n str;
|
||||
fn overrides(&self) -> Option<&[&'n str]>;
|
||||
fn requires(&self) -> Option<&[&'n str]>;
|
||||
fn blacklist(&self) -> Option<&[&'n str]>;
|
||||
fn is_set(&self, ArgSettings) -> bool;
|
||||
fn set(&mut self, ArgSettings);
|
||||
fn has_switch(&self) -> bool;
|
||||
fn max_vals(&self) -> Option<u8>;
|
||||
fn min_vals(&self) -> Option<u8>;
|
||||
fn num_vals(&self) -> Option<u8>;
|
||||
fn possible_vals(&self) -> Option<&[&'n str]>;
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> Result<(), String>>>;
|
||||
}
|
|
@ -98,6 +98,34 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> {
|
|||
pub hidden: bool,
|
||||
}
|
||||
|
||||
impl<'n, 'l, 'h, 'g, 'p, 'r> Default for Arg<'n, 'l, 'h, 'g, 'p, 'r> {
|
||||
fn default() -> Self {
|
||||
Arg {
|
||||
name: "",
|
||||
short: None,
|
||||
long: None,
|
||||
help: None,
|
||||
required: false,
|
||||
takes_value: false,
|
||||
multiple: false,
|
||||
index: None,
|
||||
possible_vals: None,
|
||||
blacklist: None,
|
||||
requires: None,
|
||||
num_vals: None,
|
||||
min_vals: None,
|
||||
max_vals: None,
|
||||
val_names: None,
|
||||
group: None,
|
||||
global: false,
|
||||
empty_vals: true,
|
||||
validator: None,
|
||||
overrides: None,
|
||||
hidden: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
|
||||
/// Creates a new instance of `Arg` using a unique string name.
|
||||
/// The name will be used by the library consumer to get information about
|
||||
|
@ -120,26 +148,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
|
|||
pub fn with_name(n: &'n str) -> Self {
|
||||
Arg {
|
||||
name: n,
|
||||
short: None,
|
||||
long: None,
|
||||
help: None,
|
||||
required: false,
|
||||
takes_value: false,
|
||||
multiple: false,
|
||||
index: None,
|
||||
possible_vals: None,
|
||||
blacklist: None,
|
||||
requires: None,
|
||||
num_vals: None,
|
||||
min_vals: None,
|
||||
max_vals: None,
|
||||
val_names: None,
|
||||
group: None,
|
||||
global: false,
|
||||
empty_vals: true,
|
||||
validator: None,
|
||||
overrides: None,
|
||||
hidden: false,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,10 +379,6 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
|
|||
required: required,
|
||||
takes_value: takes_value,
|
||||
multiple: multiple,
|
||||
index: None,
|
||||
possible_vals: None,
|
||||
blacklist: None,
|
||||
requires: None,
|
||||
num_vals: if num_names > 1 {
|
||||
Some(num_names)
|
||||
} else {
|
||||
|
@ -384,14 +389,7 @@ impl<'n, 'l, 'h, 'g, 'p, 'r> Arg<'n, 'l, 'h, 'g, 'p, 'r> {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
max_vals: None,
|
||||
min_vals: None,
|
||||
group: None,
|
||||
global: false,
|
||||
empty_vals: true,
|
||||
validator: None,
|
||||
overrides: None,
|
||||
hidden: false,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
use std::fmt::{Display, Formatter, Result};
|
||||
use std::convert::From;
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use Arg;
|
||||
use args::AnyArg;
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -167,6 +170,33 @@ impl<'n> Display for FlagBuilder<'n> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n> AnyArg<'n> for FlagBuilder<'n> {
|
||||
fn name(&self) -> &'n str { self.name }
|
||||
|
||||
fn overrides(&self) -> Option<&[&'n str]> { self.overrides.as_ref().map(|o| &o[..]) }
|
||||
|
||||
fn requires(&self) -> Option<&[&'n str]> { self.requires.as_ref().map(|o| &o[..]) }
|
||||
|
||||
fn blacklist(&self) -> Option<&[&'n str]> { self.blacklist.as_ref().map(|o| &o[..]) }
|
||||
|
||||
fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(&s) }
|
||||
|
||||
fn has_switch(&self) -> bool { true }
|
||||
|
||||
fn set(&mut self, s: ArgSettings) { self.settings.set(&s) }
|
||||
|
||||
fn max_vals(&self) -> Option<u8> { None }
|
||||
|
||||
fn num_vals(&self) -> Option<u8> { None }
|
||||
|
||||
fn possible_vals(&self) -> Option<&[&'n str]> { None }
|
||||
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
|
||||
|
||||
fn min_vals(&self) -> Option<u8> { None }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::FlagBuilder;
|
|
@ -4,8 +4,10 @@ use std::fmt::{Display, Formatter, Result};
|
|||
use std::result::Result as StdResult;
|
||||
use std::io;
|
||||
|
||||
use Arg;
|
||||
use args::{AnyArg, ArgMatcher, Arg};
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
use errors::{ClapResult, error_builder};
|
||||
use app::App;
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct OptBuilder<'n> {
|
||||
|
@ -34,10 +36,10 @@ pub struct OptBuilder<'n> {
|
|||
pub settings: ArgFlags,
|
||||
}
|
||||
|
||||
impl<'n> OptBuilder<'n> {
|
||||
pub fn new(name: &'n str) -> Self {
|
||||
impl<'n> Default for OptBuilder<'n> {
|
||||
fn default() -> Self {
|
||||
OptBuilder {
|
||||
name: name,
|
||||
name: "",
|
||||
short: None,
|
||||
long: None,
|
||||
help: None,
|
||||
|
@ -53,6 +55,14 @@ impl<'n> OptBuilder<'n> {
|
|||
settings: ArgFlags::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'n> OptBuilder<'n> {
|
||||
pub fn new(name: &'n str) -> Self {
|
||||
OptBuilder {
|
||||
name: name,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_arg(a: &Arg<'n, 'n, 'n, 'n, 'n, 'n>, reqs: &mut Vec<&'n str>) -> Self {
|
||||
if a.short.is_none() && a.long.is_none() {
|
||||
|
@ -65,17 +75,12 @@ impl<'n> OptBuilder<'n> {
|
|||
name: a.name,
|
||||
short: a.short,
|
||||
long: a.long,
|
||||
blacklist: None,
|
||||
help: a.help,
|
||||
possible_vals: None,
|
||||
num_vals: a.num_vals,
|
||||
min_vals: a.min_vals,
|
||||
max_vals: a.max_vals,
|
||||
val_names: a.val_names.clone(),
|
||||
requires: None,
|
||||
validator: None,
|
||||
overrides: None,
|
||||
settings: ArgFlags::new(),
|
||||
..Default::default()
|
||||
};
|
||||
if a.multiple {
|
||||
ob.settings.set(&ArgSettings::Multiple);
|
||||
|
@ -187,6 +192,50 @@ impl<'n> OptBuilder<'n> {
|
|||
print_opt_help!(self, longest + 12, w);
|
||||
write!(w, "\n")
|
||||
}
|
||||
|
||||
pub fn validate_value(&self,
|
||||
val: &str,
|
||||
matcher: &ArgMatcher,
|
||||
app: &App)
|
||||
-> ClapResult<()> {
|
||||
// Check the possible values
|
||||
if let Some(ref p_vals) = self.possible_vals {
|
||||
if !p_vals.contains(&val) {
|
||||
let usage = try!(app.create_current_usage(matcher));
|
||||
return Err(error_builder::InvalidValue(val, p_vals, &self.to_string(), &usage));
|
||||
}
|
||||
}
|
||||
|
||||
// Check the required number of values
|
||||
if let Some(num) = self.num_vals {
|
||||
if let Some(ref ma) = matcher.get(self.name) {
|
||||
if let Some(ref vals) = ma.values {
|
||||
if (vals.len() as u8) > num && !self.settings.is_set(&ArgSettings::Multiple) {
|
||||
return Err(error_builder::TooManyValues(
|
||||
val,
|
||||
&self.to_string(),
|
||||
&*try!(app.create_current_usage(matcher))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it's an empty value, and we don't allow that, report the error
|
||||
if !self.settings.is_set(&ArgSettings::EmptyValues) &&
|
||||
matcher.contains(self.name) &&
|
||||
val.is_empty() {
|
||||
return Err(error_builder::EmptyValue(&*self.to_string(),
|
||||
&*try!(app.create_current_usage(matcher))));
|
||||
}
|
||||
|
||||
if let Some(ref vtor) = self.validator {
|
||||
if let Err(e) = vtor(val.to_owned()) {
|
||||
return Err(error_builder::ValueValidationError(&*e));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n> Display for OptBuilder<'n> {
|
||||
|
@ -217,6 +266,54 @@ impl<'n> Display for OptBuilder<'n> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'n> AnyArg<'n> for OptBuilder<'n> {
|
||||
fn name(&self) -> &'n str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn overrides(&self) -> Option<&[&'n str]> {
|
||||
self.overrides.as_ref().map(|o| &o[..])
|
||||
}
|
||||
|
||||
fn requires(&self) -> Option<&[&'n str]> {
|
||||
self.requires.as_ref().map(|o| &o[..])
|
||||
}
|
||||
|
||||
fn blacklist(&self) -> Option<&[&'n str]> {
|
||||
self.blacklist.as_ref().map(|o| &o[..])
|
||||
}
|
||||
|
||||
fn is_set(&self, s: ArgSettings) -> bool {
|
||||
self.settings.is_set(&s)
|
||||
}
|
||||
|
||||
fn has_switch(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set(&mut self, s: ArgSettings) {
|
||||
self.settings.set(&s)
|
||||
}
|
||||
|
||||
fn max_vals(&self) -> Option<u8> {
|
||||
self.max_vals
|
||||
}
|
||||
fn num_vals(&self) -> Option<u8> {
|
||||
self.num_vals
|
||||
}
|
||||
fn possible_vals(&self) -> Option<&[&'n str]> {
|
||||
self.possible_vals.as_ref().map(|o| &o[..])
|
||||
}
|
||||
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||
self.validator.as_ref()
|
||||
}
|
||||
|
||||
fn min_vals(&self) -> Option<u8> {
|
||||
self.min_vals
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::OptBuilder;
|
|
@ -4,6 +4,7 @@ use std::rc::Rc;
|
|||
use std::io;
|
||||
|
||||
use Arg;
|
||||
use args::AnyArg;
|
||||
use args::settings::{ArgFlags, ArgSettings};
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
|
@ -181,6 +182,53 @@ impl<'n> Display for PosBuilder<'n> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n> AnyArg<'n> for PosBuilder<'n> {
|
||||
fn name(&self) -> &'n str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn overrides(&self) -> Option<&[&'n str]> {
|
||||
self.overrides.as_ref().map(|o| &o[..])
|
||||
}
|
||||
|
||||
fn requires(&self) -> Option<&[&'n str]> {
|
||||
self.requires.as_ref().map(|o| &o[..])
|
||||
}
|
||||
|
||||
fn blacklist(&self) -> Option<&[&'n str]> {
|
||||
self.blacklist.as_ref().map(|o| &o[..])
|
||||
}
|
||||
|
||||
fn is_set(&self, s: ArgSettings) -> bool {
|
||||
self.settings.is_set(&s)
|
||||
}
|
||||
|
||||
fn set(&mut self, s: ArgSettings) {
|
||||
self.settings.set(&s)
|
||||
}
|
||||
|
||||
fn has_switch(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn max_vals(&self) -> Option<u8> {
|
||||
self.max_vals
|
||||
}
|
||||
fn num_vals(&self) -> Option<u8> {
|
||||
self.num_vals
|
||||
}
|
||||
fn possible_vals(&self) -> Option<&[&'n str]> {
|
||||
self.possible_vals.as_ref().map(|o| &o[..])
|
||||
}
|
||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||
self.validator.as_ref()
|
||||
}
|
||||
|
||||
fn min_vals(&self) -> Option<u8> {
|
||||
self.min_vals
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::PosBuilder;
|
97
src/args/arg_matcher.rs
Normal file
97
src/args/arg_matcher.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use vec_map::VecMap;
|
||||
|
||||
use args::{ArgMatches, MatchedArg, SubCommand};
|
||||
use std::collections::hash_map::{Entry, Keys, Iter};
|
||||
|
||||
pub struct ArgMatcher<'ar>(ArgMatches<'ar, 'ar>);
|
||||
|
||||
impl<'ar> ArgMatcher<'ar> {
|
||||
pub fn new() -> Self {
|
||||
ArgMatcher(ArgMatches::new())
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> {
|
||||
self.0.args.get_mut(arg)
|
||||
}
|
||||
|
||||
pub fn get(&self, arg: &str) -> Option<&MatchedArg> {
|
||||
self.0.args.get(arg)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, arg: &str) {
|
||||
self.0.args.remove(arg);
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, name: &'ar str) {
|
||||
self.0.args.insert(name, MatchedArg::new());
|
||||
}
|
||||
|
||||
pub fn contains(&self, arg: &str) -> bool {
|
||||
self.0.args.contains_key(arg)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.args.is_empty()
|
||||
}
|
||||
|
||||
pub fn values_of(&self, arg: &str) -> Option<Vec<&str>> {
|
||||
self.0.values_of(arg)
|
||||
}
|
||||
|
||||
pub fn usage(&mut self, usage: String) {
|
||||
self.0.usage = Some(usage);
|
||||
}
|
||||
|
||||
pub fn arg_names(&self) -> Keys<&'ar str, MatchedArg> {
|
||||
self.0.args.keys()
|
||||
}
|
||||
|
||||
pub fn entry(&mut self, arg: &'ar str) -> Entry<&'ar str, MatchedArg> {
|
||||
self.0.args.entry(arg)
|
||||
}
|
||||
|
||||
pub fn subcommand(&mut self, sc: SubCommand<'ar, 'ar>) {
|
||||
self.0.subcommand = Some(Box::new(sc));
|
||||
}
|
||||
|
||||
pub fn subcommand_name(&self) -> Option<&str> {
|
||||
self.0.subcommand_name()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<&'ar str, MatchedArg> {
|
||||
self.0.args.iter()
|
||||
}
|
||||
|
||||
pub fn inc_occurrence_of(&mut self, arg: &'ar str) {
|
||||
if let Some(a) = self.get_mut(arg) {
|
||||
a.occurrences += 1;
|
||||
return;
|
||||
}
|
||||
self.insert(arg);
|
||||
}
|
||||
|
||||
pub fn inc_occurrences_of(&mut self, args: &[&'ar str]) {
|
||||
for arg in args {
|
||||
self.inc_occurrence_of(arg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_val_to(&mut self, arg: &'ar str, val: String) {
|
||||
let ma = self.entry(arg).or_insert(MatchedArg {
|
||||
// occurrences will be incremented on getting a value
|
||||
occurrences: 0,
|
||||
values: Some(VecMap::new()),
|
||||
});
|
||||
if let Some(ref mut vals) = ma.values {
|
||||
let len = vals.len() + 1;
|
||||
vals.insert(len, val);
|
||||
ma.occurrences += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ar> Into<ArgMatches<'ar, 'ar>> for ArgMatcher<'ar> {
|
||||
fn into(self) -> ArgMatches<'ar, 'ar> {
|
||||
self.0
|
||||
}
|
||||
}
|
19
src/args/matched_arg.rs
Normal file
19
src/args/matched_arg.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use vec_map::VecMap;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
pub struct MatchedArg {
|
||||
#[doc(hidden)]
|
||||
pub occurrences: u8,
|
||||
#[doc(hidden)]
|
||||
pub values: Option<VecMap<String>>,
|
||||
}
|
||||
|
||||
impl MatchedArg {
|
||||
pub fn new() -> Self {
|
||||
MatchedArg {
|
||||
occurrences: 1,
|
||||
values: None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
pub struct MatchedArg {
|
||||
// #[doc(hidden)]
|
||||
// pub name: String,
|
||||
#[doc(hidden)]
|
||||
pub occurrences: u8,
|
||||
#[doc(hidden)]
|
||||
pub values: Option<BTreeMap<u8, String>>,
|
||||
}
|
|
@ -1,15 +1,19 @@
|
|||
pub use self::arg::Arg;
|
||||
pub use self::argmatches::ArgMatches;
|
||||
pub use self::arg_matches::ArgMatches;
|
||||
pub use self::arg_matcher::ArgMatcher;
|
||||
pub use self::subcommand::SubCommand;
|
||||
pub use self::argbuilder::{FlagBuilder, OptBuilder, PosBuilder};
|
||||
pub use self::matchedarg::MatchedArg;
|
||||
pub use self::arg_builder::{FlagBuilder, OptBuilder, PosBuilder};
|
||||
pub use self::matched_arg::MatchedArg;
|
||||
pub use self::group::ArgGroup;
|
||||
pub use self::any_arg::AnyArg;
|
||||
|
||||
mod arg;
|
||||
mod argmatches;
|
||||
pub mod any_arg;
|
||||
mod arg_matches;
|
||||
mod arg_matcher;
|
||||
mod subcommand;
|
||||
mod argbuilder;
|
||||
mod matchedarg;
|
||||
mod arg_builder;
|
||||
mod matched_arg;
|
||||
mod group;
|
||||
#[allow(dead_code)]
|
||||
pub mod settings;
|
||||
|
|
616
src/errors.rs
Normal file
616
src/errors.rs
Normal file
|
@ -0,0 +1,616 @@
|
|||
use std::process;
|
||||
use std::error::Error;
|
||||
use std::fmt as std_fmt;
|
||||
use std::io::{self, Write};
|
||||
use std::convert::From;
|
||||
|
||||
use fmt::Format;
|
||||
|
||||
pub type ClapResult<T> = Result<T, ClapError>;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
pub mod error_builder {
|
||||
use suggestions;
|
||||
use super::ClapError;
|
||||
use super::ClapErrorType as cet;
|
||||
use fmt::Format;
|
||||
|
||||
/// Error occurs when clap find two ore more conflicting arguments
|
||||
pub fn ArgumentConflict<S>(arg: S, other: Option<S>, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The argument '{}' cannot be used with {}\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(arg.as_ref()),
|
||||
match other {
|
||||
Some(name) => format!("'{}'", Format::Warning(name)),
|
||||
None => "one or more of the other specified arguments".to_owned(),
|
||||
},
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::ArgumentConflict,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when option does not allow empty values but some was found
|
||||
pub fn EmptyValue<S>(arg: S, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The argument '{}' requires a value but none was supplied\
|
||||
\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(arg.as_ref()),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::EmptyValue,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when some possible values were set, but clap found unexpected value
|
||||
pub fn InvalidValue<S>(bad_val: S,
|
||||
good_vals: &[S],
|
||||
arg: S,
|
||||
usage: S)
|
||||
-> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
let suffix = suggestions::did_you_mean_suffix(arg.as_ref(),
|
||||
good_vals.iter(),
|
||||
suggestions::DidYouMeanMessageStyle::EnumValue);
|
||||
|
||||
let mut sorted = vec![];
|
||||
for v in good_vals {
|
||||
sorted.push(v.as_ref());
|
||||
}
|
||||
sorted.sort();
|
||||
let valid_values = sorted.iter()
|
||||
.fold(String::new(), |a, name| a + &format!( " {}", name)[..]);
|
||||
ClapError {
|
||||
error: format!("{} '{}' isn't a valid value for '{}'\n\t\
|
||||
[valid values:{}]\n\
|
||||
{}\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(bad_val.as_ref()),
|
||||
Format::Warning(arg.as_ref()),
|
||||
valid_values,
|
||||
suffix.0,
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::InvalidValue,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when clap found unexpected flag or option
|
||||
pub fn InvalidArgument<S>(arg: S, did_you_mean: Option<S>, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The argument '{}' isn't valid{}\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(arg),
|
||||
if did_you_mean.is_some() {
|
||||
format!("{}\n", did_you_mean.unwrap().as_ref())
|
||||
} else {
|
||||
"".to_owned()
|
||||
},
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::InvalidArgument,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when clap found unexpected subcommand
|
||||
pub fn InvalidSubcommand<S>(subcmd: S, did_you_mean: S, name: S, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The subcommand '{}' isn't valid\n\t\
|
||||
Did you mean '{}' ?\n\n\
|
||||
If you received this message in error, try \
|
||||
re-running with '{} {} {}'\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(subcmd.as_ref()),
|
||||
Format::Good(did_you_mean.as_ref()),
|
||||
name.as_ref(),
|
||||
Format::Good("--"),
|
||||
subcmd.as_ref(),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::InvalidSubcommand,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when one or more required arguments missing
|
||||
pub fn MissingRequiredArgument<S>(required: S, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The following required arguments were not supplied:{}\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
required.as_ref(),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::MissingRequiredArgument,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when required subcommand missing
|
||||
pub fn MissingSubcommand<S>(name: S, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} '{}' requires a subcommand but none was provided\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(name),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::MissingSubcommand,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Error occurs when argument contains invalid unicode characters
|
||||
pub fn InvalidUnicode<S>(usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} Invalid unicode character in one or more arguments\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::InvalidUnicode,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when argument got more values then were expected
|
||||
pub fn TooManyValues<S>(val: S, arg: S, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The argument '{}' was found, but '{}' wasn't expecting \
|
||||
any more values\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(val),
|
||||
Format::Warning(arg),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::TooManyValues,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when argument got less values then were expected
|
||||
pub fn TooFewValues<S>(arg: S, min_vals: u8, curr_vals: usize, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The argument '{}' requires at least {} values, but {} w{} \
|
||||
provided\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(arg.as_ref()),
|
||||
Format::Warning(min_vals.to_string()),
|
||||
Format::Warning(curr_vals.to_string()),
|
||||
if curr_vals > 1 {
|
||||
"ere"
|
||||
} else {
|
||||
"as"
|
||||
},
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::TooFewValues,
|
||||
}
|
||||
}
|
||||
|
||||
/// Option fails validation of a custom validator
|
||||
pub fn ValueValidationError<S>(err: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} {}", Format::Error("error:"), err.as_ref()),
|
||||
error_type: cet::ValueValidationError,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when argument got a different number of values then were expected
|
||||
pub fn WrongNumValues<S>(arg: S,
|
||||
num_vals: u8,
|
||||
curr_vals: usize,
|
||||
suffix: S,
|
||||
usage: S)
|
||||
-> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The argument '{}' requires {} values, but {} w{} \
|
||||
provided\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(arg.as_ref()),
|
||||
Format::Warning(num_vals.to_string()),
|
||||
Format::Warning(curr_vals.to_string()),
|
||||
suffix.as_ref(),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::InvalidSubcommand,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when clap find argument while is was not expecting any
|
||||
pub fn UnexpectedArgument<S>(arg: S, name: S, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} Found argument '{}', but {} wasn't expecting any\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(arg),
|
||||
Format::Warning(name),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::UnexpectedArgument,
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when argument was used multiple times and was not set as multiple.
|
||||
pub fn UnexpectedMultipleUsage<S>(arg: S, usage: S) -> ClapError
|
||||
where S: AsRef<str>
|
||||
{
|
||||
ClapError {
|
||||
error: format!("{} The argument '{}' was supplied more than once, but does \
|
||||
not support multiple occurrences\n\n\
|
||||
{}\n\n\
|
||||
For more information try {}",
|
||||
Format::Error("error:"),
|
||||
Format::Warning(arg),
|
||||
usage.as_ref(),
|
||||
Format::Good("--help")),
|
||||
error_type: cet::UnexpectedMultipleUsage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Command line argument parser error types
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum ClapErrorType {
|
||||
/// Error occurs when some possible values were set, but clap found unexpected value
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .possible_value("fast")
|
||||
/// .possible_value("slow"))
|
||||
/// .get_matches_from_safe(vec!["", "other"]);
|
||||
/// ```
|
||||
InvalidValue,
|
||||
/// Error occurs when clap found unexpected flag or option
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::from_usage("-f, --flag 'some flag'"))
|
||||
/// .get_matches_from_safe(vec!["", "--other"]);
|
||||
/// ```
|
||||
InvalidArgument,
|
||||
/// Error occurs when clap found unexpected subcommand
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, SubCommand};
|
||||
/// let result = App::new("myprog")
|
||||
/// .subcommand(SubCommand::with_name("config")
|
||||
/// .about("Used for configuration")
|
||||
/// .arg(Arg::with_name("config_file")
|
||||
/// .help("The configuration file to use")
|
||||
/// .index(1)))
|
||||
/// .get_matches_from_safe(vec!["", "other"]);
|
||||
/// ```
|
||||
InvalidSubcommand,
|
||||
/// Error occurs when option does not allow empty values but some was found
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .empty_values(false))
|
||||
/// .arg(Arg::with_name("color"))
|
||||
/// .get_matches_from_safe(vec!["", "--debug", "--color"]);
|
||||
/// ```
|
||||
EmptyValue,
|
||||
/// Option fails validation of a custom validator
|
||||
ValueValidationError,
|
||||
/// Parser inner error
|
||||
ArgumentError,
|
||||
/// Error occurs when an application got more arguments then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .max_values(2))
|
||||
/// .get_matches_from_safe(vec!["", "too", "much", "values"]);
|
||||
/// ```
|
||||
TooManyArgs,
|
||||
/// Error occurs when argument got more values then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .max_values(2))
|
||||
/// .get_matches_from_safe(vec!["", "too", "much", "values"]);
|
||||
/// ```
|
||||
TooManyValues,
|
||||
/// Error occurs when argument got less values then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .min_values(3))
|
||||
/// .get_matches_from_safe(vec!["", "too", "few"]);
|
||||
/// ```
|
||||
TooFewValues,
|
||||
/// Error occurs when argument got a different number of values then were expected
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug").index(1)
|
||||
/// .max_values(2))
|
||||
/// .get_matches_from_safe(vec!["", "too", "much", "values"]);
|
||||
/// ```
|
||||
WrongNumValues,
|
||||
/// Error occurs when clap find two ore more conflicting arguments
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .conflicts_with("color"))
|
||||
/// .get_matches_from_safe(vec!["", "--debug", "--color"]);
|
||||
/// ```
|
||||
ArgumentConflict,
|
||||
/// Error occurs when one or more required arguments missing
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .required(true))
|
||||
/// .get_matches_from_safe(vec![""]);
|
||||
/// ```
|
||||
MissingRequiredArgument,
|
||||
/// Error occurs when required subcommand missing
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, AppSettings, SubCommand};
|
||||
/// let result = App::new("myprog")
|
||||
/// .setting(AppSettings::SubcommandRequired)
|
||||
/// .subcommand(SubCommand::with_name("config")
|
||||
/// .about("Used for configuration")
|
||||
/// .arg(Arg::with_name("config_file")
|
||||
/// .help("The configuration file to use")
|
||||
/// .index(1)))
|
||||
/// .get_matches_from_safe(vec![""]);
|
||||
/// ```
|
||||
MissingSubcommand,
|
||||
/// Occurs when no argument or subcommand has been supplied and
|
||||
/// `AppSettings::ArgRequiredElseHelp` was used
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg, AppSettings, SubCommand};
|
||||
/// let result = App::new("myprog")
|
||||
/// .setting(AppSettings::ArgRequiredElseHelp)
|
||||
/// .subcommand(SubCommand::with_name("config")
|
||||
/// .about("Used for configuration")
|
||||
/// .arg(Arg::with_name("config_file")
|
||||
/// .help("The configuration file to use")
|
||||
/// .index(1)))
|
||||
/// .get_matches_from_safe(vec![""]);
|
||||
/// ```
|
||||
MissingArgumentOrSubcommand,
|
||||
/// Error occurs when clap find argument while is was not expecting any
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App};
|
||||
/// let result = App::new("myprog")
|
||||
/// .get_matches_from_safe(vec!["", "--arg"]);
|
||||
/// ```
|
||||
UnexpectedArgument,
|
||||
/// Error occurs when argument was used multiple times and was not set as multiple.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .multiple(false))
|
||||
/// .get_matches_from_safe(vec!["", "--debug", "--debug"]);
|
||||
/// ```
|
||||
UnexpectedMultipleUsage,
|
||||
/// Error occurs when argument contains invalid unicode characters
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// # use std::os::unix::ffi::OsStringExt;
|
||||
/// # use std::ffi::OsString;
|
||||
/// let result = App::new("myprog")
|
||||
/// .arg(Arg::with_name("debug")
|
||||
/// .short("u")
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from_safe(vec![OsString::from_vec(vec![0x20]),
|
||||
/// OsString::from_vec(vec![0xE9])]);
|
||||
/// assert!(result.is_err());
|
||||
/// ```
|
||||
InvalidUnicode,
|
||||
/// Not a true 'error' as it means `--help` or similar was used. The help message will be sent
|
||||
/// to `stdout` unless the help is displayed due to an error (such as missing subcommands) at
|
||||
/// which point it will be sent to `stderr`
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// # use clap::ClapErrorType;
|
||||
/// let result = App::new("myprog")
|
||||
/// .get_matches_from_safe(vec!["", "--help"]);
|
||||
/// assert!(result.is_err());
|
||||
/// assert_eq!(result.unwrap_err().error_type, ClapErrorType::HelpDisplayed);
|
||||
/// ```
|
||||
HelpDisplayed,
|
||||
/// Not a true 'error' as it means `--version` or similar was used. The message will be sent
|
||||
/// to `stdout`
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use clap::{App, Arg};
|
||||
/// # use clap::ClapErrorType;
|
||||
/// let result = App::new("myprog")
|
||||
/// .get_matches_from_safe(vec!["", "--version"]);
|
||||
/// assert!(result.is_err());
|
||||
/// assert_eq!(result.unwrap_err().error_type, ClapErrorType::VersionDisplayed);
|
||||
/// ```
|
||||
VersionDisplayed,
|
||||
/// Represents an internal error, please consider filing a bug report if this happens!
|
||||
InternalError,
|
||||
/// Represents an I/O error, typically white writing to stderr or stdout
|
||||
Io,
|
||||
/// Represents an Rust Display Format error, typically white writing to stderr or stdout
|
||||
Format,
|
||||
}
|
||||
|
||||
/// Command line argument parser error
|
||||
#[derive(Debug)]
|
||||
pub struct ClapError {
|
||||
/// Formated error message
|
||||
pub error: String,
|
||||
/// The type of error
|
||||
pub error_type: ClapErrorType,
|
||||
}
|
||||
|
||||
impl ClapError {
|
||||
/// Should the message be written to `stdout` or not
|
||||
pub fn use_stderr(&self) -> bool {
|
||||
match self.error_type {
|
||||
ClapErrorType::HelpDisplayed | ClapErrorType::VersionDisplayed => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
/// Prints the error to `stderr` and exits with a status of `1`
|
||||
pub fn exit(&self) -> ! {
|
||||
if self.use_stderr() {
|
||||
werr!("{}", self.error);
|
||||
process::exit(1);
|
||||
}
|
||||
let out = io::stdout();
|
||||
writeln!(&mut out.lock(), "{}", self.error).expect("Error writing ClapError to stdout");
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ClapError {
|
||||
fn description(&self) -> &str {
|
||||
&*self.error
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match self.error_type {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std_fmt::Display for ClapError {
|
||||
fn fmt(&self, f: &mut std_fmt::Formatter) -> std_fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for ClapError {
|
||||
fn from(e: io::Error) -> Self {
|
||||
ClapError {
|
||||
error: format!("{} {}", Format::Error("error:"), e.description()),
|
||||
error_type: ClapErrorType::Io,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std_fmt::Error> for ClapError {
|
||||
fn from(e: std_fmt::Error) -> Self {
|
||||
ClapError {
|
||||
error: format!("{} {}", Format::Error("error:"), e),
|
||||
error_type: ClapErrorType::Format,
|
||||
}
|
||||
}
|
||||
}
|
83
src/lib.rs
83
src/lib.rs
|
@ -38,31 +38,43 @@
|
|||
//!
|
||||
//! ### Comparisons
|
||||
//!
|
||||
//! First, let me say that these comparisons are highly subjective, and not meant
|
||||
//! First, let me say that these comparisons are highly subjective, and not
|
||||
//! meant
|
||||
//! in a critical or harsh manner. All the argument parsing libraries out there
|
||||
//! (to include `clap`) have their own strengths and weaknesses. Sometimes it just
|
||||
//! comes down to personal taste when all other factors are equal. When in doubt,
|
||||
//! try them all and pick one that you enjoy :) There's plenty of room in the Rust
|
||||
//! (to include `clap`) have their own strengths and weaknesses. Sometimes it
|
||||
//! just
|
||||
//! comes down to personal taste when all other factors are equal. When in
|
||||
//! doubt,
|
||||
//! try them all and pick one that you enjoy :) There's plenty of room in the
|
||||
//! Rust
|
||||
//! community for multiple implementations!
|
||||
//!
|
||||
//! #### How does `clap` compare to `getopts`?
|
||||
//!
|
||||
//! [getopts](https://github.com/rust-lang-nursery/getopts) is a very basic, fairly
|
||||
//! [getopts](https://github.com/rust-lang-nursery/getopts) is a very basic,
|
||||
//! fairly
|
||||
//! minimalist argument parsing library. This isn't a bad thing, sometimes you
|
||||
//! don't need tons of features, you just want to parse some simple arguments, and
|
||||
//! don't need tons of features, you just want to parse some simple arguments,
|
||||
//! and
|
||||
//! have some help text generated for you based on valid arguments you specify.
|
||||
//! When using `getopts` you must manually implement most of the common features
|
||||
//! (such as checking to display help messages, usage strings, etc.). If you want a
|
||||
//! highly custom argument parser, and don't mind writing most the argument parser
|
||||
//! (such as checking to display help messages, usage strings, etc.). If you
|
||||
//! want a
|
||||
//! highly custom argument parser, and don't mind writing most the argument
|
||||
//! parser
|
||||
//! yourself, `getopts` is an excellent base.
|
||||
//!
|
||||
//! Due to it's lack of features, `getopts` also doesn't allocate much, or at all.
|
||||
//! Due to it's lack of features, `getopts` also doesn't allocate much, or at
|
||||
//! all.
|
||||
//! This gives it somewhat of a performance boost. Although, as you start
|
||||
//! implementing those features you need manually, that boost quickly disappears.
|
||||
//! implementing those features you need manually, that boost quickly
|
||||
//! disappears.
|
||||
//!
|
||||
//! Personally, I find many, many people that use `getopts` are manually
|
||||
//! implementing features that `clap` has by default. Using `clap` simplifies your
|
||||
//! codebase allowing you to focus on your application, and not argument parsing.
|
||||
//! implementing features that `clap` has by default. Using `clap` simplifies
|
||||
//! your
|
||||
//! codebase allowing you to focus on your application, and not argument
|
||||
//! parsing.
|
||||
//!
|
||||
//! Reasons to use `getopts` instead of `clap`
|
||||
//!
|
||||
|
@ -75,28 +87,39 @@
|
|||
//!
|
||||
//! I first want to say I'm a big a fan of BurntSushi's work, the creator of
|
||||
//! [Docopt.rs](https://github.com/docopt/docopt.rs). I aspire to produce the
|
||||
//! quality of libraries that this man does! When it comes to comparing these two
|
||||
//! quality of libraries that this man does! When it comes to comparing these
|
||||
//! two
|
||||
//! libraries they are very different. `docopt` tasks you with writing a help
|
||||
//! message, and then it parsers that message for you to determine all valid
|
||||
//! arguments and their use. Some people LOVE this, others not so much. If you're
|
||||
//! willing to write a detailed help message, it's nice that you can stick that in
|
||||
//! your program and have `docopt` do the rest. On the downside, it's somewhat less
|
||||
//! flexible than other options out there, and requires the help message change if
|
||||
//! arguments and their use. Some people LOVE this, others not so much. If
|
||||
//! you're
|
||||
//! willing to write a detailed help message, it's nice that you can stick that
|
||||
//! in
|
||||
//! your program and have `docopt` do the rest. On the downside, it's somewhat
|
||||
//! less
|
||||
//! flexible than other options out there, and requires the help message change
|
||||
//! if
|
||||
//! you need to make changes.
|
||||
//!
|
||||
//! `docopt` is also excellent at translating arguments into Rust types
|
||||
//! automatically. There is even a syntax extension which will do all this for you,
|
||||
//! ifou to manually translate from arguments to Rust types). To use BurntSushi's
|
||||
//! words, `docopt` is also somewhat of a black box. You get what you get, and it's
|
||||
//! automatically. There is even a syntax extension which will do all this for
|
||||
//! you,
|
||||
//! ifou to manually translate from arguments to Rust types). To use
|
||||
//! BurntSushi's
|
||||
//! words, `docopt` is also somewhat of a black box. You get what you get, and
|
||||
//! it's
|
||||
//! hard to tweak implementation or customise your experience for your use case.
|
||||
//!
|
||||
//! Because `docopt` is doing a ton of work to parse your help messages and
|
||||
//! determine what you were trying to communicate as valid arguments, it's also one
|
||||
//! of the more heavy weight parsers performance-wise. For most applications this
|
||||
//! determine what you were trying to communicate as valid arguments, it's also
|
||||
//! one
|
||||
//! of the more heavy weight parsers performance-wise. For most applications
|
||||
//! this
|
||||
//! isn't a concern, but it's something to keep in mind.
|
||||
//!
|
||||
//! Reasons to use `docopt` instead of `clap`
|
||||
//! * You want automatic translation from arguments to Rust types, and are using a
|
||||
//! * You want automatic translation from arguments to Rust types, and are
|
||||
//! using a
|
||||
//! nightly compiler
|
||||
//! * Performance isn't a concern
|
||||
//! * You don't have any complex relationships between arguments
|
||||
|
@ -105,7 +128,8 @@
|
|||
//!
|
||||
//! `clap` is fast, and as lightweight as possible while still giving all the
|
||||
//! features you'd expect from a modern argument parser. If you use `clap` when
|
||||
//! just need some simple arguments parsed, you'll find it a walk in the park. But
|
||||
//! just need some simple arguments parsed, you'll find it a walk in the park.
|
||||
//! But
|
||||
//! `clap` also makes it possible to represent extremely complex, and advanced
|
||||
//! requirements, without too much thought. `clap` aims to be intuitive, easy to
|
||||
//! use, and fully capable for wide variety use cases and needs.
|
||||
|
@ -624,12 +648,13 @@
|
|||
#![cfg_attr(feature = "lints", allow(explicit_iter_loop))]
|
||||
#![cfg_attr(feature = "lints", allow(should_implement_trait))]
|
||||
#![cfg_attr(feature = "lints", deny(warnings))]
|
||||
#![cfg_attr(not(any(feature = "lints", feature = "nightly")), deny(unstable_features))]
|
||||
#![deny(missing_docs,
|
||||
missing_debug_implementations,
|
||||
missing_copy_implementations,
|
||||
trivial_casts, trivial_numeric_casts,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unsafe_code,
|
||||
unstable_features,
|
||||
unused_import_braces,
|
||||
unused_qualifications)]
|
||||
|
||||
|
@ -641,12 +666,14 @@ extern crate ansi_term;
|
|||
extern crate yaml_rust;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate vec_map;
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
pub use yaml_rust::YamlLoader;
|
||||
pub use args::{Arg, ArgGroup, ArgMatches, SubCommand};
|
||||
pub use app::{App, AppSettings, ClapError, ClapErrorType};
|
||||
pub use app::{App, AppSettings};
|
||||
pub use fmt::Format;
|
||||
pub use errors::{ClapError, ClapErrorType};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
@ -654,3 +681,5 @@ mod app;
|
|||
mod args;
|
||||
mod usageparser;
|
||||
mod fmt;
|
||||
mod suggestions;
|
||||
mod errors;
|
||||
|
|
138
src/macros.rs
138
src/macros.rs
|
@ -44,90 +44,7 @@ macro_rules! load_yaml {
|
|||
);
|
||||
}
|
||||
|
||||
macro_rules! write_spaces {
|
||||
($num:expr, $w:ident) => ({
|
||||
for _ in 0..$num {
|
||||
try!(write!($w, " "));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// convenience macro for remove an item from a vec
|
||||
macro_rules! vec_remove {
|
||||
($vec:expr, $to_rem:ident) => {
|
||||
{
|
||||
let mut ix = None;
|
||||
$vec.dedup();
|
||||
for (i, val) in $vec.iter().enumerate() {
|
||||
if val == $to_rem {
|
||||
ix = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(i) = ix {
|
||||
$vec.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! remove_overriden {
|
||||
($me:ident, $name:expr) => ({
|
||||
if let Some(ref o) = $me.opts.get($name) {
|
||||
if let Some(ref ora) = o.requires {
|
||||
for a in ora {
|
||||
vec_remove!($me.required, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.blacklist {
|
||||
for a in ora {
|
||||
vec_remove!($me.blacklist, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.overrides {
|
||||
for a in ora {
|
||||
vec_remove!($me.overrides, a);
|
||||
}
|
||||
}
|
||||
} else if let Some(ref o) = $me.flags.get($name) {
|
||||
if let Some(ref ora) = o.requires {
|
||||
for a in ora {
|
||||
vec_remove!($me.required, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.blacklist {
|
||||
for a in ora {
|
||||
vec_remove!($me.blacklist, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.overrides {
|
||||
for a in ora {
|
||||
vec_remove!($me.overrides, a);
|
||||
}
|
||||
}
|
||||
} else if let Some(p) = $me.positionals_name.get($name) {
|
||||
if let Some(ref o) = $me.positionals_idx.get(p) {
|
||||
if let Some(ref ora) = o.requires {
|
||||
for a in ora {
|
||||
vec_remove!($me.required, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.blacklist {
|
||||
for a in ora {
|
||||
vec_remove!($me.blacklist, a);
|
||||
}
|
||||
}
|
||||
if let Some(ref ora) = o.overrides {
|
||||
for a in ora {
|
||||
vec_remove!($me.overrides, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// De-duplication macro used in src/app.rs
|
||||
// used in src/args/arg_builder/option.rs
|
||||
macro_rules! print_opt_help {
|
||||
($opt:ident, $spc:expr, $w:ident) => {
|
||||
if let Some(h) = $opt.help {
|
||||
|
@ -136,7 +53,7 @@ macro_rules! print_opt_help {
|
|||
if let Some(part) = hel.next() {
|
||||
try!(write!($w, "{}", part));
|
||||
}
|
||||
while let Some(part) = hel.next() {
|
||||
for part in hel {
|
||||
try!(write!($w, "\n"));
|
||||
write_spaces!($spc, $w);
|
||||
try!(write!($w, "{}", part));
|
||||
|
@ -155,38 +72,35 @@ macro_rules! print_opt_help {
|
|||
};
|
||||
}
|
||||
|
||||
// De-duplication macro used in src/app.rs
|
||||
macro_rules! parse_group_reqs {
|
||||
($me:ident, $arg:ident) => {
|
||||
for ag in $me.groups.values() {
|
||||
let mut found = false;
|
||||
for name in ag.args.iter() {
|
||||
if name == &$arg.name {
|
||||
vec_remove!($me.required, name);
|
||||
if let Some(ref reqs) = ag.requires {
|
||||
for r in reqs {
|
||||
$me.required.push(r);
|
||||
}
|
||||
}
|
||||
if let Some(ref bl) = ag.conflicts {
|
||||
for b in bl {
|
||||
$me.blacklist.push(b);
|
||||
}
|
||||
}
|
||||
found = true;
|
||||
// Helper/deduplication macro for printing the correct number of spaces in help messages
|
||||
// used in:
|
||||
// src/args/arg_builder/*.rs
|
||||
// src/app/mod.rs
|
||||
macro_rules! write_spaces {
|
||||
($num:expr, $w:ident) => ({
|
||||
for _ in 0..$num {
|
||||
try!(write!($w, " "));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// convenience macro for remove an item from a vec
|
||||
macro_rules! vec_remove {
|
||||
($vec:expr, $to_rem:ident) => {
|
||||
{
|
||||
let mut ix = None;
|
||||
$vec.dedup();
|
||||
for (i, val) in $vec.iter().enumerate() {
|
||||
if &val == &$to_rem {
|
||||
ix = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if found {
|
||||
for name in ag.args.iter() {
|
||||
if name == &$arg.name { continue }
|
||||
vec_remove!($me.required, name);
|
||||
|
||||
$me.blacklist.push(name);
|
||||
}
|
||||
if let Some(i) = ix {
|
||||
$vec.remove(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks to bluss and flan3002 in #rust IRC
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#[cfg(feature = "suggestions")]
|
||||
use strsim;
|
||||
|
||||
use fmt::Format;
|
||||
|
||||
/// Produces a string from a given list of possible values which is similar to
|
||||
/// the passed in value `v` with a certain confidence.
|
||||
/// Thus in a list of possible values like ["foo", "bar"], the value "fop" will yield
|
||||
|
@ -34,6 +36,34 @@ pub fn did_you_mean<'a, T, I>(_: &str, _: I) -> Option<&'a str>
|
|||
None
|
||||
}
|
||||
|
||||
/// Returns a suffix that can be empty, or is the standard 'did you mean phrase
|
||||
#[cfg_attr(feature = "lints", allow(needless_lifetimes))]
|
||||
pub fn did_you_mean_suffix<'z, T, I>(arg: &str,
|
||||
values: I,
|
||||
style: DidYouMeanMessageStyle)
|
||||
-> (String, Option<&'z str>)
|
||||
where T: AsRef<str> + 'z,
|
||||
I: IntoIterator<Item = &'z T>
|
||||
{
|
||||
match did_you_mean(arg, values) {
|
||||
Some(candidate) => {
|
||||
let mut suffix = "\n\tDid you mean ".to_owned();
|
||||
match style {
|
||||
DidYouMeanMessageStyle::LongFlag =>
|
||||
suffix.push_str(&*format!("{}", Format::Good("--"))),
|
||||
DidYouMeanMessageStyle::EnumValue => suffix.push('\''),
|
||||
}
|
||||
suffix.push_str(&Format::Good(candidate).to_string()[..]);
|
||||
if let DidYouMeanMessageStyle::EnumValue = style {
|
||||
suffix.push('\'');
|
||||
}
|
||||
suffix.push_str(" ?");
|
||||
(suffix, Some(candidate))
|
||||
}
|
||||
None => (String::new(), None),
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper to determine message formatting
|
||||
pub enum DidYouMeanMessageStyle {
|
||||
/// Suggested value is a long flag
|
|
@ -25,7 +25,107 @@ impl<'u> UsageParser<'u> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "lints", allow(while_let_on_iterator))]
|
||||
fn name(&mut self, c: char) -> Option<UsageToken<'u>> {
|
||||
if self.e != 0 {
|
||||
self.e += 1;
|
||||
}
|
||||
self.s = self.e + 1;
|
||||
let closing = match c {
|
||||
'[' => ']',
|
||||
'<' => '>',
|
||||
_ => unreachable!(),
|
||||
};
|
||||
while let Some(c) = self.chars.next() {
|
||||
self.e += 1;
|
||||
if c == closing {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.e > self.usage.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let name = &self.usage[self.s..self.e];
|
||||
|
||||
Some(UsageToken::Name(name,
|
||||
if c == '<' {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "lints", allow(while_let_on_iterator))]
|
||||
fn help(&mut self) -> Option<UsageToken<'u>> {
|
||||
self.s = self.e + 2;
|
||||
self.e = self.usage.len() - 1;
|
||||
|
||||
while let Some(_) = self.chars.next() {
|
||||
continue;
|
||||
}
|
||||
|
||||
Some(UsageToken::Help(&self.usage[self.s..self.e]))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "lints", allow(while_let_on_iterator))]
|
||||
fn long_arg(&mut self) -> Option<UsageToken<'u>> {
|
||||
if self.e != 1 {
|
||||
self.e += 1;
|
||||
}
|
||||
|
||||
self.s = self.e + 1;
|
||||
|
||||
while let Some(c) = self.chars.next() {
|
||||
self.e += 1;
|
||||
if c == ' ' || c == '=' || c == '.' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if self.e > self.usage.len() {
|
||||
return None;
|
||||
} else if self.e == self.usage.len() - 1 {
|
||||
return Some(UsageToken::Long(&self.usage[self.s..]));
|
||||
}
|
||||
|
||||
Some(UsageToken::Long(&self.usage[self.s..self.e]))
|
||||
}
|
||||
|
||||
fn short_arg(&mut self, c: char) -> Option<UsageToken<'u>> {
|
||||
// When short is first don't increment e
|
||||
if self.e != 1 {
|
||||
self.e += 1;
|
||||
}
|
||||
if !c.is_alphanumeric() {
|
||||
return None;
|
||||
}
|
||||
Some(UsageToken::Short(c))
|
||||
}
|
||||
|
||||
fn multiple(&mut self) -> bool {
|
||||
self.e += 1;
|
||||
let mut mult = false;
|
||||
for _ in 0..2 {
|
||||
self.e += 1;
|
||||
match self.chars.next() {
|
||||
// longs consume one '.' so they match '.. ' whereas shorts can
|
||||
// match '...'
|
||||
Some('.') | Some(' ') => {
|
||||
mult = true;
|
||||
}
|
||||
_ => {
|
||||
// if there is no help or following space all we can match is '..'
|
||||
if self.e == self.usage.len() - 1 {
|
||||
mult = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mult
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u> Iterator for UsageParser<'u> {
|
||||
|
@ -35,80 +135,19 @@ impl<'u> Iterator for UsageParser<'u> {
|
|||
loop {
|
||||
match self.chars.next() {
|
||||
Some(c) if c == '[' || c == '<' => {
|
||||
// self.s = self.e + 1;
|
||||
if self.e != 0 {
|
||||
self.e += 1;
|
||||
}
|
||||
self.s = self.e + 1;
|
||||
let closing = match c {
|
||||
'[' => ']',
|
||||
'<' => '>',
|
||||
_ => unreachable!(),
|
||||
};
|
||||
while let Some(c) = self.chars.next() {
|
||||
self.e += 1;
|
||||
if c == closing {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.e > self.usage.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let name = &self.usage[self.s..self.e];
|
||||
|
||||
return Some(UsageToken::Name(name,
|
||||
if c == '<' {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
}));
|
||||
return self.name(c);
|
||||
}
|
||||
Some('\'') => {
|
||||
self.s = self.e + 2;
|
||||
self.e = self.usage.len() - 1;
|
||||
|
||||
while let Some(_) = self.chars.next() {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(UsageToken::Help(&self.usage[self.s..self.e]));
|
||||
return self.help();
|
||||
}
|
||||
Some('-') => {
|
||||
self.e += 1;
|
||||
match self.chars.next() {
|
||||
Some('-') => {
|
||||
if self.e != 1 {
|
||||
self.e += 1;
|
||||
}
|
||||
|
||||
self.s = self.e + 1;
|
||||
|
||||
while let Some(c) = self.chars.next() {
|
||||
self.e += 1;
|
||||
if c == ' ' || c == '=' || c == '.' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.e > self.usage.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.e == self.usage.len() - 1 {
|
||||
return Some(UsageToken::Long(&self.usage[self.s..]));
|
||||
}
|
||||
return Some(UsageToken::Long(&self.usage[self.s..self.e]));
|
||||
return self.long_arg();
|
||||
}
|
||||
Some(c) => {
|
||||
// When short is first don't increment e
|
||||
if self.e != 1 {
|
||||
self.e += 1;
|
||||
}
|
||||
// Short
|
||||
if !c.is_alphanumeric() {
|
||||
return None;
|
||||
}
|
||||
return Some(UsageToken::Short(c));
|
||||
return self.short_arg(c);
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
|
@ -116,26 +155,7 @@ impl<'u> Iterator for UsageParser<'u> {
|
|||
}
|
||||
}
|
||||
Some('.') => {
|
||||
self.e += 1;
|
||||
let mut mult = false;
|
||||
for _ in 0..2 {
|
||||
self.e += 1;
|
||||
match self.chars.next() {
|
||||
// longs consume one '.' so they match '.. ' whereas shorts can
|
||||
// match '...'
|
||||
Some('.') | Some(' ') => {
|
||||
mult = true;
|
||||
}
|
||||
_ => {
|
||||
// if there is no help or following space all we can match is '..'
|
||||
if self.e == self.usage.len() - 1 {
|
||||
mult = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if mult {
|
||||
if self.multiple() {
|
||||
return Some(UsageToken::Multiple);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,11 @@ fn arg_required_else_help() {
|
|||
let result = App::new("arg_required")
|
||||
.setting(AppSettings::ArgRequiredElseHelp)
|
||||
.arg(Arg::with_name("test")
|
||||
.required(true)
|
||||
.index(1))
|
||||
.get_matches_from_safe(vec![""]);
|
||||
assert!(result.is_err());
|
||||
let err = result.err().unwrap();
|
||||
assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument);
|
||||
assert_eq!(err.error_type, ClapErrorType::MissingArgumentOrSubcommand);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -109,4 +108,4 @@ fn app_settings_fromstr() {
|
|||
assert_eq!("subcommandrequiredelsehelp".parse::<AppSettings>().ok().unwrap(), AppSettings::SubcommandRequiredElseHelp);
|
||||
assert_eq!("hidden".parse::<AppSettings>().ok().unwrap(), AppSettings::Hidden);
|
||||
assert!("hahahaha".parse::<AppSettings>().is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ fn group_single_value() {
|
|||
.get_matches_from(vec!["", "-c", "blue"]);
|
||||
assert!(m.is_present("grp"));
|
||||
assert_eq!(m.value_of("grp").unwrap(), "blue");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_single_flag() {
|
||||
let m = App::new("group")
|
||||
.args_from_usage("-f, --flag 'some flag'
|
||||
-c, --color [color] 'some option'")
|
||||
|
@ -34,6 +38,10 @@ fn group_single_value() {
|
|||
.get_matches_from(vec!["", "-f"]);
|
||||
assert!(m.is_present("grp"));
|
||||
assert!(m.value_of("grp").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_empty() {
|
||||
let m = App::new("group")
|
||||
.args_from_usage("-f, --flag 'some flag'
|
||||
-c, --color [color] 'some option'")
|
||||
|
@ -44,6 +52,20 @@ fn group_single_value() {
|
|||
assert!(m.value_of("grp").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_reqired_flags_empty() {
|
||||
let result = App::new("group")
|
||||
.args_from_usage("-f, --flag 'some flag'
|
||||
-c, --color 'some option'")
|
||||
.arg_group(ArgGroup::with_name("grp")
|
||||
.required(true)
|
||||
.add_all(&["flag", "color"]))
|
||||
.get_matches_from_safe(vec![""]);
|
||||
assert!(result.is_err());
|
||||
let err = result.err().unwrap();
|
||||
assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_multi_value_single_arg() {
|
||||
let m = App::new("group")
|
||||
|
|
|
@ -150,17 +150,15 @@ fn conflict_overriden_4() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn require_overriden() {
|
||||
fn pos_required_overridden_by_flag() {
|
||||
let result = App::new("require_overriden")
|
||||
.arg(Arg::with_name("flag")
|
||||
.arg(Arg::with_name("pos")
|
||||
.index(1)
|
||||
.required(true))
|
||||
.arg(Arg::from_usage("-c, --color 'other flag'")
|
||||
.mutually_overrides_with("flag"))
|
||||
.get_matches_from_safe(vec!["", "flag", "-c"]);
|
||||
assert!(result.is_ok());
|
||||
// let err = result.err().unwrap();
|
||||
// assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument);
|
||||
.arg(Arg::from_usage("-c, --color 'some flag'")
|
||||
.mutually_overrides_with("pos"))
|
||||
.get_matches_from_safe(vec!["", "test", "-c"]);
|
||||
assert!(result.is_ok(), "{:?}", result.unwrap_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -202,4 +200,4 @@ fn require_overriden_4() {
|
|||
assert!(result.is_err());
|
||||
let err = result.err().unwrap();
|
||||
assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ fn invalid_unicode_safe() {
|
|||
.get_matches_from_safe(vec![OsString::from_vec(vec![0x20]),
|
||||
OsString::from_vec(vec![0xe9])]);
|
||||
assert!(m.is_err());
|
||||
if let Err(e) = m {
|
||||
assert_eq!(e.error_type, ClapErrorType::InvalidUnicode);
|
||||
if let Err(err) = m {
|
||||
assert_eq!(err.error_type, ClapErrorType::InvalidUnicode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue