WIP. Big reformat

This commit is contained in:
Alena Yuryeva 2018-07-24 19:48:24 +03:00
parent 7e6d403009
commit e1fb98a0c1
38 changed files with 1182 additions and 759 deletions

View file

@ -8,15 +8,17 @@ use clap::{App, Arg};
use test::Bencher;
macro_rules! create_app {
() => ({
() => {{
App::new("claptests")
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.args_from_usage("-f --flag 'tests flags'
.version("0.1")
.about("tests clap library")
.author("Kevin K. <kbknapp@gmail.com>")
.args_from_usage(
"-f --flag 'tests flags'
-o --option=[opt] 'tests options'
[positional] 'tests positional'")
})
[positional] 'tests positional'",
)
}};
}
#[bench]

View file

@ -12,9 +12,9 @@ extern crate test;
use clap::{App, AppSettings, Arg, ArgSettings};
use test::Bencher;
use std::collections::HashMap;
use std::io::Cursor;
use test::Bencher;
#[bench]
fn build_app_short(b: &mut Bencher) { b.iter(|| app_short()); }
@ -410,306 +410,451 @@ macro_rules! doc {
doc!($map, $name, $short, $short)
};
($map:expr, $name:expr, $short:expr, $long:expr) => {
$map.insert($name, Usage {
short: $short,
long: concat!($long, "\n "),
});
$map.insert(
$name,
Usage {
short: $short,
long: concat!($long, "\n "),
},
);
};
}
lazy_static! {
static ref USAGES: HashMap<&'static str, Usage> = {
let mut h = HashMap::new();
doc!(h, "help-short",
"Show short help output.",
"Show short help output. Use --help to show more details.");
doc!(h, "help",
"Show verbose help output.",
"When given, more details about flags are provided.");
doc!(h, "version",
"Prints version information.");
doc!(
h,
"help-short",
"Show short help output.",
"Show short help output. Use --help to show more details."
);
doc!(
h,
"help",
"Show verbose help output.",
"When given, more details about flags are provided."
);
doc!(h, "version", "Prints version information.");
doc!(h, "pattern",
"A regular expression used for searching.",
"A regular expression used for searching. Multiple patterns \
may be given. To match a pattern beginning with a -, use [-].");
doc!(h, "regexp",
"A regular expression used for searching.",
"A regular expression used for searching. Multiple patterns \
may be given. To match a pattern beginning with a -, use [-].");
doc!(h, "path",
"A file or directory to search.",
"A file or directory to search. Directories are searched \
recursively.");
doc!(h, "files",
"Print each file that would be searched.",
"Print each file that would be searched without actually \
performing the search. This is useful to determine whether a \
particular file is being searched or not.");
doc!(h, "type-list",
"Show all supported file types.",
"Show all supported file types and their corresponding globs.");
doc!(
h,
"pattern",
"A regular expression used for searching.",
"A regular expression used for searching. Multiple patterns \
may be given. To match a pattern beginning with a -, use [-]."
);
doc!(
h,
"regexp",
"A regular expression used for searching.",
"A regular expression used for searching. Multiple patterns \
may be given. To match a pattern beginning with a -, use [-]."
);
doc!(
h,
"path",
"A file or directory to search.",
"A file or directory to search. Directories are searched \
recursively."
);
doc!(
h,
"files",
"Print each file that would be searched.",
"Print each file that would be searched without actually \
performing the search. This is useful to determine whether a \
particular file is being searched or not."
);
doc!(
h,
"type-list",
"Show all supported file types.",
"Show all supported file types and their corresponding globs."
);
doc!(h, "text",
"Search binary files as if they were text.");
doc!(h, "count",
"Only show count of matches for each file.");
doc!(h, "color",
"When to use color. [default: auto]",
"When to use color in the output. The possible values are \
never, auto, always or ansi. The default is auto. When always \
is used, coloring is attempted based on your environment. When \
ansi used, coloring is forcefully done using ANSI escape color \
codes.");
doc!(h, "colors",
"Configure color settings and styles.",
"This flag specifies color settings for use in the output. \
This flag may be provided multiple times. Settings are applied \
iteratively. Colors are limited to one of eight choices: \
red, blue, green, cyan, magenta, yellow, white and black. \
Styles are limited to nobold, bold, nointense or intense.\n\n\
The format of the flag is {type}:{attribute}:{value}. {type} \
should be one of path, line or match. {attribute} can be fg, bg \
or style. {value} is either a color (for fg and bg) or a text \
style. A special format, {type}:none, will clear all color \
settings for {type}.\n\nFor example, the following command will \
change the match color to magenta and the background color for \
line numbers to yellow:\n\n\
rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo.");
doc!(h, "fixed-strings",
"Treat the pattern as a literal string.",
"Treat the pattern as a literal string instead of a regular \
expression. When this flag is used, special regular expression \
meta characters such as (){}*+. do not need to be escaped.");
doc!(h, "glob",
"Include or exclude files/directories.",
"Include or exclude files/directories for searching that \
match the given glob. This always overrides any other \
ignore logic. Multiple glob flags may be used. Globbing \
rules match .gitignore globs. Precede a glob with a ! \
to exclude it.");
doc!(h, "ignore-case",
"Case insensitive search.",
"Case insensitive search. This is overridden by \
--case-sensitive.");
doc!(h, "line-number",
"Show line numbers.",
"Show line numbers (1-based). This is enabled by default when \
searching in a tty.");
doc!(h, "no-line-number",
"Suppress line numbers.",
"Suppress line numbers. This is enabled by default when NOT \
searching in a tty.");
doc!(h, "quiet",
"Do not print anything to stdout.",
"Do not print anything to stdout. If a match is found in a file, \
stop searching. This is useful when ripgrep is used only for \
its exit code.");
doc!(h, "type",
"Only search files matching TYPE.",
"Only search files matching TYPE. Multiple type flags may be \
provided. Use the --type-list flag to list all available \
types.");
doc!(h, "type-not",
"Do not search files matching TYPE.",
"Do not search files matching TYPE. Multiple type-not flags may \
be provided. Use the --type-list flag to list all available \
types.");
doc!(h, "unrestricted",
"Reduce the level of \"smart\" searching.",
"Reduce the level of \"smart\" searching. A single -u \
won't respect .gitignore (etc.) files. Two -u flags will \
additionally search hidden files and directories. Three \
-u flags will additionally search binary files. -uu is \
roughly equivalent to grep -r and -uuu is roughly \
equivalent to grep -a -r.");
doc!(h, "invert-match",
"Invert matching.",
"Invert matching. Show lines that don't match given patterns.");
doc!(h, "word-regexp",
"Only show matches surrounded by word boundaries.",
"Only show matches surrounded by word boundaries. This is \
equivalent to putting \\b before and after all of the search \
patterns.");
doc!(h, "text", "Search binary files as if they were text.");
doc!(h, "count", "Only show count of matches for each file.");
doc!(
h,
"color",
"When to use color. [default: auto]",
"When to use color in the output. The possible values are \
never, auto, always or ansi. The default is auto. When always \
is used, coloring is attempted based on your environment. When \
ansi used, coloring is forcefully done using ANSI escape color \
codes."
);
doc!(
h,
"colors",
"Configure color settings and styles.",
"This flag specifies color settings for use in the output. \
This flag may be provided multiple times. Settings are applied \
iteratively. Colors are limited to one of eight choices: \
red, blue, green, cyan, magenta, yellow, white and black. \
Styles are limited to nobold, bold, nointense or intense.\n\n\
The format of the flag is {type}:{attribute}:{value}. {type} \
should be one of path, line or match. {attribute} can be fg, bg \
or style. {value} is either a color (for fg and bg) or a text \
style. A special format, {type}:none, will clear all color \
settings for {type}.\n\nFor example, the following command will \
change the match color to magenta and the background color for \
line numbers to yellow:\n\n\
rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo."
);
doc!(
h,
"fixed-strings",
"Treat the pattern as a literal string.",
"Treat the pattern as a literal string instead of a regular \
expression. When this flag is used, special regular expression \
meta characters such as (){}*+. do not need to be escaped."
);
doc!(
h,
"glob",
"Include or exclude files/directories.",
"Include or exclude files/directories for searching that \
match the given glob. This always overrides any other \
ignore logic. Multiple glob flags may be used. Globbing \
rules match .gitignore globs. Precede a glob with a ! \
to exclude it."
);
doc!(
h,
"ignore-case",
"Case insensitive search.",
"Case insensitive search. This is overridden by \
--case-sensitive."
);
doc!(
h,
"line-number",
"Show line numbers.",
"Show line numbers (1-based). This is enabled by default when \
searching in a tty."
);
doc!(
h,
"no-line-number",
"Suppress line numbers.",
"Suppress line numbers. This is enabled by default when NOT \
searching in a tty."
);
doc!(
h,
"quiet",
"Do not print anything to stdout.",
"Do not print anything to stdout. If a match is found in a file, \
stop searching. This is useful when ripgrep is used only for \
its exit code."
);
doc!(
h,
"type",
"Only search files matching TYPE.",
"Only search files matching TYPE. Multiple type flags may be \
provided. Use the --type-list flag to list all available \
types."
);
doc!(
h,
"type-not",
"Do not search files matching TYPE.",
"Do not search files matching TYPE. Multiple type-not flags may \
be provided. Use the --type-list flag to list all available \
types."
);
doc!(
h,
"unrestricted",
"Reduce the level of \"smart\" searching.",
"Reduce the level of \"smart\" searching. A single -u \
won't respect .gitignore (etc.) files. Two -u flags will \
additionally search hidden files and directories. Three \
-u flags will additionally search binary files. -uu is \
roughly equivalent to grep -r and -uuu is roughly \
equivalent to grep -a -r."
);
doc!(
h,
"invert-match",
"Invert matching.",
"Invert matching. Show lines that don't match given patterns."
);
doc!(
h,
"word-regexp",
"Only show matches surrounded by word boundaries.",
"Only show matches surrounded by word boundaries. This is \
equivalent to putting \\b before and after all of the search \
patterns."
);
doc!(h, "after-context",
"Show NUM lines after each match.");
doc!(h, "before-context",
"Show NUM lines before each match.");
doc!(h, "context",
"Show NUM lines before and after each match.");
doc!(h, "column",
"Show column numbers",
"Show column numbers (1-based). This only shows the column \
numbers for the first match on each line. This does not try \
to account for Unicode. One byte is equal to one column. This \
implies --line-number.");
doc!(h, "context-separator",
"Set the context separator string. [default: --]",
"The string used to separate non-contiguous context lines in the \
output. Escape sequences like \\x7F or \\t may be used. The \
default value is --.");
doc!(h, "debug",
"Show debug messages.",
"Show debug messages. Please use this when filing a bug report.");
doc!(h, "file",
"Search for patterns from the given file.",
"Search for patterns from the given file, with one pattern per \
line. When this flag is used or multiple times or in \
combination with the -e/--regexp flag, then all patterns \
provided are searched. Empty pattern lines will match all input \
lines, and the newline is not counted as part of the pattern.");
doc!(h, "files-with-matches",
"Only show the path of each file with at least one match.");
doc!(h, "files-without-match",
"Only show the path of each file that contains zero matches.");
doc!(h, "with-filename",
"Show file name for each match.",
"Prefix each match with the file name that contains it. This is \
the default when more than one file is searched.");
doc!(h, "no-filename",
"Never show the file name for a match.",
"Never show the file name for a match. This is the default when \
one file is searched.");
doc!(h, "heading",
"Show matches grouped by each file.",
"This shows the file name above clusters of matches from each \
file instead of showing the file name for every match. This is \
the default mode at a tty.");
doc!(h, "no-heading",
"Don't group matches by each file.",
"Don't group matches by each file. If -H/--with-filename is \
enabled, then file names will be shown for every line matched. \
This is the default mode when not at a tty.");
doc!(h, "hidden",
"Search hidden files and directories.",
"Search hidden files and directories. By default, hidden files \
and directories are skipped.");
doc!(h, "ignore-file",
"Specify additional ignore files.",
"Specify additional ignore files for filtering file paths. \
Ignore files should be in the gitignore format and are matched \
relative to the current working directory. These ignore files \
have lower precedence than all other ignore files. When \
specifying multiple ignore files, earlier files have lower \
precedence than later files.");
doc!(h, "follow",
"Follow symbolic links.");
doc!(h, "max-count",
"Limit the number of matches.",
"Limit the number of matching lines per file searched to NUM.");
doc!(h, "maxdepth",
"Descend at most NUM directories.",
"Limit the depth of directory traversal to NUM levels beyond \
the paths given. A value of zero only searches the \
starting-points themselves.\n\nFor example, \
'rg --maxdepth 0 dir/' is a no-op because dir/ will not be \
descended into. 'rg --maxdepth 1 dir/' will search only the \
direct children of dir/.");
doc!(h, "mmap",
"Searching using memory maps when possible.",
"Search using memory maps when possible. This is enabled by \
default when ripgrep thinks it will be faster. Note that memory \
map searching doesn't currently support all options, so if an \
incompatible option (e.g., --context) is given with --mmap, \
then memory maps will not be used.");
doc!(h, "no-messages",
"Suppress all error messages.",
"Suppress all error messages. This is equivalent to redirecting \
stderr to /dev/null.");
doc!(h, "no-mmap",
"Never use memory maps.",
"Never use memory maps, even when they might be faster.");
doc!(h, "no-ignore",
"Don't respect ignore files.",
"Don't respect ignore files (.gitignore, .ignore, etc.). This \
implies --no-ignore-parent and --no-ignore-vcs.");
doc!(h, "no-ignore-parent",
"Don't respect ignore files in parent directories.",
"Don't respect ignore files (.gitignore, .ignore, etc.) in \
parent directories.");
doc!(h, "no-ignore-vcs",
"Don't respect VCS ignore files",
"Don't respect version control ignore files (.gitignore, etc.). \
This implies --no-ignore-parent. Note that .ignore files will \
continue to be respected.");
doc!(h, "null",
"Print NUL byte after file names",
"Whenever a file name is printed, follow it with a NUL byte. \
This includes printing file names before matches, and when \
printing a list of matching files such as with --count, \
--files-with-matches and --files. This option is useful for use \
with xargs.");
doc!(h, "path-separator",
"Path separator to use when printing file paths.",
"The path separator to use when printing file paths. This \
defaults to your platform's path separator, which is / on Unix \
and \\ on Windows. This flag is intended for overriding the \
default when the environment demands it (e.g., cygwin). A path \
separator is limited to a single byte.");
doc!(h, "pretty",
"Alias for --color always --heading -n.");
doc!(h, "replace",
"Replace matches with string given.",
"Replace every match with the string given when printing \
results. Neither this flag nor any other flag will modify your \
files.\n\nCapture group indices (e.g., $5) and names \
(e.g., $foo) are supported in the replacement string.\n\n\
Note that the replacement by default replaces each match, and \
NOT the entire line. To replace the entire line, you should \
match the entire line.");
doc!(h, "case-sensitive",
"Search case sensitively.",
"Search case sensitively. This overrides -i/--ignore-case and \
-S/--smart-case.");
doc!(h, "smart-case",
"Smart case search.",
"Searches case insensitively if the pattern is all lowercase. \
Search case sensitively otherwise. This is overridden by \
either -s/--case-sensitive or -i/--ignore-case.");
doc!(h, "sort-files",
"Sort results by file path. Implies --threads=1.",
"Sort results by file path. Note that this currently \
disables all parallelism and runs search in a single thread.");
doc!(h, "threads",
"The approximate number of threads to use.",
"The approximate number of threads to use. A value of 0 (which \
is the default) causes ripgrep to choose the thread count \
using heuristics.");
doc!(h, "vimgrep",
"Show results in vim compatible format.",
"Show results with every match on its own line, including \
line numbers and column numbers. With this option, a line with \
more than one match will be printed more than once.");
doc!(h, "after-context", "Show NUM lines after each match.");
doc!(h, "before-context", "Show NUM lines before each match.");
doc!(h, "context", "Show NUM lines before and after each match.");
doc!(
h,
"column",
"Show column numbers",
"Show column numbers (1-based). This only shows the column \
numbers for the first match on each line. This does not try \
to account for Unicode. One byte is equal to one column. This \
implies --line-number."
);
doc!(
h,
"context-separator",
"Set the context separator string. [default: --]",
"The string used to separate non-contiguous context lines in the \
output. Escape sequences like \\x7F or \\t may be used. The \
default value is --."
);
doc!(
h,
"debug",
"Show debug messages.",
"Show debug messages. Please use this when filing a bug report."
);
doc!(
h,
"file",
"Search for patterns from the given file.",
"Search for patterns from the given file, with one pattern per \
line. When this flag is used or multiple times or in \
combination with the -e/--regexp flag, then all patterns \
provided are searched. Empty pattern lines will match all input \
lines, and the newline is not counted as part of the pattern."
);
doc!(
h,
"files-with-matches",
"Only show the path of each file with at least one match."
);
doc!(
h,
"files-without-match",
"Only show the path of each file that contains zero matches."
);
doc!(
h,
"with-filename",
"Show file name for each match.",
"Prefix each match with the file name that contains it. This is \
the default when more than one file is searched."
);
doc!(
h,
"no-filename",
"Never show the file name for a match.",
"Never show the file name for a match. This is the default when \
one file is searched."
);
doc!(
h,
"heading",
"Show matches grouped by each file.",
"This shows the file name above clusters of matches from each \
file instead of showing the file name for every match. This is \
the default mode at a tty."
);
doc!(
h,
"no-heading",
"Don't group matches by each file.",
"Don't group matches by each file. If -H/--with-filename is \
enabled, then file names will be shown for every line matched. \
This is the default mode when not at a tty."
);
doc!(
h,
"hidden",
"Search hidden files and directories.",
"Search hidden files and directories. By default, hidden files \
and directories are skipped."
);
doc!(
h,
"ignore-file",
"Specify additional ignore files.",
"Specify additional ignore files for filtering file paths. \
Ignore files should be in the gitignore format and are matched \
relative to the current working directory. These ignore files \
have lower precedence than all other ignore files. When \
specifying multiple ignore files, earlier files have lower \
precedence than later files."
);
doc!(h, "follow", "Follow symbolic links.");
doc!(
h,
"max-count",
"Limit the number of matches.",
"Limit the number of matching lines per file searched to NUM."
);
doc!(
h,
"maxdepth",
"Descend at most NUM directories.",
"Limit the depth of directory traversal to NUM levels beyond \
the paths given. A value of zero only searches the \
starting-points themselves.\n\nFor example, \
'rg --maxdepth 0 dir/' is a no-op because dir/ will not be \
descended into. 'rg --maxdepth 1 dir/' will search only the \
direct children of dir/."
);
doc!(
h,
"mmap",
"Searching using memory maps when possible.",
"Search using memory maps when possible. This is enabled by \
default when ripgrep thinks it will be faster. Note that memory \
map searching doesn't currently support all options, so if an \
incompatible option (e.g., --context) is given with --mmap, \
then memory maps will not be used."
);
doc!(
h,
"no-messages",
"Suppress all error messages.",
"Suppress all error messages. This is equivalent to redirecting \
stderr to /dev/null."
);
doc!(
h,
"no-mmap",
"Never use memory maps.",
"Never use memory maps, even when they might be faster."
);
doc!(
h,
"no-ignore",
"Don't respect ignore files.",
"Don't respect ignore files (.gitignore, .ignore, etc.). This \
implies --no-ignore-parent and --no-ignore-vcs."
);
doc!(
h,
"no-ignore-parent",
"Don't respect ignore files in parent directories.",
"Don't respect ignore files (.gitignore, .ignore, etc.) in \
parent directories."
);
doc!(
h,
"no-ignore-vcs",
"Don't respect VCS ignore files",
"Don't respect version control ignore files (.gitignore, etc.). \
This implies --no-ignore-parent. Note that .ignore files will \
continue to be respected."
);
doc!(
h,
"null",
"Print NUL byte after file names",
"Whenever a file name is printed, follow it with a NUL byte. \
This includes printing file names before matches, and when \
printing a list of matching files such as with --count, \
--files-with-matches and --files. This option is useful for use \
with xargs."
);
doc!(
h,
"path-separator",
"Path separator to use when printing file paths.",
"The path separator to use when printing file paths. This \
defaults to your platform's path separator, which is / on Unix \
and \\ on Windows. This flag is intended for overriding the \
default when the environment demands it (e.g., cygwin). A path \
separator is limited to a single byte."
);
doc!(h, "pretty", "Alias for --color always --heading -n.");
doc!(
h,
"replace",
"Replace matches with string given.",
"Replace every match with the string given when printing \
results. Neither this flag nor any other flag will modify your \
files.\n\nCapture group indices (e.g., $5) and names \
(e.g., $foo) are supported in the replacement string.\n\n\
Note that the replacement by default replaces each match, and \
NOT the entire line. To replace the entire line, you should \
match the entire line."
);
doc!(
h,
"case-sensitive",
"Search case sensitively.",
"Search case sensitively. This overrides -i/--ignore-case and \
-S/--smart-case."
);
doc!(
h,
"smart-case",
"Smart case search.",
"Searches case insensitively if the pattern is all lowercase. \
Search case sensitively otherwise. This is overridden by \
either -s/--case-sensitive or -i/--ignore-case."
);
doc!(
h,
"sort-files",
"Sort results by file path. Implies --threads=1.",
"Sort results by file path. Note that this currently \
disables all parallelism and runs search in a single thread."
);
doc!(
h,
"threads",
"The approximate number of threads to use.",
"The approximate number of threads to use. A value of 0 (which \
is the default) causes ripgrep to choose the thread count \
using heuristics."
);
doc!(
h,
"vimgrep",
"Show results in vim compatible format.",
"Show results with every match on its own line, including \
line numbers and column numbers. With this option, a line with \
more than one match will be printed more than once."
);
doc!(h, "type-add",
"Add a new glob for a file type.",
"Add a new glob for a particular file type. Only one glob can be \
added at a time. Multiple --type-add flags can be provided. \
Unless --type-clear is used, globs are added to any existing \
globs defined inside of ripgrep.\n\nNote that this MUST be \
passed to every invocation of ripgrep. Type settings are NOT \
persisted.\n\nExample: \
rg --type-add 'foo:*.foo' -tfoo PATTERN.\n\n\
--type-add can also be used to include rules from other types \
with the special include directive. The include directive \
permits specifying one or more other type names (separated by a \
comma) that have been defined and its rules will automatically \
be imported into the type specified. For example, to create a \
type called src that matches C++, Python and Markdown files, one \
can use:\n\n\
--type-add 'src:include:cpp,py,md'\n\n\
Additional glob rules can still be added to the src type by \
using the --type-add flag again:\n\n\
--type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'\n\n\
Note that type names must consist only of Unicode letters or \
numbers. Punctuation characters are not allowed.");
doc!(h, "type-clear",
"Clear globs for given file type.",
"Clear the file type globs previously defined for TYPE. This \
only clears the default type definitions that are found inside \
of ripgrep.\n\nNote that this MUST be passed to every \
invocation of ripgrep. Type settings are NOT persisted.");
doc!(
h,
"type-add",
"Add a new glob for a file type.",
"Add a new glob for a particular file type. Only one glob can be \
added at a time. Multiple --type-add flags can be provided. \
Unless --type-clear is used, globs are added to any existing \
globs defined inside of ripgrep.\n\nNote that this MUST be \
passed to every invocation of ripgrep. Type settings are NOT \
persisted.\n\nExample: \
rg --type-add 'foo:*.foo' -tfoo PATTERN.\n\n\
--type-add can also be used to include rules from other types \
with the special include directive. The include directive \
permits specifying one or more other type names (separated by a \
comma) that have been defined and its rules will automatically \
be imported into the type specified. For example, to create a \
type called src that matches C++, Python and Markdown files, one \
can use:\n\n\
--type-add 'src:include:cpp,py,md'\n\n\
Additional glob rules can still be added to the src type by \
using the --type-add flag again:\n\n\
--type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'\n\n\
Note that type names must consist only of Unicode letters or \
numbers. Punctuation characters are not allowed."
);
doc!(
h,
"type-clear",
"Clear globs for given file type.",
"Clear the file type globs previously defined for TYPE. This \
only clears the default type definitions that are found inside \
of ripgrep.\n\nNote that this MUST be passed to every \
invocation of ripgrep. Type settings are NOT persisted."
);
h
};

