fix: Deprecate runtime usage parser

Fixes #8
This commit is contained in:
Ed Page 2021-11-23 09:53:02 -06:00
parent 4c4a2b86a0
commit 3c3a0b71d1
6 changed files with 42 additions and 246 deletions

View file

@ -45,51 +45,6 @@ macro_rules! create_app {
}}; }};
} }
macro_rules! create_app_from_usage {
() => {{
App::new("claptests")
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.arg(Arg::from_usage("-o --option=[opt]... 'tests options'"))
.arg(Arg::from_usage("[positional] 'tests positionals'"))
.arg(Arg::from_usage("-f --flag... 'tests flags'").global(true))
.args(&[
Arg::from_usage("[flag2] -F 'tests flags with exclusions'")
.conflicts_with("flag")
.requires("option2"),
Arg::from_usage(
"[option2] --long-option-2 [option2] 'tests long options with exclusions'",
)
.conflicts_with("option")
.requires("positional2"),
Arg::from_usage("[positional2] 'tests positionals with exclusions'"),
Arg::from_usage("-O --Option [option3] 'tests options with specific value sets'")
.possible_values(OPT3_VALS),
Arg::from_usage("[positional3]... 'tests positionals with specific values'")
.possible_values(POS3_VALS),
Arg::from_usage("--multvals [one] [two] 'Tests multiple values, not mult occs'"),
Arg::from_usage(
"--multvalsmo... [one] [two] 'Tests multiple values, not mult occs'",
),
Arg::from_usage("--minvals2 [minvals]... 'Tests 2 min vals'").min_values(2),
Arg::from_usage("--maxvals3 [maxvals]... 'Tests 3 max vals'").max_values(3),
])
.subcommand(
App::new("subcmd")
.about("tests subcommands")
.version("0.1")
.author("Kevin K. <kbknapp@gmail.com>")
.arg(Arg::from_usage("-o --option [scoption]... 'tests options'"))
.arg(Arg::from_usage("[scpositional] 'tests positionals'")),
)
}};
}
pub fn build_from_usage(c: &mut Criterion) {
c.bench_function("build_from_usage", |b| b.iter(|| create_app_from_usage!()));
}
pub fn build_from_builder(c: &mut Criterion) { pub fn build_from_builder(c: &mut Criterion) {
c.bench_function("build_from_builder", |b| { c.bench_function("build_from_builder", |b| {
b.iter(|| { b.iter(|| {
@ -334,7 +289,6 @@ pub fn parse_complex_with_sc_complex(c: &mut Criterion) {
criterion_group!( criterion_group!(
benches, benches,
build_from_usage,
build_from_builder, build_from_builder,
parse_complex, parse_complex,
parse_complex_with_flag, parse_complex_with_flag,

View file

@ -1271,50 +1271,17 @@ impl<'help> App<'help> {
self self
} }
/// A convenience method for adding a single [argument] from a usage type string. The string /// Deprecated in Issue #8, see [`arg!`][crate::arg!].
/// used follows the same rules and syntax as [`Arg::from_usage`] #[deprecated(since = "3.0.0", note = "Replaced with `arg!`")]
///
/// **NOTE:** The downside to using this method is that you can not set any additional
/// properties of the [`Arg`] other than what [`Arg::from_usage`] supports.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .arg_from_usage("-c --config=<FILE> 'Sets a configuration file to use'")
/// # ;
/// ```
/// [argument]: ./struct.Arg.html
/// [`Arg`]: ./struct.Arg.html
/// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage
pub fn arg_from_usage(self, usage: &'help str) -> Self { pub fn arg_from_usage(self, usage: &'help str) -> Self {
#![allow(deprecated)]
self.arg(Arg::from_usage(usage)) self.arg(Arg::from_usage(usage))
} }
/// Adds multiple [arguments] at once from a usage string, one per line. See /// Deprecated in Issue #8, see [`arg!`][crate::arg!].
/// [`Arg::from_usage`] for details on the syntax and rules supported. #[deprecated(since = "3.0.0", note = "Replaced with `arg!`")]
///
/// **NOTE:** Like [`App::arg_from_usage`] the downside is you only set properties for the
/// [`Arg`]s which [`Arg::from_usage`] supports.
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg};
/// App::new("myprog")
/// .args_from_usage(
/// "-c --config=[FILE] 'Sets a configuration file to use'
/// [debug]... -d 'Sets the debugging level'
/// <FILE> 'The input file to use'"
/// )
/// # ;
/// ```
/// [arguments]: ./struct.Arg.html
/// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage
/// [`App::arg_from_usage`]: ./struct.App.html#method.arg_from_usage
/// [`Arg`]: ./struct.Arg.html
pub fn args_from_usage(mut self, usage: &'help str) -> Self { pub fn args_from_usage(mut self, usage: &'help str) -> Self {
#![allow(deprecated)]
for line in usage.lines() { for line in usage.lines() {
let l = line.trim(); let l = line.trim();
if l.is_empty() { if l.is_empty() {

View file

@ -482,137 +482,8 @@ impl<'help> Arg<'help> {
a a
} }
/// Creates a new instance of [`Arg`] from a usage string. Allows creation of basic settings /// Deprecated in Issue #8, see [`arg!`][crate::arg!].
/// for the [`Arg`]. The syntax is flexible, but there are some rules to follow. #[deprecated(since = "3.0.0", note = "Replaced with `arg!`")]
///
/// **NOTE**: Not all settings may be set using the usage string method. Some properties are
/// only available via the builder pattern.
///
/// **NOTE**: Only ASCII values are officially supported in [`Arg::from_usage`] strings. Some
/// UTF-8 codepoints may work just fine, but this is not guaranteed.
///
/// # Syntax
///
/// Usage strings typically following the form:
///
/// ```notrust
/// [explicit name] [short] [long] [value names] [help string]
/// ```
///
/// This is not a hard rule as the attributes can appear in other orders. There are also
/// several additional sigils which denote additional settings. Below are the details of each
/// portion of the string.
///
/// ### Explicit Name
///
/// This is an optional field, if it's omitted the argument will use one of the additional
/// fields as the name using the following priority order:
///
/// * Explicit Name (This always takes precedence when present)
/// * Long
/// * Short
/// * Value Name
///
/// `clap` determines explicit names as the first string of characters between either `[]` or
/// `<>` where `[]` has the dual notation of meaning the argument is optional, and `<>` meaning
/// the argument is required.
///
/// Explicit names may be followed by:
/// * The multiple denotation `...`
///
/// Example explicit names as follows (`ename` for an optional argument, and `rname` for a
/// required argument):
///
/// ```notrust
/// [ename] -s, --long 'some flag'
/// <rname> -r, --longer 'some other flag'
/// ```
///
/// ### Short
///
/// This is set by placing a single character after a leading `-`.
///
/// Shorts may be followed by
/// * The multiple denotation `...`
/// * An optional comma `,` which is cosmetic only
/// * Value notation
///
/// Example shorts are as follows (`-s`, and `-r`):
///
/// ```notrust
/// -s, --long 'some flag'
/// <rname> -r [val], --longer 'some option'
/// ```
///
/// ### Long
///
/// This is set by placing a word (no spaces) after a leading `--`.
///
/// Shorts may be followed by
/// * The multiple denotation `...`
/// * Value notation
///
/// Example longs are as follows (`--some`, and `--rapid`):
///
/// ```notrust
/// -s, --some 'some flag'
/// --rapid=[FILE] 'some option'
/// ```
///
/// ### Values (Value Notation)
///
/// This is set by placing a word(s) between `[]` or `<>` optionally after `=` (although this
/// is cosmetic only and does not affect functionality). If an explicit name has **not** been
/// set, using `<>` will denote a required argument, and `[]` will denote an optional argument
///
/// Values may be followed by
/// * The multiple denotation `...`
/// * More Value notation
///
/// More than one value will also implicitly set the arguments number of values, i.e. having
/// two values, `--option [val1] [val2]` specifies that in order for option to be satisified it
/// must receive exactly two values
///
/// Example values are as follows (`FILE`, and `SPEED`):
///
/// ```notrust
/// -s, --some [FILE] 'some option'
/// --rapid=<SPEED>... 'some required multiple option'
/// ```
///
/// ### Help String
///
/// The help string is denoted between a pair of single quotes `''` and may contain any
/// characters.
///
/// Example help strings are as follows:
///
/// ```notrust
/// -s, --some [FILE] 'some option'
/// --rapid=<SPEED>... 'some required multiple option'
/// ```
///
/// ### Additional Sigils
///
/// Multiple notation `...` (three consecutive dots/periods) specifies that this argument may
/// be used multiple times. Do not confuse multiple occurrences (`...`) with multiple values.
/// `--option val1 val2` is a single occurrence with multiple values. `--flag --flag` is
/// multiple occurrences (and then you can obviously have instances of both as well)
///
/// # Examples
///
/// ```rust
/// # use clap::{App, Arg};
/// App::new("prog")
/// .args(&[
/// Arg::from_usage("--config <FILE> 'a required file for the configuration and no short'"),
/// Arg::from_usage("-d, --debug... 'turns on debugging information and allows multiples'"),
/// Arg::from_usage("[input] 'an optional input file to use'")
/// ])
/// # ;
/// ```
/// [`Arg`]: ./struct.Arg.html
/// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage
pub fn from_usage(u: &'help str) -> Self { pub fn from_usage(u: &'help str) -> Self {
UsageParser::from_usage(u).parse() UsageParser::from_usage(u).parse()
} }

View file

@ -241,6 +241,8 @@ fn default_value_end(b: u8) -> bool {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#![allow(deprecated)]
use crate::build::{Arg, ArgSettings}; use crate::build::{Arg, ArgSettings};
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
@ -1239,4 +1241,36 @@ mod test {
assert_eq!(a.val_names.iter().collect::<Vec<_>>(), [&"üñíčöĐ€"]); assert_eq!(a.val_names.iter().collect::<Vec<_>>(), [&"üñíčöĐ€"]);
assert_eq!(a.help, Some("hælp")); assert_eq!(a.help, Some("hælp"));
} }
#[test]
fn value_names_building_num_vals_from_usage() {
use crate::App;
let m = App::new("test")
.arg(Arg::from_usage("--pos <who> <what> <why>"))
.try_get_matches_from(vec!["myprog", "--pos", "val1", "val2", "val3"]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind);
let m = m.unwrap();
assert_eq!(
m.values_of("pos").unwrap().collect::<Vec<_>>(),
["val1", "val2", "val3"]
);
}
#[test]
fn issue_665() {
use crate::{App, ErrorKind};
// Verify fix for "arg_from_usage(): required values not being enforced when followed by another option"
let res = App::new("tester")
.arg(Arg::from_usage("-v, --reroll-count=[N] 'Mark the patch series as PATCH vN'"))
.arg(
Arg::from_usage("--subject-prefix [Subject-Prefix] 'Use [Subject-Prefix] instead of the standard [PATCH] prefix'")
.setting(ArgSettings::ForbidEmptyValues)
)
.try_get_matches_from(vec!["test", "--subject-prefix", "-v", "2"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue);
}
} }

View file

@ -1270,21 +1270,6 @@ fn value_names_building_num_vals() {
); );
} }
#[test]
fn value_names_building_num_vals_from_usage() {
let m = App::new("test")
.arg(Arg::from_usage("--pos <who> <what> <why>"))
.try_get_matches_from(vec!["myprog", "--pos", "val1", "val2", "val3"]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind);
let m = m.unwrap();
assert_eq!(
m.values_of("pos").unwrap().collect::<Vec<_>>(),
["val1", "val2", "val3"]
);
}
#[test] #[test]
fn value_names_building_num_vals_for_positional() { fn value_names_building_num_vals_for_positional() {
let m = App::new("test") let m = App::new("test")

View file

@ -435,21 +435,6 @@ fn did_you_mean() {
)); ));
} }
#[test]
fn issue_665() {
// Verify fix for "arg_from_usage(): required values not being enforced when followed by another option"
let res = App::new("tester")
.arg(Arg::from_usage("-v, --reroll-count=[N] 'Mark the patch series as PATCH vN'"))
.arg(
Arg::from_usage("--subject-prefix [Subject-Prefix] 'Use [Subject-Prefix] instead of the standard [PATCH] prefix'")
.setting(ArgSettings::ForbidEmptyValues)
)
.try_get_matches_from(vec!["test", "--subject-prefix", "-v", "2"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue);
}
#[test] #[test]
fn issue_1047_min_zero_vals_default_val() { fn issue_1047_min_zero_vals_default_val() {
let m = App::new("foo") let m = App::new("foo")