mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
Merge branch 'master' into flag-subcommands
This commit is contained in:
commit
27441329f9
31 changed files with 335 additions and 279 deletions
|
@ -67,7 +67,7 @@ maintenance = {status = "actively-developed"}
|
|||
[dependencies]
|
||||
bitflags = "1.2"
|
||||
unicode-width = "0.1"
|
||||
textwrap = "0.11"
|
||||
textwrap = "0.12"
|
||||
indexmap = "1.0"
|
||||
os_str_bytes = { version = "2.3", features = ["raw"] }
|
||||
vec_map = "0.8"
|
||||
|
@ -75,7 +75,7 @@ strsim = { version = "0.10", optional = true }
|
|||
yaml-rust = { version = "0.4.1", optional = true }
|
||||
atty = { version = "0.2", optional = true }
|
||||
termcolor = { version = "1.1", optional = true }
|
||||
term_size = { version = "1.0.0-beta1", optional = true }
|
||||
terminal_size = { version = "0.1.12", optional = true }
|
||||
lazy_static = { version = "1", optional = true }
|
||||
clap_derive = { path = "./clap_derive", version = "3.0.0-beta.1", optional = true }
|
||||
|
||||
|
@ -90,7 +90,7 @@ default = ["suggestions", "color", "derive", "std", "cargo"]
|
|||
std = [] # support for no_std in a backwards-compatible way
|
||||
suggestions = ["strsim"]
|
||||
color = ["atty", "termcolor"]
|
||||
wrap_help = ["term_size", "textwrap/term_size"]
|
||||
wrap_help = ["terminal_size", "textwrap/terminal_size"]
|
||||
derive = ["clap_derive", "lazy_static"]
|
||||
yaml = ["yaml-rust"]
|
||||
cargo = [] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
|
||||
|
|
|
@ -350,12 +350,12 @@ fn main() {
|
|||
(about: "Does awesome things")
|
||||
(@arg CONFIG: -c --config +takes_value "Sets a custom config file")
|
||||
(@arg INPUT: +required "Sets the input file to use")
|
||||
(@arg debug: -d ... "Sets the level of debugging information")
|
||||
(@arg verbose: -v --verbose "Print test information verbosely")
|
||||
(@subcommand test =>
|
||||
(about: "controls testing features")
|
||||
(version: "1.3")
|
||||
(author: "Someone E. <someone_else@other.com>")
|
||||
(@arg verbose: -v --verbose "Print test information verbosely")
|
||||
(@arg debug: -d ... "Sets the level of debugging information")
|
||||
)
|
||||
).get_matches();
|
||||
|
||||
|
@ -410,7 +410,7 @@ To try out the pre-built [examples][examples], use the following steps:
|
|||
|
||||
To test out `clap`'s default auto-generated help/version follow these steps:
|
||||
* Create a new cargo project `$ cargo new fake --bin && cd fake`
|
||||
* Writer your program as described in the quick example section.
|
||||
* Write your program as described in the quick example section.
|
||||
* Build your program `$ cargo build --release`
|
||||
* Run with help or version `$ ./target/release/fake --help` or `$ ./target/release/fake --version`
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ struct Opt {
|
|||
// the long option will be translated by default to kebab case,
|
||||
// i.e. `--nb-cars`.
|
||||
/// Number of cars
|
||||
#[clap(short = "c", long)]
|
||||
#[clap(short = 'c', long)]
|
||||
nb_cars: Option<i32>,
|
||||
|
||||
/// admin_level to consider
|
||||
|
|
|
@ -26,7 +26,7 @@ struct Opt {
|
|||
// but this makes adding an argument after the values impossible:
|
||||
// my_program -D a=1 -D b=2 my_input_file
|
||||
// becomes invalid.
|
||||
#[clap(short = "D", parse(try_from_str = parse_key_val), number_of_values = 1)]
|
||||
#[clap(short = 'D', parse(try_from_str = parse_key_val), number_of_values = 1)]
|
||||
defines: Vec<(String, i32)>,
|
||||
}
|
||||
|
||||
|
|
|
@ -147,11 +147,7 @@ impl ToTokens for Method {
|
|||
fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
|
||||
let Method { ref name, ref args } = self;
|
||||
|
||||
let tokens = if name == "short" {
|
||||
quote!( .#name(#args.chars().nth(0).unwrap()) )
|
||||
} else {
|
||||
quote!( .#name(#args) )
|
||||
};
|
||||
let tokens = quote!( .#name(#args) );
|
||||
|
||||
tokens.to_tokens(ts);
|
||||
}
|
||||
|
@ -242,6 +238,28 @@ impl Name {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_char(self, style: CasingStyle) -> TokenStream {
|
||||
use CasingStyle::*;
|
||||
|
||||
match self {
|
||||
Name::Assigned(tokens) => quote!( (#tokens).chars().next().unwrap() ),
|
||||
Name::Derived(ident) => {
|
||||
let s = ident.unraw().to_string();
|
||||
let s = match style {
|
||||
Pascal => s.to_camel_case(),
|
||||
Kebab => s.to_kebab_case(),
|
||||
Camel => s.to_mixed_case(),
|
||||
ScreamingSnake => s.to_shouty_snake_case(),
|
||||
Snake => s.to_snake_case(),
|
||||
Verbatim => s,
|
||||
};
|
||||
|
||||
let s = s.chars().next().unwrap();
|
||||
quote_spanned!(ident.span()=> #s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Attrs {
|
||||
|
@ -284,7 +302,11 @@ impl Attrs {
|
|||
|
||||
for attr in parse_clap_attributes(attrs) {
|
||||
match attr {
|
||||
Short(ident) | Long(ident) => {
|
||||
Short(ident) => {
|
||||
self.push_method(ident, self.name.clone().translate_char(*self.casing));
|
||||
}
|
||||
|
||||
Long(ident) => {
|
||||
self.push_method(ident, self.name.clone().translate(*self.casing));
|
||||
}
|
||||
|
||||
|
|
|
@ -188,12 +188,18 @@ fn gen_from_subcommand(
|
|||
quote!(::std::string::String),
|
||||
quote!(values_of),
|
||||
)
|
||||
} else {
|
||||
} else if is_simple_ty(subty, "OsString") {
|
||||
(
|
||||
subty.span(),
|
||||
quote!(::std::ffi::OsString),
|
||||
quote!(values_of_os),
|
||||
)
|
||||
} else {
|
||||
abort!(
|
||||
ty.span(),
|
||||
"The type must be either `Vec<String>` or `Vec<OsString>` \
|
||||
to be used with `external_subcommand`."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,10 @@ pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> {
|
|||
subty_if(ty, |_| true)
|
||||
}
|
||||
|
||||
fn only_last_segment(ty: &syn::Type) -> Option<&PathSegment> {
|
||||
fn only_last_segment(mut ty: &syn::Type) -> Option<&PathSegment> {
|
||||
while let syn::Type::Group(syn::TypeGroup { elem, .. }) = ty {
|
||||
ty = elem;
|
||||
}
|
||||
match ty {
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
|
|
|
@ -99,7 +99,7 @@ fn test_standalone_short_generates_kebab_case() {
|
|||
fn test_custom_short_overwrites_default_name() {
|
||||
#[derive(Clap, Debug, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(short = "o")]
|
||||
#[clap(short = 'o')]
|
||||
foo_option: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ use clap::Clap;
|
|||
fn basic() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short = "a", long = "arg")]
|
||||
#[clap(short = 'a', long = "arg")]
|
||||
arg: Vec<i32>,
|
||||
}
|
||||
assert_eq!(Opt { arg: vec![24] }, Opt::parse_from(&["test", "-a24"]));
|
||||
|
|
|
@ -32,7 +32,7 @@ struct PathOpt {
|
|||
#[clap(short, parse(from_os_str))]
|
||||
option_path_1: Option<PathBuf>,
|
||||
|
||||
#[clap(short = "q", parse(from_os_str))]
|
||||
#[clap(short = 'q', parse(from_os_str))]
|
||||
option_path_2: Option<PathBuf>,
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ struct Occurrences {
|
|||
#[clap(short, parse(from_occurrences))]
|
||||
unsigned: usize,
|
||||
|
||||
#[clap(short = "r", parse(from_occurrences))]
|
||||
#[clap(short = 'r', parse(from_occurrences))]
|
||||
little_unsigned: u8,
|
||||
|
||||
#[clap(short, long, parse(from_occurrences = foo))]
|
||||
|
|
|
@ -7,7 +7,7 @@ use utils::*;
|
|||
fn explicit_short_long_no_rename() {
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short = ".", long = ".foo")]
|
||||
#[clap(short = '.', long = ".foo")]
|
||||
foo: Vec<String>,
|
||||
}
|
||||
|
||||
|
|
33
clap_derive/tests/nested.rs
Normal file
33
clap_derive/tests/nested.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
|
||||
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
|
||||
// Andrew Hobden (@hoverbear) <andrew@hoverbear.org>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
|
||||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
// Tests that clap_derive properly detects an `Option` field
|
||||
// that results from a macro expansion
|
||||
#[test]
|
||||
fn use_option() {
|
||||
macro_rules! expand_ty {
|
||||
($name:ident: $ty:ty) => {
|
||||
#[derive(Clap)]
|
||||
struct Outer {
|
||||
#[clap(short, long)]
|
||||
#[allow(dead_code)]
|
||||
$name: $ty,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
expand_ty!(my_field: Option<String>);
|
||||
}
|
|
@ -30,7 +30,7 @@ struct Opt {
|
|||
)]
|
||||
x: i32,
|
||||
|
||||
#[clap(short = "l", long = "level", aliases = &["set-level", "lvl"])]
|
||||
#[clap(short = 'l', long = "level", aliases = &["set-level", "lvl"])]
|
||||
level: String,
|
||||
|
||||
#[clap(long("values"))]
|
||||
|
@ -129,7 +129,7 @@ fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
|
|||
|
||||
#[derive(Clap, PartialEq, Debug)]
|
||||
struct HexOpt {
|
||||
#[clap(short = "n", parse(try_from_str = parse_hex))]
|
||||
#[clap(short, parse(try_from_str = parse_hex))]
|
||||
number: u64,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
error: The type must be either `Vec<String>` or `Vec<OsString>` to be used with `external_subcommand`.
|
||||
--> $DIR/external_subcommand_wrong_type.rs:7:11
|
||||
|
|
||||
7 | Other(Vec<CString>),
|
||||
| ^^^
|
||||
|
||||
error: The type must be either `Vec<String>` or `Vec<OsString>` to be used with `external_subcommand`.
|
||||
--> $DIR/external_subcommand_wrong_type.rs:13:11
|
||||
|
|
||||
|
@ -10,12 +16,3 @@ error: The enum variant marked with `external_attribute` must be a single-typed
|
|||
18 | / #[clap(external_subcommand)]
|
||||
19 | | Other { a: String },
|
||||
| |_______________________^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/external_subcommand_wrong_type.rs:7:15
|
||||
|
|
||||
7 | Other(Vec<CString>),
|
||||
| ^^^^^^^ expected struct `std::ffi::CString`, found struct `std::ffi::OsString`
|
||||
|
|
||||
= note: expected struct `std::vec::Vec<std::ffi::CString>`
|
||||
found struct `std::vec::Vec<std::ffi::OsString>`
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Accept and endure. Do not touch.
|
||||
#![allow(unused)]
|
||||
|
||||
use clap::{find_subcmd_mut, match_alias, IntoApp};
|
||||
use clap::IntoApp;
|
||||
|
||||
pub fn get_help<T: IntoApp>() -> String {
|
||||
let mut output = Vec::new();
|
||||
|
@ -33,7 +33,9 @@ pub fn get_long_help<T: IntoApp>() -> String {
|
|||
|
||||
pub fn get_subcommand_long_help<T: IntoApp>(subcmd: &str) -> String {
|
||||
let mut output = Vec::new();
|
||||
find_subcmd_mut!(<T as IntoApp>::into_app(), subcmd)
|
||||
<T as IntoApp>::into_app()
|
||||
.get_subcommands_mut()
|
||||
.find(|s| s.get_name() == subcmd)
|
||||
.unwrap()
|
||||
.write_long_help(&mut output)
|
||||
.unwrap();
|
||||
|
|
|
@ -4,7 +4,7 @@ mod shells;
|
|||
use std::io::Write;
|
||||
|
||||
// Internal
|
||||
use clap::{find_subcmd, flags, match_alias, App, AppSettings, Arg};
|
||||
use clap::{App, AppSettings, Arg};
|
||||
pub use shells::*;
|
||||
|
||||
/// Generator trait which can be used to write generators
|
||||
|
@ -61,11 +61,7 @@ pub trait Generator {
|
|||
fn all_subcommands(app: &App) -> Vec<(String, String)> {
|
||||
let mut subcmds: Vec<_> = Self::subcommands(app);
|
||||
|
||||
for sc_v in app
|
||||
.get_subcommands()
|
||||
.iter()
|
||||
.map(|s| Self::all_subcommands(&s))
|
||||
{
|
||||
for sc_v in app.get_subcommands().map(|s| Self::all_subcommands(&s)) {
|
||||
subcmds.extend(sc_v);
|
||||
}
|
||||
|
||||
|
@ -81,7 +77,7 @@ pub trait Generator {
|
|||
let mut app = p;
|
||||
|
||||
for sc in path {
|
||||
app = find_subcmd!(app, sc).unwrap();
|
||||
app = app.find_subcommand(sc).unwrap();
|
||||
}
|
||||
|
||||
app
|
||||
|
@ -123,7 +119,6 @@ pub trait Generator {
|
|||
|
||||
let mut shorts: Vec<char> = p
|
||||
.get_arguments()
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
if a.get_index().is_none() && a.get_short().is_some() {
|
||||
Some(a.get_short().unwrap())
|
||||
|
@ -151,7 +146,6 @@ pub trait Generator {
|
|||
|
||||
let mut longs: Vec<String> = p
|
||||
.get_arguments()
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
if a.get_index().is_none() && a.get_long().is_some() {
|
||||
Some(a.get_long().unwrap().to_string())
|
||||
|
@ -179,7 +173,7 @@ pub trait Generator {
|
|||
fn flags<'b>(p: &'b App<'b>) -> Vec<Arg> {
|
||||
debug!("flags: name={}", p.get_name());
|
||||
|
||||
let mut flags: Vec<_> = flags!(p).cloned().collect();
|
||||
let mut flags: Vec<_> = p.get_flags_no_heading().cloned().collect();
|
||||
|
||||
if flags.iter().find(|x| x.get_name() == "help").is_none() {
|
||||
flags.push(
|
||||
|
|
|
@ -146,7 +146,7 @@ fn option_details_for_path(app: &App, path: &str) -> String {
|
|||
let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
||||
let mut opts = String::new();
|
||||
|
||||
for o in opts!(p) {
|
||||
for o in p.get_opts_no_heading() {
|
||||
if let Some(l) = o.get_long() {
|
||||
opts = format!(
|
||||
"{}
|
||||
|
@ -201,7 +201,9 @@ fn all_options_for_path(app: &App, path: &str) -> String {
|
|||
longs = Bash::longs(p)
|
||||
.iter()
|
||||
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
|
||||
pos = positionals!(p).fold(String::new(), |acc, p| format!("{} {}", acc, p)),
|
||||
pos = p
|
||||
.get_positionals()
|
||||
.fold(String::new(), |acc, p| format!("{} {}", acc, p)),
|
||||
subcmds = scs.join(" "),
|
||||
);
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ fn generate_inner<'b>(
|
|||
let mut completions = String::new();
|
||||
let preamble = String::from("\n cand ");
|
||||
|
||||
for option in opts!(p) {
|
||||
for option in p.get_opts_no_heading() {
|
||||
if let Some(data) = option.get_short() {
|
||||
let tooltip = get_tooltip(option.get_about(), data);
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ fn gen_fish_inner(root_command: &str, app: &App, buffer: &mut String) {
|
|||
|
||||
debug!("gen_fish_inner: bin_name={}", bin_name);
|
||||
|
||||
for option in opts!(app) {
|
||||
for option in app.get_opts_no_heading() {
|
||||
let mut template = basic_template.clone();
|
||||
|
||||
if let Some(data) = option.get_short() {
|
||||
|
|
|
@ -84,7 +84,7 @@ fn generate_inner<'b>(
|
|||
let mut completions = String::new();
|
||||
let preamble = String::from("\n [CompletionResult]::new(");
|
||||
|
||||
for option in opts!(p) {
|
||||
for option in p.get_opts_no_heading() {
|
||||
if let Some(data) = option.get_short() {
|
||||
let tooltip = get_tooltip(option.get_about(), data);
|
||||
|
||||
|
|
|
@ -248,7 +248,7 @@ esac",
|
|||
name = p.get_name(),
|
||||
name_hyphen = p.get_bin_name().unwrap().replace(" ", "-"),
|
||||
subcommands = subcmds.join("\n"),
|
||||
pos = positionals!(p).count() + 1
|
||||
pos = p.get_positionals().count() + 1
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -260,7 +260,7 @@ fn parser_of<'b>(p: &'b App<'b>, mut sc: &str) -> &'b App<'b> {
|
|||
}
|
||||
|
||||
sc = sc.split(' ').last().unwrap();
|
||||
find_subcmd!(p, sc).expect(INTERNAL_ERROR_MSG)
|
||||
p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG)
|
||||
}
|
||||
|
||||
// Writes out the args section, which ends up being the flags, opts and postionals, and a jump to
|
||||
|
@ -354,7 +354,7 @@ fn write_opts_of(p: &App) -> String {
|
|||
|
||||
let mut ret = vec![];
|
||||
|
||||
for o in opts!(p) {
|
||||
for o in p.get_opts_no_heading() {
|
||||
debug!("write_opts_of:iter: o={}", o.get_name());
|
||||
|
||||
let help = o.get_about().map_or(String::new(), escape_help);
|
||||
|
@ -490,7 +490,7 @@ fn write_positionals_of(p: &App) -> String {
|
|||
|
||||
let mut ret = vec![];
|
||||
|
||||
for arg in positionals!(p) {
|
||||
for arg in p.get_positionals() {
|
||||
debug!("write_positionals_of:iter: arg={}", arg.get_name());
|
||||
|
||||
let optional = if !arg.is_set(ArgSettings::Required) {
|
||||
|
|
4
justfile
4
justfile
|
@ -25,8 +25,8 @@ run-tests:
|
|||
cargo bench
|
||||
|
||||
@lint:
|
||||
rustup add component clippy
|
||||
rustup add component rustfmt
|
||||
rustup component add clippy
|
||||
rustup component add rustfmt
|
||||
cargo clippy --lib --features "yaml unstable" -- -D warnings
|
||||
cargo clippy --tests --examples --features "yaml unstable"
|
||||
cargo fmt -- --check
|
||||
|
|
|
@ -165,20 +165,40 @@ impl<'b> App<'b> {
|
|||
|
||||
/// Get the list of subcommands
|
||||
#[inline]
|
||||
pub fn get_subcommands(&self) -> &[App<'b>] {
|
||||
&self.subcommands
|
||||
pub fn get_subcommands(&self) -> impl Iterator<Item = &App<'b>> {
|
||||
self.subcommands.iter()
|
||||
}
|
||||
|
||||
/// Get the list of subcommands
|
||||
/// Iterate through the set of subcommands, getting a mutable reference to each.
|
||||
#[inline]
|
||||
pub fn get_subcommands_mut(&mut self) -> &mut [App<'b>] {
|
||||
&mut self.subcommands
|
||||
pub fn get_subcommands_mut(&mut self) -> impl Iterator<Item = &mut App<'b>> {
|
||||
self.subcommands.iter_mut()
|
||||
}
|
||||
|
||||
/// Get the list of arguments
|
||||
/// Iterate through the set of arguments
|
||||
#[inline]
|
||||
pub fn get_arguments(&self) -> &[Arg<'b>] {
|
||||
&self.args.args
|
||||
pub fn get_arguments(&self) -> impl Iterator<Item = &Arg<'b>> {
|
||||
self.args.args.iter()
|
||||
}
|
||||
|
||||
/// Get the list of *positional* arguments.
|
||||
#[inline]
|
||||
pub fn get_positionals(&self) -> impl Iterator<Item = &Arg<'b>> {
|
||||
self.get_arguments().filter(|a| a.is_positional())
|
||||
}
|
||||
|
||||
/// Iterate through the *flags* that don't have custom heading.
|
||||
pub fn get_flags_no_heading(&self) -> impl Iterator<Item = &Arg<'b>> {
|
||||
self.get_arguments()
|
||||
.filter(|a| !a.is_set(ArgSettings::TakesValue) && a.get_index().is_none())
|
||||
.filter(|a| a.get_help_heading().is_none())
|
||||
}
|
||||
|
||||
/// Iterate through the *options* that don't have custom heading.
|
||||
pub fn get_opts_no_heading(&self) -> impl Iterator<Item = &Arg<'b>> {
|
||||
self.get_arguments()
|
||||
.filter(|a| a.is_set(ArgSettings::TakesValue) && a.get_index().is_none())
|
||||
.filter(|a| a.get_help_heading().is_none())
|
||||
}
|
||||
|
||||
/// Get the list of arguments the given argument conflicts with
|
||||
|
@ -210,6 +230,15 @@ impl<'b> App<'b> {
|
|||
pub fn has_subcommands(&self) -> bool {
|
||||
!self.subcommands.is_empty()
|
||||
}
|
||||
|
||||
/// Find subcommand such that its name or one of aliases equals `name`.
|
||||
#[inline]
|
||||
pub fn find_subcommand<T>(&self, name: &T) -> Option<&App<'b>>
|
||||
where
|
||||
T: PartialEq<str> + ?Sized,
|
||||
{
|
||||
self.get_subcommands().find(|s| s.aliases_to(name))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> App<'b> {
|
||||
|
@ -2204,6 +2233,7 @@ impl<'b> App<'b> {
|
|||
self.args.args.iter().find(|a| a.id == *arg_id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// Should we color the output?
|
||||
pub(crate) fn color(&self) -> ColorChoice {
|
||||
debug!("App::color: Color setting...");
|
||||
|
@ -2220,6 +2250,7 @@ impl<'b> App<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn contains_short(&self, s: char) -> bool {
|
||||
if !self.is_set(AppSettings::Built) {
|
||||
panic!("If App::_build hasn't been called, manually search through Arg shorts");
|
||||
|
@ -2228,24 +2259,29 @@ impl<'b> App<'b> {
|
|||
self.args.contains(s)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set(&mut self, s: AppSettings) {
|
||||
self.settings.set(s)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn unset(&mut self, s: AppSettings) {
|
||||
self.settings.unset(s)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn has_args(&self) -> bool {
|
||||
!self.args.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn has_opts(&self) -> bool {
|
||||
opts!(self).count() > 0
|
||||
self.get_opts_no_heading().count() > 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn has_flags(&self) -> bool {
|
||||
flags!(self).count() > 0
|
||||
self.get_flags_no_heading().count() > 0
|
||||
}
|
||||
|
||||
pub(crate) fn has_visible_subcommands(&self) -> bool {
|
||||
|
@ -2255,11 +2291,40 @@ impl<'b> App<'b> {
|
|||
.any(|sc| !sc.is_set(AppSettings::Hidden))
|
||||
}
|
||||
|
||||
/// Check if this subcommand can be referred to as `name`. In other words,
|
||||
/// check if `name` is the name of this subcommand or is one of its aliases.
|
||||
#[inline]
|
||||
pub(crate) fn aliases_to<T>(&self, name: &T) -> bool
|
||||
where
|
||||
T: PartialEq<str> + ?Sized,
|
||||
{
|
||||
*name == *self.get_name() || self.get_all_aliases().any(|alias| *name == *alias)
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) fn id_exists(&self, id: &Id) -> bool {
|
||||
self.args.args.iter().any(|x| x.id == *id) || self.groups.iter().any(|x| x.id == *id)
|
||||
}
|
||||
|
||||
/// Iterate through the groups this arg is member of.
|
||||
pub(crate) fn groups_for_arg<'a>(&'a self, arg: &'_ Id) -> impl Iterator<Item = Id> + 'a {
|
||||
debug!("App::groups_for_arg: id={:?}", arg);
|
||||
let arg = arg.clone();
|
||||
self.groups
|
||||
.iter()
|
||||
.filter(move |grp| grp.args.iter().any(|a| a == &arg))
|
||||
.map(|grp| grp.id.clone())
|
||||
}
|
||||
|
||||
/// Iterate through all the names of all subcommands (not recursively), including aliases.
|
||||
/// Used for suggestions.
|
||||
pub(crate) fn all_subcommand_names(&self) -> impl Iterator<Item = &str> {
|
||||
self.get_subcommands().map(|s| s.get_name()).chain(
|
||||
self.get_subcommands()
|
||||
.flat_map(|s| s.aliases.iter().map(|&(n, _)| n)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn unroll_args_in_group(&self, group: &Id) -> Vec<Id> {
|
||||
debug!("App::unroll_args_in_group: group={:?}", group);
|
||||
let mut g_vec = vec![group];
|
||||
|
|
|
@ -3005,8 +3005,8 @@ impl<'help> Arg<'help> {
|
|||
///
|
||||
/// **WARNING**: When building your CLIs, consider the effects of allowing leading hyphens and
|
||||
/// the user passing in a value that matches a valid short. For example `prog -opt -F` where
|
||||
/// `-F` is supposed to be a value, yet `-F` is *also* a valid short for another arg. Care should
|
||||
/// should be taken when designing these args. This is compounded by the ability to "stack"
|
||||
/// `-F` is supposed to be a value, yet `-F` is *also* a valid short for another arg.
|
||||
/// Care should be taken when designing these args. This is compounded by the ability to "stack"
|
||||
/// short args. I.e. if `-val` is supposed to be a value, but `-v`, `-a`, and `-l` are all valid
|
||||
/// shorts.
|
||||
///
|
||||
|
|
127
src/macros.rs
127
src/macros.rs
|
@ -272,7 +272,7 @@ macro_rules! app_from_crate {
|
|||
///
|
||||
/// # Alternative form for non-ident values
|
||||
///
|
||||
/// Certain places that normally accept an `ident`, will optionally accept an alternative of `("expr enclosed by parens")`
|
||||
/// Certain places that normally accept an `ident` also optionally accept an alternative of `("expr enclosed by parens")`
|
||||
/// * `(@arg something: --something)` could also be `(@arg ("something-else"): --("something-else"))`
|
||||
/// * `(@subcommand something => ...)` could also be `(@subcommand ("something-else") => ...)`
|
||||
///
|
||||
|
@ -552,128 +552,3 @@ macro_rules! debug {
|
|||
macro_rules! debug {
|
||||
($($arg:tt)*) => {};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! flags {
|
||||
($app:expr, $how:ident) => {{
|
||||
$app.get_arguments()
|
||||
.$how()
|
||||
.filter(|a| !a.is_set($crate::ArgSettings::TakesValue) && a.get_index().is_none())
|
||||
.filter(|a| !a.get_help_heading().is_some())
|
||||
}};
|
||||
($app:expr) => {
|
||||
$crate::flags!($app, iter)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! opts {
|
||||
($app:expr, $how:ident) => {{
|
||||
$app.get_arguments()
|
||||
.$how()
|
||||
.filter(|a| a.is_set($crate::ArgSettings::TakesValue) && a.get_index().is_none())
|
||||
.filter(|a| !a.get_help_heading().is_some())
|
||||
}};
|
||||
($app:expr) => {
|
||||
opts!($app, iter)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! positionals {
|
||||
($app:expr) => {{
|
||||
$app.get_arguments()
|
||||
.iter()
|
||||
.filter(|a| !(a.get_short().is_some() || a.get_long().is_some()))
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! groups_for_arg {
|
||||
($app:expr, $grp:expr) => {{
|
||||
debug!("groups_for_arg: name={:?}", $grp);
|
||||
$app.groups
|
||||
.iter()
|
||||
.filter(|grp| grp.args.iter().any(|a| a == $grp))
|
||||
.map(|grp| grp.id.clone())
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! find_subcmd_cloned {
|
||||
($app:expr, $sc:expr) => {{
|
||||
$app.get_subcommands()
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|a| match_alias!(a, $sc, a.get_name()))
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! find_subcmd {
|
||||
($app:expr, $sc:expr) => {{
|
||||
$app.get_subcommands()
|
||||
.iter()
|
||||
.find(|a| match_alias!(a, $sc, a.get_name()))
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! find_subcmd_mut {
|
||||
($app:expr, $sc:expr) => {{
|
||||
$app.get_subcommands_mut()
|
||||
.iter_mut()
|
||||
.find(|a| match_alias!(a, $sc, a.get_name()))
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! longs {
|
||||
($app:expr, $how:ident) => {{
|
||||
use crate::mkeymap::KeyType;
|
||||
$app.args.keys.iter().map(|x| &x.key).filter_map(|a| {
|
||||
if let KeyType::Long(v) = a {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}};
|
||||
($app:expr) => {
|
||||
longs!($app, iter)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! names {
|
||||
(@args $app:expr) => {{
|
||||
$app.get_arguments().iter().map(|a| &*a.get_name())
|
||||
}};
|
||||
(@sc $app:expr) => {{
|
||||
$app.get_subcommands().iter().map(|s| &*s.get_name()).chain(
|
||||
$app.get_subcommands()
|
||||
.iter()
|
||||
.filter(|s| !s.aliases.is_empty()) // REFACTOR
|
||||
.flat_map(|s| s.aliases.iter().map(|&(n, _)| n)),
|
||||
)
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! sc_names {
|
||||
($app:expr) => {{
|
||||
names!(@sc $app)
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! match_alias {
|
||||
($a:expr, $to:expr, $what:expr) => {{
|
||||
$what == $to || $a.get_all_aliases().any(|alias| alias == $to)
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -23,11 +23,12 @@ use crate::{
|
|||
use indexmap::IndexSet;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
#[cfg(not(feature = "wrap_help"))]
|
||||
mod term_size {
|
||||
pub(crate) fn dimensions() -> Option<(usize, usize)> {
|
||||
None
|
||||
}
|
||||
pub(crate) fn dimensions() -> Option<(usize, usize)> {
|
||||
#[cfg(not(feature = "wrap_help"))]
|
||||
return None;
|
||||
|
||||
#[cfg(feature = "wrap_help")]
|
||||
terminal_size::terminal_size().map(|(w, h)| (w.0.into(), h.0.into()))
|
||||
}
|
||||
|
||||
fn str_width(s: &str) -> usize {
|
||||
|
@ -80,7 +81,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
|||
Some(0) => usize::MAX,
|
||||
Some(w) => w,
|
||||
None => cmp::min(
|
||||
term_size::dimensions().map_or(100, |(w, _)| w),
|
||||
dimensions().map_or(100, |(w, _)| w),
|
||||
match parser.app.max_w {
|
||||
None | Some(0) => usize::MAX,
|
||||
Some(mw) => mw,
|
||||
|
@ -663,15 +664,18 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
|||
pub(crate) fn write_all_args(&mut self) -> ClapResult<()> {
|
||||
debug!("Help::write_all_args");
|
||||
let flags = self.parser.has_flags();
|
||||
// Strange filter/count vs fold... https://github.com/rust-lang/rust/issues/33038
|
||||
let pos = positionals!(self.parser.app).fold(0, |acc, arg| {
|
||||
// FIXME: Strange filter/count vs fold... https://github.com/rust-lang/rust/issues/33038
|
||||
let pos = self.parser.app.get_positionals().fold(0, |acc, arg| {
|
||||
if should_show_arg(self.use_long, arg) {
|
||||
acc + 1
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
}) > 0;
|
||||
let opts = opts!(self.parser.app)
|
||||
let opts = self
|
||||
.parser
|
||||
.app
|
||||
.get_opts_no_heading()
|
||||
.filter(|arg| should_show_arg(self.use_long, arg))
|
||||
.collect::<Vec<_>>();
|
||||
let subcmds = self.parser.has_visible_subcommands();
|
||||
|
@ -687,7 +691,7 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
|||
|
||||
let mut first = if pos {
|
||||
self.warning("ARGS:\n")?;
|
||||
self.write_args_unsorted(&*positionals!(self.parser.app).collect::<Vec<_>>())?;
|
||||
self.write_args_unsorted(&self.parser.app.get_positionals().collect::<Vec<_>>())?;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
|
@ -716,8 +720,8 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
|||
self.none("\n\n")?;
|
||||
}
|
||||
self.warning("FLAGS:\n")?;
|
||||
let flags_v: Vec<_> = flags!(self.parser.app).collect();
|
||||
self.write_args(&*flags_v)?;
|
||||
let flags_v: Vec<_> = self.parser.app.get_flags_no_heading().collect();
|
||||
self.write_args(&flags_v)?;
|
||||
first = false;
|
||||
}
|
||||
if !opts.is_empty() {
|
||||
|
@ -1066,16 +1070,16 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
|||
.iter()
|
||||
.filter(|a| a.has_switch())
|
||||
.collect::<Vec<_>>();
|
||||
self.write_args(&*opts_flags)?;
|
||||
self.write_args(&opts_flags)?;
|
||||
}
|
||||
b"flags" => {
|
||||
self.write_args(&*flags!(self.parser.app).collect::<Vec<_>>())?;
|
||||
self.write_args(&self.parser.app.get_flags_no_heading().collect::<Vec<_>>())?;
|
||||
}
|
||||
b"options" => {
|
||||
self.write_args(&*opts!(self.parser.app).collect::<Vec<_>>())?;
|
||||
self.write_args(&self.parser.app.get_opts_no_heading().collect::<Vec<_>>())?;
|
||||
}
|
||||
b"positionals" => {
|
||||
self.write_args(&*positionals!(self.parser.app).collect::<Vec<_>>())?;
|
||||
self.write_args(&self.parser.app.get_positionals().collect::<Vec<_>>())?;
|
||||
}
|
||||
b"subcommands" => {
|
||||
self.write_subcommands(self.parser.app)?;
|
||||
|
|
|
@ -71,7 +71,10 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
usage.push_str(" [OPTIONS]");
|
||||
}
|
||||
if !self.p.is_set(AS::UnifiedHelpMessage)
|
||||
&& opts!(self.p.app)
|
||||
&& self
|
||||
.p
|
||||
.app
|
||||
.get_opts_no_heading()
|
||||
.any(|o| !o.is_set(ArgSettings::Required) && !o.is_set(ArgSettings::Hidden))
|
||||
{
|
||||
usage.push_str(" [OPTIONS]");
|
||||
|
@ -79,11 +82,23 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
|
||||
usage.push_str(&req_string[..]);
|
||||
|
||||
let has_last = positionals!(self.p.app).any(|p| p.is_set(ArgSettings::Last));
|
||||
let has_last = self
|
||||
.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.any(|p| p.is_set(ArgSettings::Last));
|
||||
// places a '--' in the usage string if there are args and options
|
||||
// supporting multiple values
|
||||
if opts!(self.p.app).any(|o| o.is_set(ArgSettings::MultipleValues))
|
||||
&& positionals!(self.p.app).any(|p| !p.is_set(ArgSettings::Required))
|
||||
if self
|
||||
.p
|
||||
.app
|
||||
.get_opts_no_heading()
|
||||
.any(|o| o.is_set(ArgSettings::MultipleValues))
|
||||
&& self
|
||||
.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.any(|p| !p.is_set(ArgSettings::Required))
|
||||
&& !(self.p.app.has_visible_subcommands()
|
||||
|| self.p.is_set(AS::AllowExternalSubcommands))
|
||||
&& !has_last
|
||||
|
@ -94,19 +109,28 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
(!p.is_set(ArgSettings::Required) || p.is_set(ArgSettings::Last))
|
||||
&& !p.is_set(ArgSettings::Hidden)
|
||||
};
|
||||
if positionals!(self.p.app).any(not_req_or_hidden) {
|
||||
if self.p.app.get_positionals().any(not_req_or_hidden) {
|
||||
if let Some(args_tag) = self.get_args_tag(incl_reqs) {
|
||||
usage.push_str(&*args_tag);
|
||||
} else {
|
||||
usage.push_str(" [ARGS]");
|
||||
}
|
||||
if has_last && incl_reqs {
|
||||
let pos = positionals!(self.p.app)
|
||||
let pos = self
|
||||
.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.find(|p| p.is_set(ArgSettings::Last))
|
||||
.expect(INTERNAL_ERROR_MSG);
|
||||
debug!("Usage::create_help_usage: '{}' has .last(true)", pos.name);
|
||||
let req = pos.is_set(ArgSettings::Required);
|
||||
if req && positionals!(self.p.app).any(|p| !p.is_set(ArgSettings::Required)) {
|
||||
if req
|
||||
&& self
|
||||
.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.any(|p| !p.is_set(ArgSettings::Required))
|
||||
{
|
||||
usage.push_str(" -- <");
|
||||
} else if req {
|
||||
usage.push_str(" [--] <");
|
||||
|
@ -181,13 +205,16 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
fn get_args_tag(&self, incl_reqs: bool) -> Option<String> {
|
||||
debug!("Usage::get_args_tag; incl_reqs = {:?}", incl_reqs);
|
||||
let mut count = 0;
|
||||
'outer: for pos in positionals!(self.p.app)
|
||||
'outer: for pos in self
|
||||
.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Required))
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
||||
{
|
||||
debug!("Usage::get_args_tag:iter:{}", pos.name);
|
||||
for grp_s in groups_for_arg!(self.p.app, &pos.id) {
|
||||
for grp_s in self.p.app.groups_for_arg(&pos.id) {
|
||||
debug!("Usage::get_args_tag:iter:{:?}:iter:{:?}", pos.name, grp_s);
|
||||
// if it's part of a required group we don't want to count it
|
||||
if self
|
||||
|
@ -210,7 +237,10 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
debug!("Usage::get_args_tag:iter: More than one, returning [ARGS]");
|
||||
return None; // [ARGS]
|
||||
} else if count == 1 && incl_reqs {
|
||||
let pos = positionals!(self.p.app)
|
||||
let pos = self
|
||||
.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.find(|pos| {
|
||||
!pos.is_set(ArgSettings::Required)
|
||||
&& !pos.is_set(ArgSettings::Hidden)
|
||||
|
@ -232,7 +262,9 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
{
|
||||
debug!("Usage::get_args_tag:iter: Don't collapse returning all");
|
||||
return Some(
|
||||
positionals!(self.p.app)
|
||||
self.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Required))
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
||||
|
@ -242,7 +274,10 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
);
|
||||
} else if !incl_reqs {
|
||||
debug!("Usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
|
||||
let highest_req_pos = positionals!(self.p.app)
|
||||
let highest_req_pos = self
|
||||
.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.filter_map(|pos| {
|
||||
if pos.is_set(ArgSettings::Required) && !pos.is_set(ArgSettings::Last) {
|
||||
Some(pos.index)
|
||||
|
@ -251,16 +286,12 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
}
|
||||
})
|
||||
.max()
|
||||
.unwrap_or_else(|| Some(positionals!(self.p.app).count() as u64));
|
||||
.unwrap_or_else(|| Some(self.p.app.get_positionals().count() as u64));
|
||||
return Some(
|
||||
positionals!(self.p.app)
|
||||
.filter_map(|pos| {
|
||||
if pos.index <= highest_req_pos {
|
||||
Some(pos)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
self.p
|
||||
.app
|
||||
.get_positionals()
|
||||
.filter(|pos| pos.index <= highest_req_pos)
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Required))
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
||||
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
||||
|
@ -275,7 +306,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
// Determines if we need the `[FLAGS]` tag in the usage string
|
||||
fn needs_flags_tag(&self) -> bool {
|
||||
debug!("Usage::needs_flags_tag");
|
||||
'outer: for f in flags!(self.p.app) {
|
||||
'outer: for f in self.p.app.get_flags_no_heading() {
|
||||
debug!("Usage::needs_flags_tag:iter: f={}", f.name);
|
||||
if let Some(l) = f.long {
|
||||
if l == "help" || l == "version" {
|
||||
|
@ -283,7 +314,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
for grp_s in groups_for_arg!(self.p.app, &f.id) {
|
||||
for grp_s in self.p.app.groups_for_arg(&f.id) {
|
||||
debug!("Usage::needs_flags_tag:iter:iter: grp_s={:?}", grp_s);
|
||||
if self
|
||||
.p
|
||||
|
@ -356,7 +387,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
unrolled_reqs
|
||||
.iter()
|
||||
.chain(incls.iter())
|
||||
.filter(|a| positionals!(self.p.app).any(|p| &&p.id == a))
|
||||
.filter(|a| self.p.app.get_positionals().any(|p| &&p.id == a))
|
||||
.filter(|&pos| !m.contains(pos))
|
||||
.filter_map(|pos| self.p.app.find(pos))
|
||||
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
|
||||
|
@ -367,7 +398,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
unrolled_reqs
|
||||
.iter()
|
||||
.chain(incls.iter())
|
||||
.filter(|a| positionals!(self.p.app).any(|p| &&p.id == a))
|
||||
.filter(|a| self.p.app.get_positionals().any(|p| &&p.id == a))
|
||||
.filter_map(|pos| self.p.app.find(pos))
|
||||
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
|
||||
.filter(|pos| !args_in_groups.contains(&pos.id))
|
||||
|
@ -383,7 +414,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
|||
for a in unrolled_reqs
|
||||
.iter()
|
||||
.chain(incls.iter())
|
||||
.filter(|name| !positionals!(self.p.app).any(|p| &&p.id == name))
|
||||
.filter(|name| !self.p.app.get_positionals().any(|p| &&p.id == name))
|
||||
.filter(|name| !self.p.app.groups.iter().any(|g| &&g.id == name))
|
||||
.filter(|name| !args_in_groups.contains(name))
|
||||
.filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name)))
|
||||
|
|
|
@ -42,26 +42,30 @@ where
|
|||
T: AsRef<str>,
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
use crate::mkeymap::KeyType;
|
||||
|
||||
match did_you_mean(arg, longs).pop() {
|
||||
Some(ref candidate) => {
|
||||
return Some((candidate.to_owned(), None));
|
||||
}
|
||||
Some(candidate) => Some((candidate, None)),
|
||||
|
||||
None => {
|
||||
for subcommand in subcommands {
|
||||
subcommand._build();
|
||||
if let Some(ref candidate) = did_you_mean(
|
||||
arg,
|
||||
longs!(subcommand).map(|x| x.to_string_lossy().into_owned()),
|
||||
)
|
||||
.pop()
|
||||
{
|
||||
return Some((candidate.to_owned(), Some(subcommand.get_name().to_owned())));
|
||||
|
||||
let longs = subcommand.args.keys.iter().map(|x| &x.key).filter_map(|a| {
|
||||
if let KeyType::Long(v) = a {
|
||||
Some(v.to_string_lossy().into_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(candidate) = did_you_mean(arg, longs).pop() {
|
||||
return Some((candidate, Some(subcommand.get_name().to_string())));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(all(test, features = "suggestions"))]
|
||||
|
|
|
@ -171,7 +171,7 @@ where
|
|||
let only_highest = |a: &Arg| {
|
||||
a.is_set(ArgSettings::MultipleValues) && (a.index.unwrap_or(0) != highest_idx)
|
||||
};
|
||||
if positionals!(self.app).any(only_highest) {
|
||||
if self.app.get_positionals().any(only_highest) {
|
||||
// First we make sure if there is a positional that allows multiple values
|
||||
// the one before it (second to last) has one of these:
|
||||
// * a value terminator
|
||||
|
@ -206,7 +206,9 @@ where
|
|||
);
|
||||
|
||||
// Next we check how many have both Multiple and not a specific number of values set
|
||||
let count = positionals!(self.app)
|
||||
let count = self
|
||||
.app
|
||||
.get_positionals()
|
||||
.filter(|p| p.settings.is_set(ArgSettings::MultipleValues) && p.num_vals.is_none())
|
||||
.count();
|
||||
let ok = count <= 1
|
||||
|
@ -227,7 +229,7 @@ where
|
|||
let mut found = false;
|
||||
let mut foundx2 = false;
|
||||
|
||||
for p in positionals!(self.app) {
|
||||
for p in self.app.get_positionals() {
|
||||
if foundx2 && !p.is_set(ArgSettings::Required) {
|
||||
assert!(
|
||||
p.is_set(ArgSettings::Required),
|
||||
|
@ -283,13 +285,16 @@ where
|
|||
}
|
||||
}
|
||||
assert!(
|
||||
positionals!(self.app)
|
||||
self.app
|
||||
.get_positionals()
|
||||
.filter(|p| p.is_set(ArgSettings::Last))
|
||||
.count()
|
||||
< 2,
|
||||
"Only one positional argument may have last(true) set. Found two."
|
||||
);
|
||||
if positionals!(self.app)
|
||||
if self
|
||||
.app
|
||||
.get_positionals()
|
||||
.any(|p| p.is_set(ArgSettings::Last) && p.is_set(ArgSettings::Required))
|
||||
&& self.has_subcommands()
|
||||
&& !self.is_set(AS::SubcommandsNegateReqs)
|
||||
|
@ -336,7 +341,7 @@ where
|
|||
self._verify_positionals();
|
||||
|
||||
// Set the LowIndexMultiple flag if required
|
||||
if positionals!(self.app).any(|a| {
|
||||
if self.app.get_positionals().any(|a| {
|
||||
a.is_set(ArgSettings::MultipleValues)
|
||||
&& (a.index.unwrap_or(0) as usize
|
||||
!= self
|
||||
|
@ -346,7 +351,7 @@ where
|
|||
.iter()
|
||||
.filter(|x| x.key.is_position())
|
||||
.count())
|
||||
}) && positionals!(self.app).last().map_or(false, |p_name| {
|
||||
}) && self.app.get_positionals().last().map_or(false, |p_name| {
|
||||
!self.app[&p_name.id].is_set(ArgSettings::Last)
|
||||
}) {
|
||||
self.app.settings.set(AS::LowIndexMultiplePositional);
|
||||
|
@ -533,8 +538,10 @@ where
|
|||
|| self.is_set(AS::AllowExternalSubcommands)
|
||||
|| self.is_set(AS::InferSubcommands))
|
||||
{
|
||||
let cands =
|
||||
suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self.app));
|
||||
let cands = suggestions::did_you_mean(
|
||||
&*arg_os.to_string_lossy(),
|
||||
self.app.all_subcommand_names(),
|
||||
);
|
||||
if !cands.is_empty() {
|
||||
let cands: Vec<_> =
|
||||
cands.iter().map(|cand| format!("'{}'", cand)).collect();
|
||||
|
@ -578,8 +585,10 @@ where
|
|||
ParseResult::ValuesDone(id) => ParseResult::ValuesDone(id),
|
||||
|
||||
_ => {
|
||||
if let Some(p) =
|
||||
positionals!(self.app).find(|p| p.index == Some(pos_counter as u64))
|
||||
if let Some(p) = self
|
||||
.app
|
||||
.get_positionals()
|
||||
.find(|p| p.index == Some(pos_counter as u64))
|
||||
{
|
||||
ParseResult::Pos(p.id.clone())
|
||||
} else {
|
||||
|
@ -593,8 +602,11 @@ where
|
|||
|
||||
if self.is_new_arg(&n, &needs_val_of)
|
||||
|| sc_match
|
||||
|| !suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self.app))
|
||||
.is_empty()
|
||||
|| !suggestions::did_you_mean(
|
||||
&n.to_string_lossy(),
|
||||
self.app.all_subcommand_names(),
|
||||
)
|
||||
.is_empty()
|
||||
{
|
||||
debug!("Parser::get_matches_with: Bumping the positional counter...");
|
||||
pos_counter += 1;
|
||||
|
@ -653,7 +665,7 @@ where
|
|||
self.add_val_to_arg(p, &arg_os, matcher, ValueType::CommandLine)?;
|
||||
|
||||
matcher.inc_occurrence_of(&p.id);
|
||||
for grp in groups_for_arg!(self.app, &p.id) {
|
||||
for grp in self.app.groups_for_arg(&p.id) {
|
||||
matcher.inc_occurrence_of(&grp);
|
||||
}
|
||||
|
||||
|
@ -712,8 +724,10 @@ where
|
|||
self.app.color(),
|
||||
)?);
|
||||
} else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
|
||||
let cands =
|
||||
suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self.app));
|
||||
let cands = suggestions::did_you_mean(
|
||||
&*arg_os.to_string_lossy(),
|
||||
self.app.all_subcommand_names(),
|
||||
);
|
||||
if !cands.is_empty() {
|
||||
let cands: Vec<_> = cands.iter().map(|cand| format!("'{}'", cand)).collect();
|
||||
return Err(ClapError::invalid_subcommand(
|
||||
|
@ -742,7 +756,9 @@ where
|
|||
|
||||
if !external_subcommand {
|
||||
if let Some(ref pos_sc_name) = subcmd_name {
|
||||
let sc_name = find_subcmd!(self.app, *pos_sc_name)
|
||||
let sc_name = self
|
||||
.app
|
||||
.find_subcommand(pos_sc_name)
|
||||
.expect(INTERNAL_ERROR_MSG)
|
||||
.name
|
||||
.clone();
|
||||
|
@ -816,7 +832,7 @@ where
|
|||
}
|
||||
debug!("No");
|
||||
|
||||
for group in groups_for_arg!(self.app, &arg.id) {
|
||||
for group in self.app.groups_for_arg(&arg.id) {
|
||||
debug!("Parser::maybe_inc_pos_counter: group={:?}", group);
|
||||
let group = self.app.groups.iter().find(|g| g.id == group);
|
||||
|
||||
|
@ -842,7 +858,9 @@ where
|
|||
}
|
||||
|
||||
if self.is_set(AS::InferSubcommands) {
|
||||
let v = sc_names!(self.app)
|
||||
let v = self
|
||||
.app
|
||||
.all_subcommand_names()
|
||||
.filter(|s| arg_os.is_prefix_of(s))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -855,7 +873,7 @@ where
|
|||
return Some(sc);
|
||||
}
|
||||
}
|
||||
} else if let Some(sc) = find_subcmd!(self.app, arg_os) {
|
||||
} else if let Some(sc) = self.app.find_subcommand(arg_os) {
|
||||
return Some(&sc.name);
|
||||
}
|
||||
|
||||
|
@ -914,18 +932,18 @@ where
|
|||
help_help = true;
|
||||
break; // Maybe?
|
||||
}
|
||||
if let Some(id) = find_subcmd!(sc, cmd).map(|x| x.id.clone()) {
|
||||
if let Some(id) = sc.find_subcommand(cmd).map(|x| x.id.clone()) {
|
||||
sc._propagate(Propagation::To(id));
|
||||
}
|
||||
|
||||
if let Some(mut c) = find_subcmd_cloned!(sc, cmd) {
|
||||
if let Some(mut c) = sc.find_subcommand(cmd).cloned() {
|
||||
c._build();
|
||||
sc = c;
|
||||
|
||||
if i == cmds.len() - 1 {
|
||||
break;
|
||||
}
|
||||
} else if let Some(mut c) = find_subcmd_cloned!(sc, &cmd.to_string_lossy()) {
|
||||
} else if let Some(mut c) = sc.find_subcommand(&cmd.to_string_lossy()).cloned() {
|
||||
c._build();
|
||||
sc = c;
|
||||
|
||||
|
@ -1045,7 +1063,7 @@ where
|
|||
|
||||
mid_string.push_str(" ");
|
||||
|
||||
if let Some(x) = find_subcmd!(self.app, sc_name) {
|
||||
if let Some(x) = self.app.find_subcommand(sc_name) {
|
||||
let id = x.id.clone();
|
||||
self.app._propagate(Propagation::To(id));
|
||||
}
|
||||
|
@ -1387,7 +1405,7 @@ where
|
|||
|
||||
matcher.inc_occurrence_of(&opt.id);
|
||||
// Increment or create the group "args"
|
||||
for grp in groups_for_arg!(self.app, &opt.id) {
|
||||
for grp in self.app.groups_for_arg(&opt.id) {
|
||||
matcher.inc_occurrence_of(&grp);
|
||||
}
|
||||
|
||||
|
@ -1464,7 +1482,7 @@ where
|
|||
matcher.add_index_to(&arg.id, self.cur_idx.get(), ty);
|
||||
|
||||
// Increment or create the group "args"
|
||||
for grp in groups_for_arg!(self.app, &arg.id) {
|
||||
for grp in self.app.groups_for_arg(&arg.id) {
|
||||
matcher.add_val_to(&grp, v.to_os_string(), ty);
|
||||
}
|
||||
|
||||
|
@ -1480,7 +1498,7 @@ where
|
|||
matcher.inc_occurrence_of(&flag.id);
|
||||
matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueType::CommandLine);
|
||||
// Increment or create the group "args"
|
||||
for grp in groups_for_arg!(self.app, &flag.id) {
|
||||
for grp in self.app.groups_for_arg(&flag.id) {
|
||||
matcher.inc_occurrence_of(&grp);
|
||||
}
|
||||
|
||||
|
@ -1560,12 +1578,12 @@ where
|
|||
pub(crate) fn add_defaults(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
|
||||
debug!("Parser::add_defaults");
|
||||
|
||||
for o in opts!(self.app) {
|
||||
for o in self.app.get_opts_no_heading() {
|
||||
debug!("Parser::add_defaults:iter:{}:", o.name);
|
||||
self.add_value(o, matcher, ValueType::DefaultValue)?;
|
||||
}
|
||||
|
||||
for p in positionals!(self.app) {
|
||||
for p in self.app.get_positionals() {
|
||||
debug!("Parser::add_defaults:iter:{}:", p.name);
|
||||
self.add_value(p, matcher, ValueType::DefaultValue)?;
|
||||
}
|
||||
|
@ -1693,7 +1711,7 @@ where
|
|||
.args
|
||||
.get(&KeyType::Long(OsString::from(name.0.clone())))
|
||||
{
|
||||
for g in groups_for_arg!(self.app, &opt.id) {
|
||||
for g in self.app.groups_for_arg(&opt.id) {
|
||||
matcher.inc_occurrence_of(&g);
|
||||
}
|
||||
matcher.insert(&opt.id);
|
||||
|
|
|
@ -309,7 +309,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
|
|||
// args in that group to the conflicts, as well as any args those args conflict
|
||||
// with
|
||||
|
||||
for grp in groups_for_arg!(self.p.app, name) {
|
||||
for grp in self.p.app.groups_for_arg(&name) {
|
||||
if let Some(g) = self
|
||||
.p
|
||||
.app
|
||||
|
|
|
@ -64,7 +64,7 @@ pub fn compare_output2(l: App, args: &str, right1: &str, right2: &str, stderr: b
|
|||
compare(&*left, right1) || compare(&*left, right2)
|
||||
}
|
||||
|
||||
// Legacy tests from the pyhton script days
|
||||
// Legacy tests from the python script days
|
||||
|
||||
pub fn complex_app() -> App<'static> {
|
||||
let opt3_vals = ["fast", "slow"];
|
||||
|
|
Loading…
Reference in a new issue