View file

@ -106,7 +106,7 @@ where
#[doc(hidden)]
pub g_settings: AppFlags,
#[doc(hidden)]
pub args: MKeyMap,
pub args: MKeyMap<'a, 'b>,
#[doc(hidden)]
pub subcommands: Vec<App<'a, 'b>>,
#[doc(hidden)]
@ -990,7 +990,7 @@ impl<'a, 'b> App<'a, 'b> {
{
let i = self
.args
.iter()
.values()
.enumerate()
.filter_map(|(i, a)| if a.name == arg { Some(i) } else { None })
.next();
@ -1412,7 +1412,7 @@ impl<'a, 'b> App<'a, 'b> {
let global_arg_vec: Vec<&str> = (&self)
.args
.iter()
.values()
.filter(|a| a.is_set(ArgSettings::Global))
.map(|ga| ga.name)
.collect();
@ -1446,7 +1446,7 @@ impl<'a, 'b> App<'a, 'b> {
}
true
});
//TODO add .values_mut() for MKeyMap
for a in self.args.values_mut() {
// Fill in the groups
if let Some(ref grps) = a.groups {
@ -1483,16 +1483,18 @@ impl<'a, 'b> App<'a, 'b> {
debugln!("App::app_debug_asserts;");
// * Args listed inside groups should exist
// * Groups should not have naming conflicts with Args
let g = groups!(self).find(|g| {
g.args
.iter()
.any(|arg| !(find!(self, arg).is_some() || groups!(self).any(|g| &g.name == arg)))
});
assert!(
g.is_none(),
"The group '{}' contains an arg that doesn't exist or has a naming conflict with a group.",
g.unwrap().name
);
// * Will be removed as a part of removing String types
// let g = groups!(self).find(|g| {
// g.args
// .iter()
// .any(|arg| !(find!(self, arg).is_some() || groups!(self).any(|g| &g.name == arg)))
// });
// assert!(
// g.is_none(),
// "The group '{}' contains an arg that doesn't exist or has a naming conflict with a group.",
// g.unwrap().name
// );
true
}
@ -1519,7 +1521,7 @@ impl<'a, 'b> App<'a, 'b> {
sc.max_w = self.max_w;
}
{
for a in self.args.iter().filter(|a| a.is_set(ArgSettings::Global)) {
for a in self.args.values().filter(|a| a.is_set(ArgSettings::Global)) {
sc.args.push(a.clone());
}
}
@ -1744,9 +1746,11 @@ impl<'a, 'b> App<'a, 'b> {
ColorWhen::Auto
}
}
pub(crate) fn contains_long(&self, l: &str) -> bool { longs!(self).any(|al| al == l) }
pub(crate) fn contains_long(&self, l: &str) -> bool {
longs!(self).any(|&al| al == OsString::from(l).as_os_str())
}
pub(crate) fn contains_short(&self, s: char) -> bool { shorts!(self).any(|arg_s| arg_s == s) }
pub(crate) fn contains_short(&self, s: char) -> bool { shorts!(self).any(|&arg_s| arg_s == s) }
pub fn is_set(&self, s: AppSettings) -> bool {
self.settings.is_set(s) || self.g_settings.is_set(s)
@ -1770,9 +1774,13 @@ impl<'a, 'b> App<'a, 'b> {
pub fn has_positionals(&self) -> bool { positionals!(self).count() > 0 }
pub fn has_visible_opts(&self) -> bool { opts!(self).any(|o| !o.is_set(ArgSettings::Hidden)) }
pub fn has_visible_opts(&self) -> bool {
opts!(self).any(|(k, o)| !o.is_set(ArgSettings::Hidden))
}
pub fn has_visible_flags(&self) -> bool { flags!(self).any(|o| !o.is_set(ArgSettings::Hidden)) }
pub fn has_visible_flags(&self) -> bool {
flags!(self).any(|(k, o)| !o.is_set(ArgSettings::Hidden))
}
pub fn has_visible_positionals(&self) -> bool {
positionals!(self).any(|o| !o.is_set(ArgSettings::Hidden))

View file

@ -1,8 +1,8 @@
// Std
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::str::FromStr;
use std::ops::BitOr;
use std::str::FromStr;
bitflags! {
struct Flags: u64 {
@ -62,8 +62,11 @@ impl BitOr for AppFlags {
impl Default for AppFlags {
fn default() -> Self {
AppFlags(
Flags::NEEDS_LONG_VERSION | Flags::NEEDS_LONG_HELP | Flags::NEEDS_SC_HELP
| Flags::UTF8_NONE | Flags::COLOR_AUTO,
Flags::NEEDS_LONG_VERSION
| Flags::NEEDS_LONG_HELP
| Flags::NEEDS_SC_HELP
| Flags::UTF8_NONE
| Flags::COLOR_AUTO,
)
}
}
@ -713,7 +716,10 @@ pub enum AppSettings {
///
/// assert!(m.subcommand_matches("foo").is_none());
/// ```
#[deprecated(since = "2.27.0", note = "No longer required to propagate values")]
#[deprecated(
since = "2.27.0",
note = "No longer required to propagate values"
)]
PropagateGlobalValuesDown,
/// Allows [`SubCommand`]s to override all requirements of the parent command.
@ -925,23 +931,32 @@ pub enum AppSettings {
/// [`SubCommand`]: ./struct.SubCommand.html
WaitOnError,
#[doc(hidden)] NeedsLongVersion,
#[doc(hidden)]
NeedsLongVersion,
#[doc(hidden)] NeedsLongHelp,
#[doc(hidden)]
NeedsLongHelp,
#[doc(hidden)] NeedsSubcommandHelp,
#[doc(hidden)]
NeedsSubcommandHelp,
#[doc(hidden)] LowIndexMultiplePositional,
#[doc(hidden)]
LowIndexMultiplePositional,
#[doc(hidden)] TrailingValues,
#[doc(hidden)]
TrailingValues,
#[doc(hidden)] ValidNegNumFound,
#[doc(hidden)]
ValidNegNumFound,
#[doc(hidden)] Propagated,
#[doc(hidden)]
Propagated,
#[doc(hidden)] ValidArgFound,
#[doc(hidden)]
ValidArgFound,
#[doc(hidden)] ContainsLast,
#[doc(hidden)]
ContainsLast,
}
impl FromStr for AppSettings {

View file

@ -86,7 +86,7 @@ pub enum ArgSettings {
AllowEmptyValues,
/// Sets an arg to be global (i.e. exist in all subcommands)
/// **DEPRECATED**
#[deprecated(since="2.32.0", note="Use `App::global_arg` instead")]
#[deprecated(since = "2.32.0", note = "Use `App::global_arg` instead")]
Global,
/// Hides an arg from the help message
Hidden,
@ -119,8 +119,10 @@ pub enum ArgSettings {
HiddenShortHelp,
/// The argument should **not** be shown in long help text
HiddenLongHelp,
#[doc(hidden)] RequiredUnlessAll,
#[doc(hidden)] ValueDelimiterNotSet,
#[doc(hidden)]
RequiredUnlessAll,
#[doc(hidden)]
ValueDelimiterNotSet,
}
impl FromStr for ArgSettings {

View file

@ -129,7 +129,9 @@ impl<'a> ArgGroup<'a> {
/// # }
/// ```
#[cfg(feature = "yaml")]
pub fn from_yaml(y: &'a yaml_rust::Yaml) -> ArgGroup<'a> { ArgGroup::from(y.as_hash().unwrap()) }
pub fn from_yaml(y: &'a yaml_rust::Yaml) -> ArgGroup<'a> {
ArgGroup::from(y.as_hash().unwrap())
}
/// Adds an [argument] to this group by name
///

View file

@ -1,72 +1,75 @@
#[cfg(feature = "yaml")]
macro_rules! yaml_tuple2 {
($a:ident, $v:ident, $c:ident) => {{
if let Some(vec) = $v.as_vec() {
for ys in vec {
if let Some(tup) = ys.as_vec() {
debug_assert_eq!(2, tup.len());
$a = $a.$c(yaml_str!(tup[0]), yaml_str!(tup[1]));
} else {
panic!("Failed to convert YAML value to vec");
}
if let Some(vec) = $v.as_vec() {
for ys in vec {
if let Some(tup) = ys.as_vec() {
debug_assert_eq!(2, tup.len());
$a = $a.$c(yaml_str!(tup[0]), yaml_str!(tup[1]));
} else {
panic!("Failed to convert YAML value to vec");
}
} else {
panic!("Failed to convert YAML value to vec");
}
$a
} else {
panic!("Failed to convert YAML value to vec");
}
};
$a
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_tuple3 {
($a:ident, $v:ident, $c:ident) => {{
if let Some(vec) = $v.as_vec() {
for ys in vec {
if let Some(tup) = ys.as_vec() {
debug_assert_eq!(3, tup.len());
$a = $a.$c(yaml_str!(tup[0]), yaml_opt_str!(tup[1]), yaml_str!(tup[2]));
} else {
panic!("Failed to convert YAML value to vec");
}
if let Some(vec) = $v.as_vec() {
for ys in vec {
if let Some(tup) = ys.as_vec() {
debug_assert_eq!(3, tup.len());
$a = $a.$c(yaml_str!(tup[0]), yaml_opt_str!(tup[1]), yaml_str!(tup[2]));
} else {
panic!("Failed to convert YAML value to vec");
}
} else {
panic!("Failed to convert YAML value to vec");
}
$a
} else {
panic!("Failed to convert YAML value to vec");
}
};
$a
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_vec_or_str {
($v:ident, $a:ident, $c:ident) => {{
let maybe_vec = $v.as_vec();
if let Some(vec) = maybe_vec {
for ys in vec {
if let Some(s) = ys.as_str() {
$a = $a.$c(s);
} else {
panic!("Failed to convert YAML value {:?} to a string", ys);
}
}
} else {
if let Some(s) = $v.as_str() {
let maybe_vec = $v.as_vec();
if let Some(vec) = maybe_vec {
for ys in vec {
if let Some(s) = ys.as_str() {
$a = $a.$c(s);
} else {
panic!("Failed to convert YAML value {:?} to either a vec or string", $v);
panic!("Failed to convert YAML value {:?} to a string", ys);
}
}
$a
} else {
if let Some(s) = $v.as_str() {
$a = $a.$c(s);
} else {
panic!(
"Failed to convert YAML value {:?} to either a vec or string",
$v
);
}
}
};
$a
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_opt_str {
($v:expr) => {{
if $v.is_null() {
Some($v.as_str().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)))
Some(
$v.as_str()
.unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)),
)
} else {
None
}
@ -76,7 +79,8 @@ macro_rules! yaml_opt_str {
#[cfg(feature = "yaml")]
macro_rules! yaml_str {
($v:expr) => {{
$v.as_str().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))
$v.as_str()
.unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))
}};
}
@ -90,20 +94,28 @@ macro_rules! yaml_to_str {
#[cfg(feature = "yaml")]
macro_rules! yaml_to_bool {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_bool().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)))
$a.$c($v
.as_bool()
.unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)))
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_to_u64 {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as u64)
$a.$c($v
.as_i64()
.unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))
as u64)
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_to_usize {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as usize)
$a.$c($v
.as_i64()
.unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))
as usize)
}};
}

View file

@ -7,7 +7,7 @@ pub mod arg;
mod arg_group;
mod usage_parser;
pub use self::usage_parser::UsageParser;
pub use self::app::{App, AppFlags, AppSettings, Propagation};
pub use self::arg::{Arg, ArgFlags, ArgSettings};
pub use self::arg_group::ArgGroup;
pub use self::arg_group::ArgGroup;
pub use self::usage_parser::UsageParser;

View file

@ -1,7 +1,7 @@
// Internal
use INTERNAL_ERROR_MSG;
use build::{Arg, ArgSettings};
use util::VecMap;
use INTERNAL_ERROR_MSG;
#[derive(PartialEq, Debug)]
enum UsageToken {
@ -74,7 +74,8 @@ impl<'a> UsageParser<'a> {
fn name(&mut self, arg: &mut Arg<'a, 'a>) {
debugln!("UsageParser::name;");
if *self.usage
if *self
.usage
.as_bytes()
.get(self.pos)
.expect(INTERNAL_ERROR_MSG) == b'<' && !self.explicit_name_set
@ -122,7 +123,8 @@ impl<'a> UsageParser<'a> {
fn short_or_long(&mut self, arg: &mut Arg<'a, 'a>) {
debugln!("UsageParser::short_or_long;");
self.pos += 1;
if *self.usage
if *self
.usage
.as_bytes()
.get(self.pos)
.expect(INTERNAL_ERROR_MSG) == b'-'

View file

@ -8,7 +8,7 @@ use INTERNAL_ERROR_MSG;
pub struct ElvishGen<'a, 'b, 'c>
where
'a: 'b,
'b: 'c
'b: 'c,
{
app: &'c App<'a, 'b>,
}
@ -20,10 +20,10 @@ impl<'a, 'b, 'c> ElvishGen<'a, 'b, 'c> {
let bin_name = self.app.bin_name.as_ref().unwrap();
let mut names = vec![];
let subcommands_cases =
generate_inner(self.app, "", &mut names);
let subcommands_cases = generate_inner(self.app, "", &mut names);
let result = format!(r#"
let result = format!(
r#"
edit:completion:arg-completer[{bin_name}] = [@words]{{
fn spaces [n]{{
repeat $n ' ' | joins ''
@ -54,10 +54,10 @@ edit:completion:arg-completer[{bin_name}] = [@words]{{
// Escape string inside single quotes
fn escape_string(string: &str) -> String { string.replace("'", "''") }
fn get_tooltip<T : ToString>(help: Option<&str>, data: T) -> String {
fn get_tooltip<T: ToString>(help: Option<&str>, data: T) -> String {
match help {
Some(help) => escape_string(help),
_ => data.to_string()
_ => data.to_string(),
}
}
@ -65,7 +65,11 @@ fn generate_inner<'a, 'b, 'c>(
p: &'c App<'a, 'b>,
previous_command_name: &str,
names: &mut Vec<&'a str>,
) -> String where 'a: 'b, 'b: 'c{
) -> String
where
'a: 'b,
'b: 'c,
{
debugln!("ElvishGen::generate_inner;");
let command_name = if previous_command_name.is_empty() {
p.bin_name.as_ref().expect(INTERNAL_ERROR_MSG).clone()
@ -113,13 +117,11 @@ fn generate_inner<'a, 'b, 'c>(
r"
&'{}'= {{{}
}}",
&command_name,
completions
&command_name, completions
);
for subcommand in &p.subcommands {
let subcommand_subcommands_cases =
generate_inner(&subcommand, &command_name, names);
let subcommand_subcommands_cases = generate_inner(&subcommand, &command_name, names);
subcommands_cases.push_str(&subcommand_subcommands_cases);
}

View file

@ -24,5 +24,5 @@ macro_rules! get_zsh_arg_conflicts {
} else {
String::new()
}
}
};
}

View file

@ -1,23 +1,23 @@
#[macro_use]
mod macros;
mod bash;
mod fish;
mod zsh;
mod powershell;
mod elvish;
mod fish;
mod powershell;
mod shell;
mod zsh;
// Std
use std::io::Write;
// Internal
use build::App;
use self::bash::BashGen;
use self::fish::FishGen;
use self::zsh::ZshGen;
use self::powershell::PowerShellGen;
use self::elvish::ElvishGen;
use self::fish::FishGen;
use self::powershell::PowerShellGen;
pub use self::shell::Shell;
use self::zsh::ZshGen;
use build::App;
pub struct ComplGen<'a, 'b>(&'b App<'a, 'b>)
where
@ -33,7 +33,7 @@ impl<'a, 'b> ComplGen<'a, 'b> {
Shell::Zsh => ZshGen::new(self.0).generate_to(buf),
Shell::PowerShell => PowerShellGen::new(self.0).generate_to(buf),
Shell::Elvish => ElvishGen::new(self.0).generate_to(buf),
_ => panic!("Unsupported shell type for generating completions")
_ => panic!("Unsupported shell type for generating completions"),
}
}
}

View file

@ -16,10 +16,10 @@ impl<'a, 'b> PowerShellGen<'a, 'b> {
let bin_name = self.0.bin_name.as_ref().unwrap();
let mut names = vec![];
let subcommands_cases =
generate_inner(self.0, "", &mut names);
let subcommands_cases = generate_inner(self.0, "", &mut names);
let result = format!(r#"
let result = format!(
r#"
using namespace System.Management.Automation
using namespace System.Management.Automation.Language
@ -57,10 +57,10 @@ Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{
// Escape string inside single quotes
fn escape_string(string: &str) -> String { string.replace("'", "''") }
fn get_tooltip<T : ToString>(help: Option<&str>, data: T) -> String {
fn get_tooltip<T: ToString>(help: Option<&str>, data: T) -> String {
match help {
Some(help) => escape_string(&help),
_ => data.to_string()
_ => data.to_string(),
}
}
@ -83,14 +83,22 @@ fn generate_inner<'a, 'b, 'p>(
if let Some(data) = option.short {
let tooltip = get_tooltip(option.help, data);
completions.push_str(&preamble);
completions.push_str(format!("'-{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
completions.push_str(
format!(
"'-{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip
).as_str(),
);
}
if let Some(data) = option.long {
let tooltip = get_tooltip(option.help, data);
completions.push_str(&preamble);
completions.push_str(format!("'--{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
completions.push_str(
format!(
"'--{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip
).as_str(),
);
}
}
@ -98,14 +106,22 @@ fn generate_inner<'a, 'b, 'p>(
if let Some(data) = flag.short {
let tooltip = get_tooltip(flag.help, data);
completions.push_str(&preamble);
completions.push_str(format!("'-{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
completions.push_str(
format!(
"'-{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip
).as_str(),
);
}
if let Some(data) = flag.long {
let tooltip = get_tooltip(flag.help, data);
completions.push_str(&preamble);
completions.push_str(format!("'--{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip).as_str());
completions.push_str(
format!(
"'--{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterName", tooltip
).as_str(),
);
}
}
@ -113,8 +129,12 @@ fn generate_inner<'a, 'b, 'p>(
let data = &subcommand.name;
let tooltip = get_tooltip(subcommand.about, data);
completions.push_str(&preamble);
completions.push_str(format!("'{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterValue", tooltip).as_str());
completions.push_str(
format!(
"'{}', '{}', {}, '{}')",
data, data, "[CompletionResultType]::ParameterValue", tooltip
).as_str(),
);
}
let mut subcommands_cases = format!(
@ -122,13 +142,11 @@ fn generate_inner<'a, 'b, 'p>(
'{}' {{{}
break
}}",
&command_name,
completions
&command_name, completions
);
for subcommand in &p.subcommands {
let subcommand_subcommands_cases =
generate_inner(&subcommand, &command_name, names);
let subcommand_subcommands_cases = generate_inner(&subcommand, &command_name, names);
subcommands_cases.push_str(&subcommand_subcommands_cases);
}

View file

@ -1,7 +1,7 @@
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::str::FromStr;
use std::fmt;
use std::str::FromStr;
/// Describes which shell to produce a completions file for
#[cfg_attr(feature = "lints", allow(enum_variant_names))]
@ -36,7 +36,9 @@ impl FromStr for Shell {
"BASH" | _ if s.eq_ignore_ascii_case("bash") => Ok(Shell::Bash),
"POWERSHELL" | _ if s.eq_ignore_ascii_case("powershell") => Ok(Shell::PowerShell),
"ELVISH" | _ if s.eq_ignore_ascii_case("elvish") => Ok(Shell::Elvish),
_ => Err(String::from("[valid values: bash, fish, zsh, powershell, elvish]")),
_ => Err(String::from(
"[valid values: bash, fish, zsh, powershell, elvish]",
)),
}
}
}
@ -49,7 +51,7 @@ impl fmt::Display for Shell {
Shell::Zsh => write!(f, "ZSH"),
Shell::PowerShell => write!(f, "POWERSHELL"),
Shell::Elvish => write!(f, "ELVISH"),
_ => panic!("Unsupported shell type for completion generation")
_ => panic!("Unsupported shell type for completion generation"),
}
}
}

View file

@ -1,7 +1,7 @@
// Std
use std::io::Write;
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::io::Write;
// Internal
use build::{App, ArgSettings};
@ -86,9 +86,8 @@ _{name} \"$@\"",
fn subcommand_details(p: &App) -> String {
debugln!("ZshGen::subcommand_details;");
// First we do ourself
let mut ret = vec![
format!(
"\
let mut ret = vec![format!(
"\
(( $+functions[_{bin_name_underscore}_commands] )) ||
_{bin_name_underscore}_commands() {{
local commands; commands=(
@ -96,11 +95,10 @@ _{bin_name_underscore}_commands() {{
)
_describe -t commands '{bin_name} commands' commands \"$@\"
}}",
bin_name_underscore = p.bin_name.as_ref().unwrap().replace(" ", "__"),
bin_name = p.bin_name.as_ref().unwrap(),
subcommands_and_args = subcommands_of(p)
),
];
bin_name_underscore = p.bin_name.as_ref().unwrap().replace(" ", "__"),
bin_name = p.bin_name.as_ref().unwrap(),
subcommands_and_args = subcommands_of(p)
)];
// Next we start looping through all the children, grandchildren, etc.
let mut all_subcommands = completions::all_subcommands(p);
@ -145,7 +143,8 @@ fn subcommands_of(p: &App) -> String {
let s = format!(
"\"{name}:{help}\" \\",
name = n,
help = sc.about
help = sc
.about
.unwrap_or("")
.replace("[", "\\[")
.replace("]", "\\]")
@ -458,20 +457,24 @@ fn write_positionals_of(p: &App) -> String {
""
},
name = arg.name,
help = arg.help
help = arg
.help
.map_or("".to_owned(), |v| " -- ".to_owned() + v)
.replace("[", "\\[")
.replace("]", "\\]"),
action = arg.possible_vals
action = arg
.possible_vals
.as_ref()
.map_or("_files".to_owned(), |values| format!(
"({})",
values
.iter()
.map(|v| escape_value(*v))
.collect::<Vec<String>>()
.join(" ")
))
.map_or("_files".to_owned(), |values| {
format!(
"({})",
values
.iter()
.map(|v| escape_value(*v))
.collect::<Vec<String>>()
.join(" ")
)
})
);
debugln!("write_positionals_of:iter: Wrote...{}", a);

View file

@ -517,7 +517,11 @@
//! [license]: https://raw.githubusercontent.com/kbknapp/clap-rs/master/LICENSE-MIT
#![crate_type = "lib"]
<<<<<<< HEAD
#![doc(html_root_url = "https://docs.rs/clap/3.0.0-alpha.1")]
=======
#![doc(html_root_url = "https://docs.rs/clap/3.0.0-alpha1")]
>>>>>>> WIP. Big reformat
#![deny(
missing_docs,
missing_debug_implementations,
@ -582,11 +586,17 @@ use std::result::Result as StdResult;
#[macro_use]
mod macros;
<<<<<<< HEAD
<<<<<<< HEAD
mod build;
=======
mod mkeymap;
>>>>>>> WIP changing macros into MKeyMap calls
mod completions;
=======
mod build;
mod completions;
mod mkeymap;
>>>>>>> WIP. Big reformat
mod output;
mod parse;
mod util;

View file

@ -26,9 +26,10 @@
#[cfg(feature = "yaml")]
#[macro_export]
macro_rules! load_yaml {
($yml:expr) => (
&::clap::YamlLoader::load_from_str(include_str!($yml)).expect("failed to load YAML file")[0]
);
($yml:expr) => {
&::clap::YamlLoader::load_from_str(include_str!($yml))
.expect("failed to load YAML file")[0]
};
}
/// Convenience macro getting a typed value `T` where `T` implements [`std::str::FromStr`] from an
@ -66,9 +67,10 @@ macro_rules! value_t {
if let Some(v) = $m.value_of($v) {
match v.parse::<$t>() {
Ok(val) => Ok(val),
Err(_) =>
Err(::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", v))),
Err(_) => Err(::clap::Error::value_validation_auto(format!(
"The argument '{}' isn't a valid value",
v
))),
}
} else {
Err(::clap::Error::argument_not_found_auto($v))
@ -111,9 +113,10 @@ macro_rules! value_t_or_exit {
if let Some(v) = $m.value_of($v) {
match v.parse::<$t>() {
Ok(val) => val,
Err(_) =>
::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", v)).exit(),
Err(_) => ::clap::Error::value_validation_auto(format!(
"The argument '{}' isn't a valid value",
v
)).exit(),
}
} else {
::clap::Error::argument_not_found_auto($v).exit()
@ -163,9 +166,11 @@ macro_rules! values_t {
match pv.parse::<$t>() {
Ok(rv) => tmp.push(rv),
Err(..) => {
err = Some(::clap::Error::value_validation_auto(
format!("The argument '{}' isn't a valid value", pv)));
break
err = Some(::clap::Error::value_validation_auto(format!(
"The argument '{}' isn't a valid value",
pv
)));
break;
}
}
}
@ -218,10 +223,13 @@ macro_rules! values_t_or_exit {
};
($m:ident.values_of($v:expr), $t:ty) => {
if let Some(vals) = $m.values_of($v) {
vals.map(|v| v.parse::<$t>().unwrap_or_else(|_|{
::clap::Error::value_validation_auto(
format!("One or more arguments aren't valid values")).exit()
})).collect::<Vec<$t>>()
vals.map(|v| {
v.parse::<$t>().unwrap_or_else(|_| {
::clap::Error::value_validation_auto(format!(
"One or more arguments aren't valid values"
)).exit()
})
}).collect::<Vec<$t>>()
} else {
::clap::Error::argument_not_found_auto($v).exit()
}
@ -451,12 +459,14 @@ macro_rules! crate_version {
macro_rules! crate_authors {
($sep:expr) => {{
use std::ops::Deref;
use std::sync::{ONCE_INIT, Once};
use std::sync::{Once, ONCE_INIT};
#[allow(missing_copy_implementations)]
#[allow(dead_code)]
struct CargoAuthors { __private_field: () };
struct CargoAuthors {
__private_field: (),
};
impl Deref for CargoAuthors {
type Target = str;
@ -476,7 +486,9 @@ macro_rules! crate_authors {
}
}
&*CargoAuthors { __private_field: () }
&*CargoAuthors {
__private_field: (),
}
}};
() => {
env!("CARGO_PKG_AUTHORS")
@ -845,16 +857,16 @@ mod debug_macros {
#[cfg_attr(not(feature = "debug"), macro_use)]
mod debug_macros {
macro_rules! debugln {
($fmt:expr) => ();
($fmt:expr, $($arg:tt)*) => ();
($fmt:expr) => {};
($fmt:expr, $($arg:tt)*) => {};
}
macro_rules! sdebugln {
($fmt:expr) => ();
($fmt:expr, $($arg:tt)*) => ();
($fmt:expr) => {};
($fmt:expr, $($arg:tt)*) => {};
}
macro_rules! debug {
($fmt:expr) => ();
($fmt:expr, $($arg:tt)*) => ();
($fmt:expr) => {};
($fmt:expr, $($arg:tt)*) => {};
}
}
@ -863,12 +875,12 @@ mod debug_macros {
// src/args/arg_builder/*.rs
// src/app/mod.rs
macro_rules! write_nspaces {
($dst:expr, $num:expr) => ({
($dst:expr, $num:expr) => {{
debugln!("write_spaces!: num={}", $num);
for _ in 0..$num {
$dst.write_all(b" ")?;
}
})
}};
}
macro_rules! args {
@ -876,70 +888,94 @@ macro_rules! args {
$app.args.$how()
};
($app:expr) => {
args!($app, iter)
}
args!($app, values)
};
}
macro_rules! args_mut {
($app:expr) => {
args!($app, iter_mut)
}
args!($app, values_mut)
};
}
macro_rules! flags {
($app:expr, $how:ident) => {
$app.args.$how()
.filter(|a| !a.settings.is_set(::build::ArgSettings::TakesValue))
.filter(|a| a.short.is_some() || a.long.is_some())
.filter(|a| !a.help_heading.is_some())
};
($app:expr, $how:ident) => {{
use mkeymap::KeyType::*;
$app.args
.$how()
.filter(|(k, a)| !a.settings.is_set(::build::ArgSettings::TakesValue))
.filter(|(k, a)| match k {
Long(_) => true,
Short(_) => true,
Position(_) => false,
})
.filter(|(k, a)| !a.help_heading.is_some())
}};
($app:expr) => {
flags!($app, iter)
}
};
}
#[allow(unused_macros)]
macro_rules! flags_mut {
($app:expr) => {
flags!($app, iter_mut)
}
};
}
macro_rules! opts {
($app:expr, $how:ident) => {
$app.args.$how()
.filter(|a| a.settings.is_set(::build::ArgSettings::TakesValue))
.filter(|a| a.short.is_some() || a.long.is_some())
.filter(|a| !a.help_heading.is_some())
};
($app:expr, $how:ident) => {{
use mkeymap::KeyType::*;
$app.args
.$how()
.filter(|(k, a)| a.settings.is_set(::build::ArgSettings::TakesValue))
.filter(|(k, a)| match k {
Long(_) => true,
Short(_) => true,
Position(_) => false,
})
.filter(|(k, a)| !a.help_heading.is_some())
}};
($app:expr) => {
opts!($app, iter)
}
};
}
#[allow(unused_macros)]
macro_rules! opts_mut {
($app:expr) => {
opts!($app, iter_mut)
}
};
}
// * We need a way to iterate through all the Positional Args
// macro_rules! positionals {
// ($app:expr, $how:ident) => {
// $app.args
// .$how()
// .filter(|a| !a.help_heading.is_some())
// .filter(|a| !(a.short.is_some() || a.long.is_some()))
// };
// ($app:expr) => {
// positionals!($app, values)
// };
// }
macro_rules! positionals {
($app:expr, $how:ident) => {
$app.args.$how()
.filter(|a| !a.help_heading.is_some())
($app:expr) => {
$app.args
.values()
.filter(|a| !(a.short.is_some() || a.long.is_some()))
};
($app:expr) => {
positionals!($app, iter)
}
}
#[allow(unused_macros)]
macro_rules! positionals_mut {
($app:expr) => {
positionals!($app, iter_mut)
}
$app.args
.values_mut()
.filter(|a| !(a.short.is_some() || a.long.is_some()))
};
}
#[allow(unused_macros)]
@ -948,15 +984,15 @@ macro_rules! custom_headings {
$app.args.$how().filter(|a| (a.help_heading.is_some()))
};
($app:expr) => {
custom_headings!($app, iter)
}
custom_headings!($app, values)
};
}
#[allow(unused_macros)]
macro_rules! custom_headings_mut {
($app:expr) => {
custom_headings!($app, iter_mut)
}
custom_headings!($app, values_mut)
};
}
macro_rules! subcommands_cloned {
@ -965,7 +1001,7 @@ macro_rules! subcommands_cloned {
};
($app:expr) => {
subcommands_cloned!($app, iter)
}
};
}
macro_rules! subcommands {
@ -974,13 +1010,13 @@ macro_rules! subcommands {
};
($app:expr) => {
subcommands!($app, iter)
}
};
}
macro_rules! subcommands_mut {
($app:expr) => {
subcommands!($app, iter_mut)
}
};
}
macro_rules! groups {
@ -989,13 +1025,13 @@ macro_rules! groups {
};
($app:expr) => {
groups!($app, iter)
}
};
}
macro_rules! groups_mut {
($app:expr) => {
groups!($app, iter_mut)
}
};
}
macro_rules! find_from {
@ -1015,13 +1051,14 @@ macro_rules! find_from {
}
// Finds an arg by name
// ! look up usages and find ways to improve performance
macro_rules! find {
($app:expr, $name:expr, $what:ident) => {
$what!($app).find(|a| &a.name == $name)
};
($app:expr, $name:expr) => {
$app.args.iter().find(|a| &a.name == $name)
}
$app.args.values().find(|a| &a.name == $name)
};
}
// macro_rules! find_by_long {
@ -1050,15 +1087,13 @@ macro_rules! find {
macro_rules! find_subcmd_cloned {
($_self:expr, $sc:expr) => {{
subcommands_cloned!($_self)
.find(|a| match_alias!(a, $sc, &*a.name))
subcommands_cloned!($_self).find(|a| match_alias!(a, $sc, &*a.name))
}};
}
macro_rules! find_subcmd {
($app:expr, $sc:expr) => {{
subcommands!($app)
.find(|a| match_alias!(a, $sc, &*a.name))
subcommands!($app).find(|a| match_alias!(a, $sc, &*a.name))
}};
}
@ -1085,35 +1120,45 @@ macro_rules! find_subcmd {
// }};
// }
//TODO change into one macro (repeated structure)
//TODO change into one macro (repeated structure) + Positionals
macro_rules! longs {
($app:expr) => ({
($app:expr) => {{
use mkeymap::KeyType;
$app.args.keys().filter_map(|a| if let KeyType::Long(v) = a {Some(v)} else {None})
});
$app.args.keys().filter_map(|a| {
if let KeyType::Long(v) = a {
Some(v)
} else {
None
}
})
}};
}
macro_rules! shorts {
($app:expr) => ({
($app:expr) => {{
use mkeymap::KeyType;
$app.args.keys().filter_map(|a| if let KeyType::Short(v) = a {Some(v)} else {None})
})
$app.args.keys().filter_map(|a| {
if let KeyType::Short(v) = a {
Some(v)
} else {
None
}
})
}};
}
macro_rules! _names {
(@args $app:expr) => {{
$app.args.iter().map(|a| &*a.name)
$app.args.values().map(|a| &*a.name)
}};
(@sc $app:expr) => {{
$app.subcommands
.iter()
.map(|s| &*s.name)
.chain($app.subcommands
.iter()
.filter(|s| s.aliases.is_some())
.flat_map(|s| s.aliases.as_ref().unwrap().iter().map(|&(n, _)| n)))
}}
$app.subcommands.iter().map(|s| &*s.name).chain(
$app.subcommands
.iter()
.filter(|s| s.aliases.is_some())
.flat_map(|s| s.aliases.as_ref().unwrap().iter().map(|&(n, _)| n)),
)
}};
}
macro_rules! arg_names {
@ -1128,15 +1173,16 @@ macro_rules! sc_names {
}};
}
// macro_rules! match_alias {
// ($a:expr, $to:expr, $what:expr) => {{
// $what == $to ||
// ($a.aliases.is_some() &&
// $a.aliases
// .as_ref()
// .unwrap()
// .iter()
// .any(|alias| alias.0 == $to))
// }}
// }
//probably scrap it altogether, as instead of iterating and matching alias we can just find by a Long
macro_rules! match_alias {
($a:expr, $to:expr, $what:expr) => {{
$what == $to
|| ($a.aliases.is_some()
&& $a
.aliases
.as_ref()
.unwrap()
.iter()
.any(|alias| alias.0 == $to))
}};
}

View file

@ -65,7 +65,7 @@ impl<'a, 'b> MKeyMap<'a, 'b> {
set
});
}
index
}
//TODO ::push_many([x, y])
@ -89,6 +89,8 @@ impl<'a, 'b> MKeyMap<'a, 'b> {
self.keys.insert(key, index);
}
// ! Arg mutation functionality
pub fn get(&self, key: KeyType<'a>) -> Option<&Arg<'a, 'b>> {
self.keys
.get(&key)
@ -97,13 +99,11 @@ impl<'a, 'b> MKeyMap<'a, 'b> {
//TODO ::get_first([KeyA, KeyB])
pub fn get_mut(&mut self, key: KeyType<'a>) -> Option<&mut Arg<'a, 'b>> {
if let Some(&idx) = self
.keys
.get(&key) {
self.value_index.get_mut(idx)
} else {
None
}
if let Some(&idx) = self.keys.get(&key) {
self.value_index.get_mut(idx)
} else {
None
}
}
pub fn is_empty(&self) -> bool { self.keys.is_empty() && self.values.is_empty() }
@ -122,13 +122,13 @@ impl<'a, 'b> MKeyMap<'a, 'b> {
}
}
pub fn values(&'a self) -> Values<'a, Arg> {
pub fn values(&'a self) -> Values<'a, Arg<'a, 'b>> {
Values {
iter: self.value_index.iter(),
}
}
pub fn values_mut(&'a mut self) -> ValuesMut<'a, Arg> {
pub fn values_mut(&'a mut self) -> ValuesMut<'a, Arg<'a, 'b>> {
ValuesMut {
iter: self.value_index.iter_mut(),
}
@ -173,14 +173,19 @@ impl<'a, V> Iterator for ValuesMut<'a, V> {
fn next(&mut self) -> Option<Self::Item> { self.iter.next() }
}
pub struct Iter<'a, 'b, 'c> where
'a: 'b,
'b: 'c {
pub struct Iter<'a, 'b, 'c>
where
'a: 'b,
'b: 'c,
{
map: &'c MKeyMap<'a, 'b>,
keys: Keys<'c, usize>,
}
impl <'a, 'b, 'c> Iterator for Iter<'a, 'b, 'c>
impl<'a, 'b, 'c> Iterator for Iter<'a, 'b, 'c>
where
'a: 'b,
'b: 'c,
{
type Item = (&'c KeyType<'a>, &'c Arg<'a, 'b>);
@ -203,11 +208,12 @@ mod tests {
fn get_some_value() {
let mut map: MKeyMap = MKeyMap::new();
{
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
}
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
assert_eq!(map.get(Long(&OsStr::new("One"))), &Arg::with_name("Value1"));
assert_eq!(
map.get(Long(&OsStr::new("One"))),
Some(&Arg::with_name("Value1"))
);
}
#[test]
@ -217,6 +223,8 @@ mod tests {
map.insert(Long(&OsStr::new("One")), Arg::with_name("Value1"));
map.get(Long(&OsStr::new("Two")));
assert_eq!(map.get(Long(&OsStr::new("Two"))), None);
}
// #[test]
@ -301,7 +309,7 @@ mod tests {
assert_eq!(
map.get_mut(Long(&OsStr::new("One"))),
&mut Arg::with_name("Value1")
Some(&mut Arg::with_name("Value1"))
);
}

View file

@ -6,8 +6,8 @@ use ansi_term::Colour::{Green, Red, Yellow};
#[cfg(feature = "color")]
use atty;
use std::fmt;
use std::env;
use std::fmt;
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq)]
@ -164,9 +164,9 @@ impl<T: fmt::Display> fmt::Display for Format<T> {
#[cfg(all(test, feature = "color", not(target_os = "windows")))]
mod test {
use super::Format;
use ansi_term::ANSIString;
use ansi_term::Colour::{Green, Red, Yellow};
use super::Format;
#[test]
fn colored_output() {

View file

@ -7,18 +7,18 @@ use std::usize;
// Internal
use build::{App, AppSettings, Arg, ArgSettings};
use parse::Parser;
use parse::errors::{Error, Result as ClapResult};
use output::fmt::{Colorizer, ColorizerOption, Format};
use output::Usage;
use parse::errors::{Error, Result as ClapResult};
use parse::Parser;
use util::VecMap;
use INTERNAL_ERROR_MSG;
// Third Party
use unicode_width::UnicodeWidthStr;
#[cfg(feature = "wrap_help")]
use term_size;
use textwrap;
use unicode_width::UnicodeWidthStr;
#[cfg(not(feature = "wrap_help"))]
mod term_size {
@ -173,18 +173,16 @@ impl<'w> Help<'w> {
impl<'w> Help<'w> {
/// Writes help for each argument in the order they were declared to the wrapped stream.
fn write_args_unsorted<'a, 'b, I>(&mut self, args: I) -> io::Result<()>
where
'a: 'b,
I: Iterator<Item=&'b Arg<'a, 'b>>,
where
'a: 'b,
I: Iterator<Item = &'b Arg<'a, 'b>>,
{
debugln!("Help::write_args_unsorted;");
// The shortest an arg can legally be is 2 (i.e. '-x')
self.longest = 2;
let mut arg_v = Vec::with_capacity(10);
let use_long = self.use_long;
for arg in args.filter(|arg| {
should_show_arg(use_long, *arg)
}) {
for arg in args.filter(|arg| should_show_arg(use_long, *arg)) {
if arg.longest_filter() {
self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
}
@ -207,9 +205,9 @@ impl<'w> Help<'w> {
/// Sorts arguments by length and display order and write their help to the wrapped stream.
fn write_args<'a, 'b, I>(&mut self, args: I) -> io::Result<()>
where
'a: 'b,
I: Iterator<Item=&'b Arg<'a, 'b>>,
where
'a: 'b,
I: Iterator<Item = &'b Arg<'a, 'b>>,
{
debugln!("Help::write_args;");
// The shortest an arg can legally be is 2 (i.e. '-x')
@ -346,7 +344,8 @@ impl<'w> Help<'w> {
let h_w = str_width(h) + str_width(&*spec_vals);
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
let taken = self.longest + 12;
self.force_next_line = !nlh && self.term_w >= taken
self.force_next_line = !nlh
&& self.term_w >= taken
&& (taken as f32 / self.term_w as f32) > 0.40
&& h_w > (self.term_w - taken);
@ -427,7 +426,12 @@ impl<'w> Help<'w> {
}
/// Writes argument's help to the wrapped stream.
fn help<'b, 'c>(&mut self, arg: &Arg<'b, 'c>, spec_vals: &str, prevent_nlh: bool) -> io::Result<()> {
fn help<'b, 'c>(
&mut self,
arg: &Arg<'b, 'c>,
spec_vals: &str,
prevent_nlh: bool,
) -> io::Result<()> {
debugln!("Help::help;");
let h = if self.use_long {
arg.long_help.unwrap_or_else(|| arg.help.unwrap_or(""))
@ -577,7 +581,8 @@ impl<'w> Help<'w> {
let h_w = str_width(h) + str_width(&*spec_vals);
let nlh = self.next_line_help;
let taken = self.longest + 12;
self.force_next_line = !nlh && self.term_w >= taken
self.force_next_line = !nlh
&& self.term_w >= taken
&& (taken as f32 / self.term_w as f32) > 0.40
&& h_w > (self.term_w - taken);
@ -739,16 +744,22 @@ impl<'w> Help<'w> {
first = false;
}
if custom_headings {
for heading in parser.app.help_headings.iter()
for heading in parser
.app
.help_headings
.iter()
.filter(|heading| heading.is_some())
.map(|heading| heading.unwrap()) {
if !first {
self.writer.write_all(b"\n\n")?;
}
color!(self, format!("{}:\n", heading), warning)?;
self.write_args(custom_headings!(parser.app).filter(|a| a.help_heading.unwrap() == heading))?;
first = false
.map(|heading| heading.unwrap())
{
if !first {
self.writer.write_all(b"\n\n")?;
}
color!(self, format!("{}:\n", heading), warning)?;
self.write_args(
custom_headings!(parser.app).filter(|a| a.help_heading.unwrap() == heading),
)?;
first = false
}
}
}
@ -832,8 +843,7 @@ impl<'w> Help<'w> {
($thing:expr) => {{
let mut owned_thing = $thing.to_owned();
owned_thing = owned_thing.replace("{n}", "\n");
write!(self.writer, "{}\n",
wrap_help(&owned_thing, self.term_w))?
write!(self.writer, "{}\n", wrap_help(&owned_thing, self.term_w))?
}};
}
// Print the version
@ -1113,16 +1123,19 @@ impl<'w> Help<'w> {
}
fn should_show_arg(use_long: bool, arg: &Arg) -> bool {
debugln!("Help::should_show_arg: use_long={:?}, arg={}", use_long, arg.name);
debugln!(
"Help::should_show_arg: use_long={:?}, arg={}",
use_long,
arg.name
);
if arg.is_set(ArgSettings::Hidden) {
return false;
}
(!arg.is_set(ArgSettings::HiddenLongHelp) && use_long) ||
(!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long) ||
arg.is_set(ArgSettings::NextLineHelp)
(!arg.is_set(ArgSettings::HiddenLongHelp) && use_long)
|| (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long)
|| arg.is_set(ArgSettings::NextLineHelp)
}
fn wrap_help(help: &str, avail_chars: usize) -> String {
let wrapper = textwrap::Wrapper::new(avail_chars).break_words(false);
help.lines()

View file

@ -4,4 +4,4 @@ mod usage;
pub mod fmt;
pub use self::help::Help;
pub use self::usage::Usage;
pub use self::usage::Usage;

View file

@ -1 +1 @@
pub mod suggestions;
pub mod suggestions;

View file

@ -2,6 +2,6 @@ mod arg_matches;
mod matched_arg;
mod subcommand;
pub use self::arg_matches::{ArgMatches, Values, OsValues};
pub use self::arg_matches::{ArgMatches, OsValues, Values};
pub use self::matched_arg::MatchedArg;
pub use self::subcommand::SubCommand;
pub use self::matched_arg::MatchedArg;

View file

@ -1,13 +1,13 @@
pub mod errors;
pub mod features;
mod matches;
mod arg_matcher;
mod matches;
mod parser;
mod validator;
pub use self::parser::{Parser, ParseResult};
pub use self::matches::ArgMatches;
pub use self::arg_matcher::ArgMatcher;
pub use self::matches::{Values, OsValues, SubCommand, MatchedArg};
pub use self::validator::Validator;
pub use self::matches::ArgMatches;
pub use self::matches::{MatchedArg, OsValues, SubCommand, Values};
pub use self::parser::{ParseResult, Parser};
pub use self::validator::Validator;

View file

@ -27,6 +27,7 @@ use util::VecMap;
use build::app::Propagation;
use build::AppSettings as AS;
use build::{App, Arg, ArgSettings};
use mkeymap::KeyType;
use output::Help;
use output::Usage;
use parse::errors::Error as ClapError;
@ -40,7 +41,6 @@ use INVALID_UTF8;
use INTERNAL_ERROR_MSG;
use parse::features::suggestions;
use output::Usage;
use mkeymap::KeyType;
#[derive(Debug, PartialEq, Copy, Clone)]
#[doc(hidden)]
@ -64,10 +64,10 @@ where
pub required: Vec<&'a str>,
pub r_ifs: Vec<(&'a str, &'b str, &'a str)>,
pub overriden: Vec<&'a str>,
cache: Option<&'a str>,
//cache: Option<&'a str>,
num_opts: usize,
num_flags: usize,
pub positionals: VecMap<&'a str>,
//pub positionals: VecMap<&'a str>,
seen: Vec<&'a str>,
cur_idx: Cell<usize>,
}
@ -107,7 +107,7 @@ where
pub fn new(app: &'c mut App<'a, 'b>) -> Self {
let reqs = app
.args
.iter()
.values()
.filter(|a| a.settings.is_set(ArgSettings::Required))
.map(|a| a.name)
.collect();
@ -117,10 +117,8 @@ where
required: reqs,
r_ifs: Vec::new(),
overriden: Vec::new(),
cache: None,
num_opts: 0,
num_flags: 0,
positionals: VecMap::new(),
seen: Vec::new(),
cur_idx: Cell::new(0),
}
@ -135,15 +133,41 @@ where
// Firt we verify that the index highest supplied index, is equal to the number of
// positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
// but no 2)
#[cfg(feature = "vec_map")]
fn _highest_idx(map: &VecMap<&str>) -> usize { map.keys().last().unwrap_or(0) }
#[cfg(not(feature = "vec_map"))]
fn _highest_idx(map: &VecMap<&str>) -> usize { *map.keys().last().unwrap_or(&0) }
// #[cfg(feature = "vec_map")]
// fn _highest_idx(map: &VecMap<&str>) -> usize { map.keys().last().unwrap_or(0) }
let highest_idx = _highest_idx(&self.positionals);
// #[cfg(not(feature = "vec_map"))]
// fn _highest_idx(map: &VecMap<&str>) -> usize { *map.keys().last().unwrap_or(&0) }
let num_p = self.positionals.len();
let highest_idx = *self
.app
.args
.keys()
.filter_map(|x| {
if let KeyType::Position(n) = x {
Some(n)
} else {
None
}
})
.max()
.unwrap_or(&0);
//_highest_idx(&self.positionals);
let num_p = self
.app
.args
.keys()
.filter(|x| {
if let KeyType::Position(_) = x {
true
} else {
false
}
})
.count();
assert!(
highest_idx == num_p,
@ -162,14 +186,29 @@ where
// * a value terminator
// * ArgSettings::Last
// * The last arg is Required
let mut it = self.positionals.values().rev();
let mut it = self.app.args.keys().filter(|x| {
if let KeyType::Position(_) = x {
true
} else {
false
}
});
//self.positionals.values().rev();
// We can't pass the closure (it.next()) to the macro directly because each call to
// find() (iterator, not macro) gets called repeatedly.
let last_name = it.next().expect(INTERNAL_ERROR_MSG);
let second_to_last_name = it.next().expect(INTERNAL_ERROR_MSG);
let last = find!(self.app, last_name).expect(INTERNAL_ERROR_MSG);
let second_to_last = find!(self.app, second_to_last_name).expect(INTERNAL_ERROR_MSG);
let last = self
.app
.args
.get(KeyType::Position(highest_idx))
.expect(INTERNAL_ERROR_MSG);
//let second_to_last_name = it.next().expect(INTERNAL_ERROR_MSG);
//let last = find!(self.app, last_name).expect(INTERNAL_ERROR_MSG);
let second_to_last = self
.app
.args
.get(KeyType::Position(highest_idx - 1))
.expect(INTERNAL_ERROR_MSG);
// Either the final positional is required
// Or the second to last has a terminator or .last(true) set
@ -217,9 +256,10 @@ where
// index are also required.
let mut found = false;
let mut foundx2 = false;
//? What is going on here?
for p in self
.positionals
.values()
.rev()
.map(|p_name| find!(self.app, p_name).expect(INTERNAL_ERROR_MSG))
{
@ -308,7 +348,7 @@ where
for (i, a) in self.app.args.values_mut().enumerate() {
if let Some(index) = a.index {
self.app.args.insert_key(KeyType::Positional(index), i);
self.app.args.insert_key(KeyType::Position(index), i);
} else {
if let Some(c) = a.short {
self.app.args.insert_key(KeyType::Short(c), i);
@ -318,7 +358,9 @@ where
}
if let Some(v) = a.aliases {
for (item, _) in &v {
self.app.args.insert_key(KeyType::Long(&OsStr::new(item)), i);
self.app
.args
.insert_key(KeyType::Long(&OsStr::new(item)), i);
}
}
}
@ -356,7 +398,13 @@ where
// Set the LowIndexMultiple flag if required
if positionals!(self.app).any(|a| {
a.is_set(ArgSettings::MultipleValues)
&& (a.index.unwrap_or(0) as usize != self.positionals.len())
&& (a.index.unwrap_or(0) as usize
!= self
.app
.args
.keys()
.filter(|x| if let Position(_) = x { true } else { false })
.count())
}) && self.positionals.values().last().map_or(false, |p_name| {
!find!(self.app, p_name)
.expect(INTERNAL_ERROR_MSG)
@ -1030,39 +1078,34 @@ where
sdebugln!("No");
full_arg.trim_left_matches(b'-')
};
// opts?? Should probably now check once, then check whether it's opt or flag, or sth else
if let Some(opt) = self.app.args.get(KeyType::Long(arg))
{
// opts?? Should probably now check once, then check whether it's opt or flag, or sth else
if let Some(opt) = self.app.args.get(KeyType::Long(arg)) {
debugln!(
"Parser::parse_long_arg: Found valid opt '{}'",
opt.to_string()
);
self.app.settings.set(AS::ValidArgFound);
let ret = self.parse_opt(val, opt, val.is_some(), matcher)?;
if self.cache.map_or(true, |name| name != opt.name) {
self.cache = Some(opt.name);
if opt.is_set(ArgSettings::TakesValue) {
let ret = self.parse_opt(val, opt, val.is_some(), matcher)?;
// if self.cache.map_or(true, |name| name != opt.name) {
// self.cache = Some(opt.name);
// }
return Ok(ret);
} else {
// Only flags could be help or version, and we need to check the raw long
// so this is the first point to check
self.check_for_help_and_version_str(arg)?;
self.parse_flag(opt, matcher)?;
// if self.cache.map_or(true, |name| name != opt.name) {
// self.cache = Some(opt.name);
// }
return Ok(ParseResult::Flag);
}
return Ok(ret);
//flags??
} else if let Some(flag) = self.app.args.get(KeyType::Long(arg)) {
debugln!(
"Parser::parse_long_arg: Found valid flag '{}'",
flag.to_string()
);
self.app.settings.set(AS::ValidArgFound);
// Only flags could be help or version, and we need to check the raw long
// so this is the first point to check
self.check_for_help_and_version_str(arg)?;
self.parse_flag(flag, matcher)?;
// Handle conflicts, requirements, etc.
if self.cache.map_or(true, |name| name != flag.name) {
self.cache = Some(flag.name);
}
return Ok(ParseResult::Flag);
} else if self.is_set(AS::AllowLeadingHyphen) {
return Ok(ParseResult::MaybeHyphenValue);
} else if self.is_set(AS::ValidNegNumFound) {
@ -1487,7 +1530,7 @@ where
}
pub(crate) fn add_env(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
for a in &self.app.args {
for a in self.app.args.values() {
if let Some(ref val) = a.env {
if matcher
.get(a.name)
@ -1678,7 +1721,20 @@ where
pub(crate) fn has_flags(&self) -> bool { self.app.has_flags() }
pub(crate) fn has_positionals(&self) -> bool { !self.positionals.is_empty() }
pub(crate) fn has_positionals(&self) -> bool {
!self
.app
.args
.keys()
.filter(|x| {
if let KeyType::Position(_) = x {
true
} else {
false
}
})
.count() == 0
}
pub(crate) fn has_subcommands(&self) -> bool { self.app.has_subcommands() }

View file

@ -6,8 +6,8 @@ pub use self::vec_map::{Values, VecMap};
#[cfg(not(feature = "vec_map"))]
mod vec_map {
use std::collections::BTreeMap;
use std::collections::btree_map;
use std::collections::BTreeMap;
use std::fmt::{self, Debug, Formatter};
#[derive(Clone, Default, Debug)]

View file

@ -6,4 +6,4 @@ pub use self::map::{Values, VecMap};
pub use self::osstringext::OsStrExt2;
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
pub use self::osstringext::OsStrExt3;
pub use self::strext::_StrExt;
pub use self::strext::_StrExt;

View file

@ -1,8 +1,8 @@
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
use INVALID_UTF8;
use std::ffi::OsStr;
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
use std::os::unix::ffi::OsStrExt;
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
use INVALID_UTF8;
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
pub trait OsStrExt3 {

View file

@ -498,8 +498,7 @@ fn leading_double_hyphen_trailingvararg() {
#[test]
fn unset_setting() {
let m = App::new("unset_setting")
.setting(AppSettings::AllArgsOverrideSelf);
let m = App::new("unset_setting").setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf));
let m = m.unset_setting(AppSettings::AllArgsOverrideSelf);
@ -660,7 +659,10 @@ fn missing_positional_no_hyphen() {
let expected_args = vec!["arg1", "arg2", "arg3"];
assert_eq!(m.value_of("BENCH"), expected_bench);
assert_eq!(m.values_of("ARGS").unwrap().collect::<Vec<_>>(), &*expected_args);
assert_eq!(
m.values_of("ARGS").unwrap().collect::<Vec<_>>(),
&*expected_args
);
}
#[test]
@ -678,7 +680,10 @@ fn missing_positional_hyphen() {
let expected_args = vec!["arg1", "arg2", "arg3"];
assert_eq!(m.value_of("BENCH"), expected_bench);
assert_eq!(m.values_of("ARGS").unwrap().collect::<Vec<_>>(), &*expected_args);
assert_eq!(
m.values_of("ARGS").unwrap().collect::<Vec<_>>(),
&*expected_args
);
}
#[test]
@ -702,7 +707,10 @@ fn missing_positional_hyphen_far_back() {
assert_eq!(m.value_of("BENCH1"), expected_bench1);
assert_eq!(m.value_of("BENCH2"), expected_bench2);
assert_eq!(m.value_of("BENCH3"), expected_bench3);
assert_eq!(m.values_of("ARGS").unwrap().collect::<Vec<_>>(), &*expected_args);
assert_eq!(
m.values_of("ARGS").unwrap().collect::<Vec<_>>(),
&*expected_args
);
}
#[test]
@ -911,7 +919,7 @@ fn aaos_opts_mult_req_delims() {
.setting(AppSettings::AllArgsOverrideSelf)
.arg(Arg::from("--opt [val]... 'some option'"))
.get_matches_from_safe(vec![
"", "--opt", "first", "overides", "--opt", "some", "other", "val"
"", "--opt", "first", "overides", "--opt", "some", "other", "val",
]);
assert!(res.is_ok());
let m = res.unwrap();

View file

@ -59,22 +59,26 @@ fn multiple_aliases_of_option() {
.help("multiple aliases")
.aliases(&vec!["alias1", "alias2", "alias3"]),
);
let long = a.clone()
let long = a
.clone()
.get_matches_from_safe(vec!["", "--aliases", "value"]);
assert!(long.is_ok());
let long = long.unwrap();
let als1 = a.clone()
let als1 = a
.clone()
.get_matches_from_safe(vec!["", "--alias1", "value"]);
assert!(als1.is_ok());
let als1 = als1.unwrap();
let als2 = a.clone()
let als2 = a
.clone()
.get_matches_from_safe(vec!["", "--alias2", "value"]);
assert!(als2.is_ok());
let als2 = als2.unwrap();
let als3 = a.clone()
let als3 = a
.clone()
.get_matches_from_safe(vec!["", "--alias3", "value"]);
assert!(als3.is_ok());
let als3 = als3.unwrap();

View file

@ -721,45 +721,63 @@ fn build_app_with_name(s: &'static str) -> App<'static, 'static> {
App::new(s)
.about("Tests completions")
.arg(Arg::with_name("file").help("some input file"))
.subcommand(SubCommand::with_name("test")
.about("tests things")
.arg(Arg::with_name("case")
.long("case")
.takes_value(true)
.help("the case to test")))
.subcommand(
SubCommand::with_name("test").about("tests things").arg(
Arg::with_name("case")
.long("case")
.takes_value(true)
.help("the case to test"),
),
)
}
fn build_app_special_commands() -> App<'static, 'static> {
build_app_with_name("my_app")
.subcommand(SubCommand::with_name("some_cmd")
.about("tests other things")
.arg(Arg::with_name("config")
.long("--config")
.takes_value(true)
.help("the other case to test")))
.subcommand(
SubCommand::with_name("some_cmd")
.about("tests other things")
.arg(
Arg::with_name("config")
.long("--config")
.takes_value(true)
.help("the other case to test"),
),
)
.subcommand(SubCommand::with_name("some-cmd-with-hypens"))
}
fn build_app_special_help() -> App<'static, 'static> {
App::new("my_app")
.arg(Arg::with_name("single-quotes")
.long("single-quotes")
.help("Can be 'always', 'auto', or 'never'"))
.arg(Arg::with_name("double-quotes")
.long("double-quotes")
.help("Can be \"always\", \"auto\", or \"never\""))
.arg(Arg::with_name("backticks")
.long("backticks")
.help("For more information see `echo test`"))
.arg(Arg::with_name("backslash")
.long("backslash")
.help("Avoid '\\n'"))
.arg(Arg::with_name("brackets")
.long("brackets")
.help("List packages [filter]"))
.arg(Arg::with_name("expansions")
.long("expansions")
.help("Execute the shell command with $SHELL"))
.arg(
Arg::with_name("single-quotes")
.long("single-quotes")
.help("Can be 'always', 'auto', or 'never'"),
)
.arg(
Arg::with_name("double-quotes")
.long("double-quotes")
.help("Can be \"always\", \"auto\", or \"never\""),
)
.arg(
Arg::with_name("backticks")
.long("backticks")
.help("For more information see `echo test`"),
)
.arg(
Arg::with_name("backslash")
.long("backslash")
.help("Avoid '\\n'"),
)
.arg(
Arg::with_name("brackets")
.long("brackets")
.help("List packages [filter]"),
)
.arg(
Arg::with_name("expansions")
.long("expansions")
.help("Execute the shell command with $SHELL"),
)
}
#[test]

View file

@ -79,13 +79,19 @@ fn hidden_short_args() {
.short('c')
.long("config")
.hidden_short_help(true)
.help("Some help text describing the --config arg"),
.help("Some help text describing the --config arg"),
Arg::with_name("visible")
.short('v')
.long("visible")
.help("This text should be visible")]);
.help("This text should be visible"),
]);
assert!(test::compare_output(app, "test -h", HIDDEN_SHORT_ARGS, false));
assert!(test::compare_output(
app,
"test -h",
HIDDEN_SHORT_ARGS,
false
));
}
/// Ensure visible with opposite option
@ -100,13 +106,19 @@ fn hidden_short_args_long_help() {
.short('c')
.long("config")
.hidden_short_help(true)
.help("Some help text describing the --config arg"),
.help("Some help text describing the --config arg"),
Arg::with_name("visible")
.short('v')
.long("visible")
.help("This text should be visible")]);
.help("This text should be visible"),
]);
assert!(test::compare_output(app, "test --help", HIDDEN_SHORT_ARGS_LONG_HELP, false));
assert!(test::compare_output(
app,
"test --help",
HIDDEN_SHORT_ARGS_LONG_HELP,
false
));
}
static HIDDEN_LONG_ARGS: &'static str = "test 2.31.2
@ -137,13 +149,19 @@ fn hidden_long_args() {
.short('c')
.long("config")
.hidden_long_help(true)
.help("Some help text describing the --config arg"),
.help("Some help text describing the --config arg"),
Arg::with_name("visible")
.short('v')
.long("visible")
.help("This text should be visible")]);
.help("This text should be visible"),
]);
assert!(test::compare_output(app, "test --help", HIDDEN_LONG_ARGS, false));
assert!(test::compare_output(
app,
"test --help",
HIDDEN_LONG_ARGS,
false
));
}
static HIDDEN_LONG_ARGS_SHORT_HELP: &'static str = "test 2.31.2
@ -170,11 +188,17 @@ fn hidden_long_args_short_help() {
.short('c')
.long("config")
.hidden_long_help(true)
.help("Some help text describing the --config arg"),
.help("Some help text describing the --config arg"),
Arg::with_name("visible")
.short('v')
.long("visible")
.help("This text should be visible")]);
.help("This text should be visible"),
]);
assert!(test::compare_output(app, "test -h", HIDDEN_LONG_ARGS_SHORT_HELP, false));
}
assert!(test::compare_output(
app,
"test -h",
HIDDEN_LONG_ARGS_SHORT_HELP,
false
));
}

View file

@ -109,9 +109,9 @@ fn quoted_arg_long_name() {
(@arg scpositional: index(1) "tests positionals"))
);
let matches =
app.get_matches_from_safe(vec!["bin_name", "value1", "value2", "--long-option-2"])
.expect("Expected to successfully match the given args.");
let matches = app
.get_matches_from_safe(vec!["bin_name", "value1", "value2", "--long-option-2"])
.expect("Expected to successfully match the given args.");
assert!(matches.is_present("option2"));
}
@ -147,9 +147,9 @@ fn quoted_arg_name() {
(@arg scpositional: index(1) "tests positionals"))
);
let matches =
app.get_matches_from_safe(vec!["bin_name", "value1", "value2", "--long-option-2"])
.expect("Expected to successfully match the given args.");
let matches = app
.get_matches_from_safe(vec!["bin_name", "value1", "value2", "--long-option-2"])
.expect("Expected to successfully match the given args.");
assert!(matches.is_present("option2"));
}

View file

@ -398,10 +398,7 @@ fn issue_1047_min_zero_vals_default_val() {
fn issue_1105_setup(argv: Vec<&'static str>) -> Result<ArgMatches<'static>, clap::Error> {
App::new("opts")
.arg(
Arg::from("-o, --option [opt] 'some option'")
.setting(ArgSettings::AllowEmptyValues),
)
.arg(Arg::from("-o, --option [opt] 'some option'").setting(ArgSettings::AllowEmptyValues))
.arg(Arg::from("--flag 'some flag'"))
.try_get_matches_from(argv)
}

View file

@ -4,8 +4,8 @@ use clap::{App, Arg, ErrorKind};
#[test]
fn flag_overrides_itself() {
let res = App::new("posix")
.arg(Arg::from("--flag 'some flag'").overrides_with("flag"))
.get_matches_from_safe(vec!["", "--flag", "--flag"]);
.arg(Arg::from("--flag 'some flag'").overrides_with("flag"))
.get_matches_from_safe(vec!["", "--flag", "--flag"]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(m.is_present("flag"));
@ -15,8 +15,8 @@ fn flag_overrides_itself() {
#[test]
fn mult_flag_overrides_itself() {
let res = App::new("posix")
.arg(Arg::from("--flag... 'some flag'").overrides_with("flag"))
.get_matches_from_safe(vec!["", "--flag", "--flag", "--flag", "--flag"]);
.arg(Arg::from("--flag... 'some flag'").overrides_with("flag"))
.get_matches_from_safe(vec!["", "--flag", "--flag", "--flag", "--flag"]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(m.is_present("flag"));
@ -26,8 +26,8 @@ fn mult_flag_overrides_itself() {
#[test]
fn option_overrides_itself() {
let res = App::new("posix")
.arg(Arg::from("--opt [val] 'some option'").overrides_with("opt"))
.get_matches_from_safe(vec!["", "--opt=some", "--opt=other"]);
.arg(Arg::from("--opt [val] 'some option'").overrides_with("opt"))
.get_matches_from_safe(vec!["", "--opt=some", "--opt=other"]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(m.is_present("opt"));
@ -38,54 +38,67 @@ fn option_overrides_itself() {
#[test]
fn mult_option_require_delim_overrides_itself() {
let res = App::new("posix")
.arg(Arg::from("--opt [val]... 'some option'")
.overrides_with("opt")
.number_of_values(1)
.require_delimiter(true))
.get_matches_from_safe(vec!["", "--opt=some", "--opt=other", "--opt=one,two"]);
.arg(
Arg::from("--opt [val]... 'some option'")
.overrides_with("opt")
.number_of_values(1)
.require_delimiter(true),
)
.get_matches_from_safe(vec!["", "--opt=some", "--opt=other", "--opt=one,two"]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(m.is_present("opt"));
assert_eq!(m.occurrences_of("opt"), 3);
assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["some", "other", "one", "two"]);
assert_eq!(
m.values_of("opt").unwrap().collect::<Vec<_>>(),
&["some", "other", "one", "two"]
);
}
#[test]
fn mult_option_overrides_itself() {
let res = App::new("posix")
.arg(Arg::from("--opt [val]... 'some option'")
.overrides_with("opt"))
.get_matches_from_safe(vec!["", "--opt", "first", "overides", "--opt", "some", "other", "val"]);
.arg(Arg::from("--opt [val]... 'some option'").overrides_with("opt"))
.get_matches_from_safe(vec![
"", "--opt", "first", "overides", "--opt", "some", "other", "val",
]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(m.is_present("opt"));
assert_eq!(m.occurrences_of("opt"), 2);
assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["first", "overides", "some", "other", "val"]);
assert_eq!(
m.values_of("opt").unwrap().collect::<Vec<_>>(),
&["first", "overides", "some", "other", "val"]
);
}
#[test]
fn option_use_delim_false_override_itself() {
let m = App::new("posix")
.arg(Arg::from("--opt [val] 'some option'")
.overrides_with("opt"))
.get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"]);
.arg(Arg::from("--opt [val] 'some option'").overrides_with("opt"))
.get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"]);
assert!(m.is_present("opt"));
assert_eq!(m.occurrences_of("opt"), 1);
assert_eq!(m.values_of("opt").unwrap().collect::<Vec<_>>(), &["one,two"]);
assert_eq!(
m.values_of("opt").unwrap().collect::<Vec<_>>(),
&["one,two"]
);
}
#[test]
fn pos_mult_overrides_itself() {
// opts with multiple
let res = App::new("posix")
.arg(Arg::from("[val]... 'some pos'").overrides_with("val"))
.get_matches_from_safe(vec!["", "some", "other", "value"]);
.arg(Arg::from("[val]... 'some pos'").overrides_with("val"))
.get_matches_from_safe(vec!["", "some", "other", "value"]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(m.is_present("val"));
assert_eq!(m.occurrences_of("val"), 3);
assert_eq!(m.values_of("val").unwrap().collect::<Vec<_>>(), &["some", "other", "value"]);
assert_eq!(
m.values_of("val").unwrap().collect::<Vec<_>>(),
&["some", "other", "value"]
);
}
#[test]
fn posix_compatible_flags_long() {

View file

@ -196,19 +196,15 @@ fn issue_753() {
"-l, --list 'List available interfaces (and stop there)'",
))
.arg(
Arg::from(
"-i, --iface=[INTERFACE] 'Ethernet interface for fetching NTP packets'",
).required_unless("list"),
Arg::from("-i, --iface=[INTERFACE] 'Ethernet interface for fetching NTP packets'")
.required_unless("list"),
)
.arg(
Arg::from("-f, --file=[TESTFILE] 'Fetch NTP packets from pcap file'")
.conflicts_with("iface")
.required_unless("list"),
)
.arg(
Arg::from("-s, --server=[SERVER_IP] 'NTP server IP address'")
.required_unless("list"),
)
.arg(Arg::from("-s, --server=[SERVER_IP] 'NTP server IP address'").required_unless("list"))
.arg(Arg::from("-p, --port=[SERVER_PORT] 'NTP server port'").default_value("123"))
.get_matches_from_safe(vec!["test", "--list"]);
assert!(m.is_ok());

View file

@ -220,15 +220,32 @@ fn issue_1161_multiple_hyphen_hyphen() {
.arg(Arg::with_name("eff").short('f'))
.arg(Arg::with_name("pea").short('p').takes_value(true))
.arg(Arg::with_name("slop").multiple(true).last(true))
.get_matches_from_safe(vec!["-f", "-p=bob", "--", "sloppy", "slop", "-a", "--", "subprogram", "position", "args"]);
.get_matches_from_safe(vec![
"-f",
"-p=bob",
"--",
"sloppy",
"slop",
"-a",
"--",
"subprogram",
"position",
"args",
]);
assert!(res.is_ok(), "{:?}", res.unwrap_err().kind);
let m = res.unwrap();
let expected = Some(vec!["sloppy", "slop", "-a", "--", "subprogram", "position", "args"]);
let actual = m
.values_of("slop")
.map(|vals| vals.collect::<Vec<_>>());
let expected = Some(vec![
"sloppy",
"slop",
"-a",
"--",
"subprogram",
"position",
"args",
]);
let actual = m.values_of("slop").map(|vals| vals.collect::<Vec<_>>());
assert_eq!(expected, actual);
}
}

View file

@ -2,9 +2,9 @@
extern crate clap;
use clap::{App, AppSettings, Arg, ErrorKind};
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;
use clap::{App, AppSettings, Arg, ErrorKind};
#[test]
fn invalid_utf8_strict_positional() {