mirror of
https://github.com/clap-rs/clap
synced 2024-11-14 00:27:13 +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]
|
[dependencies]
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
textwrap = "0.11"
|
textwrap = "0.12"
|
||||||
indexmap = "1.0"
|
indexmap = "1.0"
|
||||||
os_str_bytes = { version = "2.3", features = ["raw"] }
|
os_str_bytes = { version = "2.3", features = ["raw"] }
|
||||||
vec_map = "0.8"
|
vec_map = "0.8"
|
||||||
|
@ -75,7 +75,7 @@ strsim = { version = "0.10", optional = true }
|
||||||
yaml-rust = { version = "0.4.1", optional = true }
|
yaml-rust = { version = "0.4.1", optional = true }
|
||||||
atty = { version = "0.2", optional = true }
|
atty = { version = "0.2", optional = true }
|
||||||
termcolor = { version = "1.1", 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 }
|
lazy_static = { version = "1", optional = true }
|
||||||
clap_derive = { path = "./clap_derive", version = "3.0.0-beta.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
|
std = [] # support for no_std in a backwards-compatible way
|
||||||
suggestions = ["strsim"]
|
suggestions = ["strsim"]
|
||||||
color = ["atty", "termcolor"]
|
color = ["atty", "termcolor"]
|
||||||
wrap_help = ["term_size", "textwrap/term_size"]
|
wrap_help = ["terminal_size", "textwrap/terminal_size"]
|
||||||
derive = ["clap_derive", "lazy_static"]
|
derive = ["clap_derive", "lazy_static"]
|
||||||
yaml = ["yaml-rust"]
|
yaml = ["yaml-rust"]
|
||||||
cargo = [] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
|
cargo = [] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
|
||||||
|
|
|
@ -350,12 +350,12 @@ fn main() {
|
||||||
(about: "Does awesome things")
|
(about: "Does awesome things")
|
||||||
(@arg CONFIG: -c --config +takes_value "Sets a custom config file")
|
(@arg CONFIG: -c --config +takes_value "Sets a custom config file")
|
||||||
(@arg INPUT: +required "Sets the input file to use")
|
(@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 =>
|
(@subcommand test =>
|
||||||
(about: "controls testing features")
|
(about: "controls testing features")
|
||||||
(version: "1.3")
|
(version: "1.3")
|
||||||
(author: "Someone E. <someone_else@other.com>")
|
(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();
|
).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:
|
To test out `clap`'s default auto-generated help/version follow these steps:
|
||||||
* Create a new cargo project `$ cargo new fake --bin && cd fake`
|
* 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`
|
* Build your program `$ cargo build --release`
|
||||||
* Run with help or version `$ ./target/release/fake --help` or `$ ./target/release/fake --version`
|
* 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,
|
// the long option will be translated by default to kebab case,
|
||||||
// i.e. `--nb-cars`.
|
// i.e. `--nb-cars`.
|
||||||
/// Number of cars
|
/// Number of cars
|
||||||
#[clap(short = "c", long)]
|
#[clap(short = 'c', long)]
|
||||||
nb_cars: Option<i32>,
|
nb_cars: Option<i32>,
|
||||||
|
|
||||||
/// admin_level to consider
|
/// admin_level to consider
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct Opt {
|
||||||
// but this makes adding an argument after the values impossible:
|
// but this makes adding an argument after the values impossible:
|
||||||
// my_program -D a=1 -D b=2 my_input_file
|
// my_program -D a=1 -D b=2 my_input_file
|
||||||
// becomes invalid.
|
// 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)>,
|
defines: Vec<(String, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,11 +147,7 @@ impl ToTokens for Method {
|
||||||
fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
|
fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
|
||||||
let Method { ref name, ref args } = self;
|
let Method { ref name, ref args } = self;
|
||||||
|
|
||||||
let tokens = if name == "short" {
|
let tokens = quote!( .#name(#args) );
|
||||||
quote!( .#name(#args.chars().nth(0).unwrap()) )
|
|
||||||
} else {
|
|
||||||
quote!( .#name(#args) )
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.to_tokens(ts);
|
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 {
|
impl Attrs {
|
||||||
|
@ -284,7 +302,11 @@ impl Attrs {
|
||||||
|
|
||||||
for attr in parse_clap_attributes(attrs) {
|
for attr in parse_clap_attributes(attrs) {
|
||||||
match attr {
|
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));
|
self.push_method(ident, self.name.clone().translate(*self.casing));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -188,12 +188,18 @@ fn gen_from_subcommand(
|
||||||
quote!(::std::string::String),
|
quote!(::std::string::String),
|
||||||
quote!(values_of),
|
quote!(values_of),
|
||||||
)
|
)
|
||||||
} else {
|
} else if is_simple_ty(subty, "OsString") {
|
||||||
(
|
(
|
||||||
subty.span(),
|
subty.span(),
|
||||||
quote!(::std::ffi::OsString),
|
quote!(::std::ffi::OsString),
|
||||||
quote!(values_of_os),
|
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)
|
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 {
|
match ty {
|
||||||
Type::Path(TypePath {
|
Type::Path(TypePath {
|
||||||
qself: None,
|
qself: None,
|
||||||
|
|
|
@ -99,7 +99,7 @@ fn test_standalone_short_generates_kebab_case() {
|
||||||
fn test_custom_short_overwrites_default_name() {
|
fn test_custom_short_overwrites_default_name() {
|
||||||
#[derive(Clap, Debug, PartialEq)]
|
#[derive(Clap, Debug, PartialEq)]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
#[clap(short = "o")]
|
#[clap(short = 'o')]
|
||||||
foo_option: bool,
|
foo_option: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use clap::Clap;
|
||||||
fn basic() {
|
fn basic() {
|
||||||
#[derive(Clap, PartialEq, Debug)]
|
#[derive(Clap, PartialEq, Debug)]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
#[clap(short = "a", long = "arg")]
|
#[clap(short = 'a', long = "arg")]
|
||||||
arg: Vec<i32>,
|
arg: Vec<i32>,
|
||||||
}
|
}
|
||||||
assert_eq!(Opt { arg: vec![24] }, Opt::parse_from(&["test", "-a24"]));
|
assert_eq!(Opt { arg: vec![24] }, Opt::parse_from(&["test", "-a24"]));
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct PathOpt {
|
||||||
#[clap(short, parse(from_os_str))]
|
#[clap(short, parse(from_os_str))]
|
||||||
option_path_1: Option<PathBuf>,
|
option_path_1: Option<PathBuf>,
|
||||||
|
|
||||||
#[clap(short = "q", parse(from_os_str))]
|
#[clap(short = 'q', parse(from_os_str))]
|
||||||
option_path_2: Option<PathBuf>,
|
option_path_2: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ struct Occurrences {
|
||||||
#[clap(short, parse(from_occurrences))]
|
#[clap(short, parse(from_occurrences))]
|
||||||
unsigned: usize,
|
unsigned: usize,
|
||||||
|
|
||||||
#[clap(short = "r", parse(from_occurrences))]
|
#[clap(short = 'r', parse(from_occurrences))]
|
||||||
little_unsigned: u8,
|
little_unsigned: u8,
|
||||||
|
|
||||||
#[clap(short, long, parse(from_occurrences = foo))]
|
#[clap(short, long, parse(from_occurrences = foo))]
|
||||||
|
|
|
@ -7,7 +7,7 @@ use utils::*;
|
||||||
fn explicit_short_long_no_rename() {
|
fn explicit_short_long_no_rename() {
|
||||||
#[derive(Clap, PartialEq, Debug)]
|
#[derive(Clap, PartialEq, Debug)]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
#[clap(short = ".", long = ".foo")]
|
#[clap(short = '.', long = ".foo")]
|
||||||
foo: Vec<String>,
|
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,
|
x: i32,
|
||||||
|
|
||||||
#[clap(short = "l", long = "level", aliases = &["set-level", "lvl"])]
|
#[clap(short = 'l', long = "level", aliases = &["set-level", "lvl"])]
|
||||||
level: String,
|
level: String,
|
||||||
|
|
||||||
#[clap(long("values"))]
|
#[clap(long("values"))]
|
||||||
|
@ -129,7 +129,7 @@ fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
|
||||||
|
|
||||||
#[derive(Clap, PartialEq, Debug)]
|
#[derive(Clap, PartialEq, Debug)]
|
||||||
struct HexOpt {
|
struct HexOpt {
|
||||||
#[clap(short = "n", parse(try_from_str = parse_hex))]
|
#[clap(short, parse(try_from_str = parse_hex))]
|
||||||
number: u64,
|
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`.
|
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
|
--> $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)]
|
18 | / #[clap(external_subcommand)]
|
||||||
19 | | Other { a: String },
|
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.
|
// Accept and endure. Do not touch.
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
use clap::{find_subcmd_mut, match_alias, IntoApp};
|
use clap::IntoApp;
|
||||||
|
|
||||||
pub fn get_help<T: IntoApp>() -> String {
|
pub fn get_help<T: IntoApp>() -> String {
|
||||||
let mut output = Vec::new();
|
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 {
|
pub fn get_subcommand_long_help<T: IntoApp>(subcmd: &str) -> String {
|
||||||
let mut output = Vec::new();
|
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()
|
.unwrap()
|
||||||
.write_long_help(&mut output)
|
.write_long_help(&mut output)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -4,7 +4,7 @@ mod shells;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
use clap::{find_subcmd, flags, match_alias, App, AppSettings, Arg};
|
use clap::{App, AppSettings, Arg};
|
||||||
pub use shells::*;
|
pub use shells::*;
|
||||||
|
|
||||||
/// Generator trait which can be used to write generators
|
/// Generator trait which can be used to write generators
|
||||||
|
@ -61,11 +61,7 @@ pub trait Generator {
|
||||||
fn all_subcommands(app: &App) -> Vec<(String, String)> {
|
fn all_subcommands(app: &App) -> Vec<(String, String)> {
|
||||||
let mut subcmds: Vec<_> = Self::subcommands(app);
|
let mut subcmds: Vec<_> = Self::subcommands(app);
|
||||||
|
|
||||||
for sc_v in app
|
for sc_v in app.get_subcommands().map(|s| Self::all_subcommands(&s)) {
|
||||||
.get_subcommands()
|
|
||||||
.iter()
|
|
||||||
.map(|s| Self::all_subcommands(&s))
|
|
||||||
{
|
|
||||||
subcmds.extend(sc_v);
|
subcmds.extend(sc_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +77,7 @@ pub trait Generator {
|
||||||
let mut app = p;
|
let mut app = p;
|
||||||
|
|
||||||
for sc in path {
|
for sc in path {
|
||||||
app = find_subcmd!(app, sc).unwrap();
|
app = app.find_subcommand(sc).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
app
|
app
|
||||||
|
@ -123,7 +119,6 @@ pub trait Generator {
|
||||||
|
|
||||||
let mut shorts: Vec<char> = p
|
let mut shorts: Vec<char> = p
|
||||||
.get_arguments()
|
.get_arguments()
|
||||||
.iter()
|
|
||||||
.filter_map(|a| {
|
.filter_map(|a| {
|
||||||
if a.get_index().is_none() && a.get_short().is_some() {
|
if a.get_index().is_none() && a.get_short().is_some() {
|
||||||
Some(a.get_short().unwrap())
|
Some(a.get_short().unwrap())
|
||||||
|
@ -151,7 +146,6 @@ pub trait Generator {
|
||||||
|
|
||||||
let mut longs: Vec<String> = p
|
let mut longs: Vec<String> = p
|
||||||
.get_arguments()
|
.get_arguments()
|
||||||
.iter()
|
|
||||||
.filter_map(|a| {
|
.filter_map(|a| {
|
||||||
if a.get_index().is_none() && a.get_long().is_some() {
|
if a.get_index().is_none() && a.get_long().is_some() {
|
||||||
Some(a.get_long().unwrap().to_string())
|
Some(a.get_long().unwrap().to_string())
|
||||||
|
@ -179,7 +173,7 @@ pub trait Generator {
|
||||||
fn flags<'b>(p: &'b App<'b>) -> Vec<Arg> {
|
fn flags<'b>(p: &'b App<'b>) -> Vec<Arg> {
|
||||||
debug!("flags: name={}", p.get_name());
|
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() {
|
if flags.iter().find(|x| x.get_name() == "help").is_none() {
|
||||||
flags.push(
|
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 p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
||||||
let mut opts = String::new();
|
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() {
|
if let Some(l) = o.get_long() {
|
||||||
opts = format!(
|
opts = format!(
|
||||||
"{}
|
"{}
|
||||||
|
@ -201,7 +201,9 @@ fn all_options_for_path(app: &App, path: &str) -> String {
|
||||||
longs = Bash::longs(p)
|
longs = Bash::longs(p)
|
||||||
.iter()
|
.iter()
|
||||||
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
|
.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(" "),
|
subcmds = scs.join(" "),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ fn generate_inner<'b>(
|
||||||
let mut completions = String::new();
|
let mut completions = String::new();
|
||||||
let preamble = String::from("\n cand ");
|
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() {
|
if let Some(data) = option.get_short() {
|
||||||
let tooltip = get_tooltip(option.get_about(), data);
|
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);
|
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();
|
let mut template = basic_template.clone();
|
||||||
|
|
||||||
if let Some(data) = option.get_short() {
|
if let Some(data) = option.get_short() {
|
||||||
|
|
|
@ -84,7 +84,7 @@ fn generate_inner<'b>(
|
||||||
let mut completions = String::new();
|
let mut completions = String::new();
|
||||||
let preamble = String::from("\n [CompletionResult]::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() {
|
if let Some(data) = option.get_short() {
|
||||||
let tooltip = get_tooltip(option.get_about(), data);
|
let tooltip = get_tooltip(option.get_about(), data);
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ esac",
|
||||||
name = p.get_name(),
|
name = p.get_name(),
|
||||||
name_hyphen = p.get_bin_name().unwrap().replace(" ", "-"),
|
name_hyphen = p.get_bin_name().unwrap().replace(" ", "-"),
|
||||||
subcommands = subcmds.join("\n"),
|
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();
|
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
|
// 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![];
|
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());
|
debug!("write_opts_of:iter: o={}", o.get_name());
|
||||||
|
|
||||||
let help = o.get_about().map_or(String::new(), escape_help);
|
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![];
|
let mut ret = vec![];
|
||||||
|
|
||||||
for arg in positionals!(p) {
|
for arg in p.get_positionals() {
|
||||||
debug!("write_positionals_of:iter: arg={}", arg.get_name());
|
debug!("write_positionals_of:iter: arg={}", arg.get_name());
|
||||||
|
|
||||||
let optional = if !arg.is_set(ArgSettings::Required) {
|
let optional = if !arg.is_set(ArgSettings::Required) {
|
||||||
|
|
4
justfile
4
justfile
|
@ -25,8 +25,8 @@ run-tests:
|
||||||
cargo bench
|
cargo bench
|
||||||
|
|
||||||
@lint:
|
@lint:
|
||||||
rustup add component clippy
|
rustup component add clippy
|
||||||
rustup add component rustfmt
|
rustup component add rustfmt
|
||||||
cargo clippy --lib --features "yaml unstable" -- -D warnings
|
cargo clippy --lib --features "yaml unstable" -- -D warnings
|
||||||
cargo clippy --tests --examples --features "yaml unstable"
|
cargo clippy --tests --examples --features "yaml unstable"
|
||||||
cargo fmt -- --check
|
cargo fmt -- --check
|
||||||
|
|
|
@ -165,20 +165,40 @@ impl<'b> App<'b> {
|
||||||
|
|
||||||
/// Get the list of subcommands
|
/// Get the list of subcommands
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_subcommands(&self) -> &[App<'b>] {
|
pub fn get_subcommands(&self) -> impl Iterator<Item = &App<'b>> {
|
||||||
&self.subcommands
|
self.subcommands.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the list of subcommands
|
/// Iterate through the set of subcommands, getting a mutable reference to each.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_subcommands_mut(&mut self) -> &mut [App<'b>] {
|
pub fn get_subcommands_mut(&mut self) -> impl Iterator<Item = &mut App<'b>> {
|
||||||
&mut self.subcommands
|
self.subcommands.iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the list of arguments
|
/// Iterate through the set of arguments
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_arguments(&self) -> &[Arg<'b>] {
|
pub fn get_arguments(&self) -> impl Iterator<Item = &Arg<'b>> {
|
||||||
&self.args.args
|
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
|
/// Get the list of arguments the given argument conflicts with
|
||||||
|
@ -210,6 +230,15 @@ impl<'b> App<'b> {
|
||||||
pub fn has_subcommands(&self) -> bool {
|
pub fn has_subcommands(&self) -> bool {
|
||||||
!self.subcommands.is_empty()
|
!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> {
|
impl<'b> App<'b> {
|
||||||
|
@ -2204,6 +2233,7 @@ impl<'b> App<'b> {
|
||||||
self.args.args.iter().find(|a| a.id == *arg_id)
|
self.args.args.iter().find(|a| a.id == *arg_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
// Should we color the output?
|
// Should we color the output?
|
||||||
pub(crate) fn color(&self) -> ColorChoice {
|
pub(crate) fn color(&self) -> ColorChoice {
|
||||||
debug!("App::color: Color setting...");
|
debug!("App::color: Color setting...");
|
||||||
|
@ -2220,6 +2250,7 @@ impl<'b> App<'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn contains_short(&self, s: char) -> bool {
|
pub(crate) fn contains_short(&self, s: char) -> bool {
|
||||||
if !self.is_set(AppSettings::Built) {
|
if !self.is_set(AppSettings::Built) {
|
||||||
panic!("If App::_build hasn't been called, manually search through Arg shorts");
|
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)
|
self.args.contains(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn set(&mut self, s: AppSettings) {
|
pub(crate) fn set(&mut self, s: AppSettings) {
|
||||||
self.settings.set(s)
|
self.settings.set(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn unset(&mut self, s: AppSettings) {
|
pub(crate) fn unset(&mut self, s: AppSettings) {
|
||||||
self.settings.unset(s)
|
self.settings.unset(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn has_args(&self) -> bool {
|
pub(crate) fn has_args(&self) -> bool {
|
||||||
!self.args.is_empty()
|
!self.args.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn has_opts(&self) -> bool {
|
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 {
|
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 {
|
pub(crate) fn has_visible_subcommands(&self) -> bool {
|
||||||
|
@ -2255,11 +2291,40 @@ impl<'b> App<'b> {
|
||||||
.any(|sc| !sc.is_set(AppSettings::Hidden))
|
.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)]
|
#[cfg(debug_assertions)]
|
||||||
pub(crate) fn id_exists(&self, id: &Id) -> bool {
|
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)
|
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> {
|
pub(crate) fn unroll_args_in_group(&self, group: &Id) -> Vec<Id> {
|
||||||
debug!("App::unroll_args_in_group: group={:?}", group);
|
debug!("App::unroll_args_in_group: group={:?}", group);
|
||||||
let mut g_vec = vec![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
|
/// **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
|
/// 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
|
/// `-F` is supposed to be a value, yet `-F` is *also* a valid short for another arg.
|
||||||
/// should be taken when designing these args. This is compounded by the ability to "stack"
|
/// 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
|
/// short args. I.e. if `-val` is supposed to be a value, but `-v`, `-a`, and `-l` are all valid
|
||||||
/// shorts.
|
/// shorts.
|
||||||
///
|
///
|
||||||
|
|
127
src/macros.rs
127
src/macros.rs
|
@ -272,7 +272,7 @@ macro_rules! app_from_crate {
|
||||||
///
|
///
|
||||||
/// # Alternative form for non-ident values
|
/// # 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"))`
|
/// * `(@arg something: --something)` could also be `(@arg ("something-else"): --("something-else"))`
|
||||||
/// * `(@subcommand something => ...)` could also be `(@subcommand ("something-else") => ...)`
|
/// * `(@subcommand something => ...)` could also be `(@subcommand ("something-else") => ...)`
|
||||||
///
|
///
|
||||||
|
@ -552,128 +552,3 @@ macro_rules! debug {
|
||||||
macro_rules! debug {
|
macro_rules! debug {
|
||||||
($($arg:tt)*) => {};
|
($($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 indexmap::IndexSet;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
#[cfg(not(feature = "wrap_help"))]
|
|
||||||
mod term_size {
|
|
||||||
pub(crate) fn dimensions() -> Option<(usize, usize)> {
|
pub(crate) fn dimensions() -> Option<(usize, usize)> {
|
||||||
None
|
#[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 {
|
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(0) => usize::MAX,
|
||||||
Some(w) => w,
|
Some(w) => w,
|
||||||
None => cmp::min(
|
None => cmp::min(
|
||||||
term_size::dimensions().map_or(100, |(w, _)| w),
|
dimensions().map_or(100, |(w, _)| w),
|
||||||
match parser.app.max_w {
|
match parser.app.max_w {
|
||||||
None | Some(0) => usize::MAX,
|
None | Some(0) => usize::MAX,
|
||||||
Some(mw) => mw,
|
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<()> {
|
pub(crate) fn write_all_args(&mut self) -> ClapResult<()> {
|
||||||
debug!("Help::write_all_args");
|
debug!("Help::write_all_args");
|
||||||
let flags = self.parser.has_flags();
|
let flags = self.parser.has_flags();
|
||||||
// Strange filter/count vs fold... https://github.com/rust-lang/rust/issues/33038
|
// FIXME: Strange filter/count vs fold... https://github.com/rust-lang/rust/issues/33038
|
||||||
let pos = positionals!(self.parser.app).fold(0, |acc, arg| {
|
let pos = self.parser.app.get_positionals().fold(0, |acc, arg| {
|
||||||
if should_show_arg(self.use_long, arg) {
|
if should_show_arg(self.use_long, arg) {
|
||||||
acc + 1
|
acc + 1
|
||||||
} else {
|
} else {
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
}) > 0;
|
}) > 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))
|
.filter(|arg| should_show_arg(self.use_long, arg))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let subcmds = self.parser.has_visible_subcommands();
|
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 {
|
let mut first = if pos {
|
||||||
self.warning("ARGS:\n")?;
|
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
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -716,8 +720,8 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
||||||
self.none("\n\n")?;
|
self.none("\n\n")?;
|
||||||
}
|
}
|
||||||
self.warning("FLAGS:\n")?;
|
self.warning("FLAGS:\n")?;
|
||||||
let flags_v: Vec<_> = flags!(self.parser.app).collect();
|
let flags_v: Vec<_> = self.parser.app.get_flags_no_heading().collect();
|
||||||
self.write_args(&*flags_v)?;
|
self.write_args(&flags_v)?;
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
if !opts.is_empty() {
|
if !opts.is_empty() {
|
||||||
|
@ -1066,16 +1070,16 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|a| a.has_switch())
|
.filter(|a| a.has_switch())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
self.write_args(&*opts_flags)?;
|
self.write_args(&opts_flags)?;
|
||||||
}
|
}
|
||||||
b"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" => {
|
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" => {
|
b"positionals" => {
|
||||||
self.write_args(&*positionals!(self.parser.app).collect::<Vec<_>>())?;
|
self.write_args(&self.parser.app.get_positionals().collect::<Vec<_>>())?;
|
||||||
}
|
}
|
||||||
b"subcommands" => {
|
b"subcommands" => {
|
||||||
self.write_subcommands(self.parser.app)?;
|
self.write_subcommands(self.parser.app)?;
|
||||||
|
|
|
@ -71,7 +71,10 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
||||||
usage.push_str(" [OPTIONS]");
|
usage.push_str(" [OPTIONS]");
|
||||||
}
|
}
|
||||||
if !self.p.is_set(AS::UnifiedHelpMessage)
|
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))
|
.any(|o| !o.is_set(ArgSettings::Required) && !o.is_set(ArgSettings::Hidden))
|
||||||
{
|
{
|
||||||
usage.push_str(" [OPTIONS]");
|
usage.push_str(" [OPTIONS]");
|
||||||
|
@ -79,11 +82,23 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
||||||
|
|
||||||
usage.push_str(&req_string[..]);
|
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
|
// places a '--' in the usage string if there are args and options
|
||||||
// supporting multiple values
|
// supporting multiple values
|
||||||
if opts!(self.p.app).any(|o| o.is_set(ArgSettings::MultipleValues))
|
if self
|
||||||
&& positionals!(self.p.app).any(|p| !p.is_set(ArgSettings::Required))
|
.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.app.has_visible_subcommands()
|
||||||
|| self.p.is_set(AS::AllowExternalSubcommands))
|
|| self.p.is_set(AS::AllowExternalSubcommands))
|
||||||
&& !has_last
|
&& !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::Required) || p.is_set(ArgSettings::Last))
|
||||||
&& !p.is_set(ArgSettings::Hidden)
|
&& !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) {
|
if let Some(args_tag) = self.get_args_tag(incl_reqs) {
|
||||||
usage.push_str(&*args_tag);
|
usage.push_str(&*args_tag);
|
||||||
} else {
|
} else {
|
||||||
usage.push_str(" [ARGS]");
|
usage.push_str(" [ARGS]");
|
||||||
}
|
}
|
||||||
if has_last && incl_reqs {
|
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))
|
.find(|p| p.is_set(ArgSettings::Last))
|
||||||
.expect(INTERNAL_ERROR_MSG);
|
.expect(INTERNAL_ERROR_MSG);
|
||||||
debug!("Usage::create_help_usage: '{}' has .last(true)", pos.name);
|
debug!("Usage::create_help_usage: '{}' has .last(true)", pos.name);
|
||||||
let req = pos.is_set(ArgSettings::Required);
|
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(" -- <");
|
usage.push_str(" -- <");
|
||||||
} else if req {
|
} else if req {
|
||||||
usage.push_str(" [--] <");
|
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> {
|
fn get_args_tag(&self, incl_reqs: bool) -> Option<String> {
|
||||||
debug!("Usage::get_args_tag; incl_reqs = {:?}", incl_reqs);
|
debug!("Usage::get_args_tag; incl_reqs = {:?}", incl_reqs);
|
||||||
let mut count = 0;
|
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::Required))
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
||||||
{
|
{
|
||||||
debug!("Usage::get_args_tag:iter:{}", pos.name);
|
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);
|
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 it's part of a required group we don't want to count it
|
||||||
if self
|
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]");
|
debug!("Usage::get_args_tag:iter: More than one, returning [ARGS]");
|
||||||
return None; // [ARGS]
|
return None; // [ARGS]
|
||||||
} else if count == 1 && incl_reqs {
|
} else if count == 1 && incl_reqs {
|
||||||
let pos = positionals!(self.p.app)
|
let pos = self
|
||||||
|
.p
|
||||||
|
.app
|
||||||
|
.get_positionals()
|
||||||
.find(|pos| {
|
.find(|pos| {
|
||||||
!pos.is_set(ArgSettings::Required)
|
!pos.is_set(ArgSettings::Required)
|
||||||
&& !pos.is_set(ArgSettings::Hidden)
|
&& !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");
|
debug!("Usage::get_args_tag:iter: Don't collapse returning all");
|
||||||
return Some(
|
return Some(
|
||||||
positionals!(self.p.app)
|
self.p
|
||||||
|
.app
|
||||||
|
.get_positionals()
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Required))
|
.filter(|pos| !pos.is_set(ArgSettings::Required))
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
||||||
|
@ -242,7 +274,10 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
||||||
);
|
);
|
||||||
} else if !incl_reqs {
|
} else if !incl_reqs {
|
||||||
debug!("Usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
|
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| {
|
.filter_map(|pos| {
|
||||||
if pos.is_set(ArgSettings::Required) && !pos.is_set(ArgSettings::Last) {
|
if pos.is_set(ArgSettings::Required) && !pos.is_set(ArgSettings::Last) {
|
||||||
Some(pos.index)
|
Some(pos.index)
|
||||||
|
@ -251,16 +286,12 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.max()
|
.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(
|
return Some(
|
||||||
positionals!(self.p.app)
|
self.p
|
||||||
.filter_map(|pos| {
|
.app
|
||||||
if pos.index <= highest_req_pos {
|
.get_positionals()
|
||||||
Some(pos)
|
.filter(|pos| pos.index <= highest_req_pos)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Required))
|
.filter(|pos| !pos.is_set(ArgSettings::Required))
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
|
||||||
.filter(|pos| !pos.is_set(ArgSettings::Last))
|
.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
|
// Determines if we need the `[FLAGS]` tag in the usage string
|
||||||
fn needs_flags_tag(&self) -> bool {
|
fn needs_flags_tag(&self) -> bool {
|
||||||
debug!("Usage::needs_flags_tag");
|
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);
|
debug!("Usage::needs_flags_tag:iter: f={}", f.name);
|
||||||
if let Some(l) = f.long {
|
if let Some(l) = f.long {
|
||||||
if l == "help" || l == "version" {
|
if l == "help" || l == "version" {
|
||||||
|
@ -283,7 +314,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
||||||
continue;
|
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);
|
debug!("Usage::needs_flags_tag:iter:iter: grp_s={:?}", grp_s);
|
||||||
if self
|
if self
|
||||||
.p
|
.p
|
||||||
|
@ -356,7 +387,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
||||||
unrolled_reqs
|
unrolled_reqs
|
||||||
.iter()
|
.iter()
|
||||||
.chain(incls.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(|&pos| !m.contains(pos))
|
||||||
.filter_map(|pos| self.p.app.find(pos))
|
.filter_map(|pos| self.p.app.find(pos))
|
||||||
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
|
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
|
||||||
|
@ -367,7 +398,7 @@ impl<'b, 'c, 'z> Usage<'b, 'c, 'z> {
|
||||||
unrolled_reqs
|
unrolled_reqs
|
||||||
.iter()
|
.iter()
|
||||||
.chain(incls.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_map(|pos| self.p.app.find(pos))
|
||||||
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
|
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
|
||||||
.filter(|pos| !args_in_groups.contains(&pos.id))
|
.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
|
for a in unrolled_reqs
|
||||||
.iter()
|
.iter()
|
||||||
.chain(incls.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| !self.p.app.groups.iter().any(|g| &&g.id == name))
|
||||||
.filter(|name| !args_in_groups.contains(name))
|
.filter(|name| !args_in_groups.contains(name))
|
||||||
.filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name)))
|
.filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name)))
|
||||||
|
|
|
@ -42,27 +42,31 @@ where
|
||||||
T: AsRef<str>,
|
T: AsRef<str>,
|
||||||
I: IntoIterator<Item = T>,
|
I: IntoIterator<Item = T>,
|
||||||
{
|
{
|
||||||
|
use crate::mkeymap::KeyType;
|
||||||
|
|
||||||
match did_you_mean(arg, longs).pop() {
|
match did_you_mean(arg, longs).pop() {
|
||||||
Some(ref candidate) => {
|
Some(candidate) => Some((candidate, None)),
|
||||||
return Some((candidate.to_owned(), None));
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
for subcommand in subcommands {
|
for subcommand in subcommands {
|
||||||
subcommand._build();
|
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
|
None
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(candidate) = did_you_mean(arg, longs).pop() {
|
||||||
|
return Some((candidate, Some(subcommand.get_name().to_string())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(test, features = "suggestions"))]
|
#[cfg(all(test, features = "suggestions"))]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
|
@ -171,7 +171,7 @@ where
|
||||||
let only_highest = |a: &Arg| {
|
let only_highest = |a: &Arg| {
|
||||||
a.is_set(ArgSettings::MultipleValues) && (a.index.unwrap_or(0) != highest_idx)
|
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
|
// First we make sure if there is a positional that allows multiple values
|
||||||
// the one before it (second to last) has one of these:
|
// the one before it (second to last) has one of these:
|
||||||
// * a value terminator
|
// * a value terminator
|
||||||
|
@ -206,7 +206,9 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
// Next we check how many have both Multiple and not a specific number of values set
|
// 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())
|
.filter(|p| p.settings.is_set(ArgSettings::MultipleValues) && p.num_vals.is_none())
|
||||||
.count();
|
.count();
|
||||||
let ok = count <= 1
|
let ok = count <= 1
|
||||||
|
@ -227,7 +229,7 @@ where
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
let mut foundx2 = 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) {
|
if foundx2 && !p.is_set(ArgSettings::Required) {
|
||||||
assert!(
|
assert!(
|
||||||
p.is_set(ArgSettings::Required),
|
p.is_set(ArgSettings::Required),
|
||||||
|
@ -283,13 +285,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
positionals!(self.app)
|
self.app
|
||||||
|
.get_positionals()
|
||||||
.filter(|p| p.is_set(ArgSettings::Last))
|
.filter(|p| p.is_set(ArgSettings::Last))
|
||||||
.count()
|
.count()
|
||||||
< 2,
|
< 2,
|
||||||
"Only one positional argument may have last(true) set. Found two."
|
"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))
|
.any(|p| p.is_set(ArgSettings::Last) && p.is_set(ArgSettings::Required))
|
||||||
&& self.has_subcommands()
|
&& self.has_subcommands()
|
||||||
&& !self.is_set(AS::SubcommandsNegateReqs)
|
&& !self.is_set(AS::SubcommandsNegateReqs)
|
||||||
|
@ -336,7 +341,7 @@ where
|
||||||
self._verify_positionals();
|
self._verify_positionals();
|
||||||
|
|
||||||
// Set the LowIndexMultiple flag if required
|
// 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.is_set(ArgSettings::MultipleValues)
|
||||||
&& (a.index.unwrap_or(0) as usize
|
&& (a.index.unwrap_or(0) as usize
|
||||||
!= self
|
!= self
|
||||||
|
@ -346,7 +351,7 @@ where
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.key.is_position())
|
.filter(|x| x.key.is_position())
|
||||||
.count())
|
.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[&p_name.id].is_set(ArgSettings::Last)
|
||||||
}) {
|
}) {
|
||||||
self.app.settings.set(AS::LowIndexMultiplePositional);
|
self.app.settings.set(AS::LowIndexMultiplePositional);
|
||||||
|
@ -533,8 +538,10 @@ where
|
||||||
|| self.is_set(AS::AllowExternalSubcommands)
|
|| self.is_set(AS::AllowExternalSubcommands)
|
||||||
|| self.is_set(AS::InferSubcommands))
|
|| self.is_set(AS::InferSubcommands))
|
||||||
{
|
{
|
||||||
let cands =
|
let cands = suggestions::did_you_mean(
|
||||||
suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self.app));
|
&*arg_os.to_string_lossy(),
|
||||||
|
self.app.all_subcommand_names(),
|
||||||
|
);
|
||||||
if !cands.is_empty() {
|
if !cands.is_empty() {
|
||||||
let cands: Vec<_> =
|
let cands: Vec<_> =
|
||||||
cands.iter().map(|cand| format!("'{}'", cand)).collect();
|
cands.iter().map(|cand| format!("'{}'", cand)).collect();
|
||||||
|
@ -578,8 +585,10 @@ where
|
||||||
ParseResult::ValuesDone(id) => ParseResult::ValuesDone(id),
|
ParseResult::ValuesDone(id) => ParseResult::ValuesDone(id),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(p) =
|
if let Some(p) = self
|
||||||
positionals!(self.app).find(|p| p.index == Some(pos_counter as u64))
|
.app
|
||||||
|
.get_positionals()
|
||||||
|
.find(|p| p.index == Some(pos_counter as u64))
|
||||||
{
|
{
|
||||||
ParseResult::Pos(p.id.clone())
|
ParseResult::Pos(p.id.clone())
|
||||||
} else {
|
} else {
|
||||||
|
@ -593,7 +602,10 @@ where
|
||||||
|
|
||||||
if self.is_new_arg(&n, &needs_val_of)
|
if self.is_new_arg(&n, &needs_val_of)
|
||||||
|| sc_match
|
|| sc_match
|
||||||
|| !suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self.app))
|
|| !suggestions::did_you_mean(
|
||||||
|
&n.to_string_lossy(),
|
||||||
|
self.app.all_subcommand_names(),
|
||||||
|
)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
{
|
{
|
||||||
debug!("Parser::get_matches_with: Bumping the positional counter...");
|
debug!("Parser::get_matches_with: Bumping the positional counter...");
|
||||||
|
@ -653,7 +665,7 @@ where
|
||||||
self.add_val_to_arg(p, &arg_os, matcher, ValueType::CommandLine)?;
|
self.add_val_to_arg(p, &arg_os, matcher, ValueType::CommandLine)?;
|
||||||
|
|
||||||
matcher.inc_occurrence_of(&p.id);
|
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);
|
matcher.inc_occurrence_of(&grp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,8 +724,10 @@ where
|
||||||
self.app.color(),
|
self.app.color(),
|
||||||
)?);
|
)?);
|
||||||
} else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
|
} else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
|
||||||
let cands =
|
let cands = suggestions::did_you_mean(
|
||||||
suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self.app));
|
&*arg_os.to_string_lossy(),
|
||||||
|
self.app.all_subcommand_names(),
|
||||||
|
);
|
||||||
if !cands.is_empty() {
|
if !cands.is_empty() {
|
||||||
let cands: Vec<_> = cands.iter().map(|cand| format!("'{}'", cand)).collect();
|
let cands: Vec<_> = cands.iter().map(|cand| format!("'{}'", cand)).collect();
|
||||||
return Err(ClapError::invalid_subcommand(
|
return Err(ClapError::invalid_subcommand(
|
||||||
|
@ -742,7 +756,9 @@ where
|
||||||
|
|
||||||
if !external_subcommand {
|
if !external_subcommand {
|
||||||
if let Some(ref pos_sc_name) = subcmd_name {
|
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)
|
.expect(INTERNAL_ERROR_MSG)
|
||||||
.name
|
.name
|
||||||
.clone();
|
.clone();
|
||||||
|
@ -816,7 +832,7 @@ where
|
||||||
}
|
}
|
||||||
debug!("No");
|
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);
|
debug!("Parser::maybe_inc_pos_counter: group={:?}", group);
|
||||||
let group = self.app.groups.iter().find(|g| g.id == group);
|
let group = self.app.groups.iter().find(|g| g.id == group);
|
||||||
|
|
||||||
|
@ -842,7 +858,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_set(AS::InferSubcommands) {
|
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))
|
.filter(|s| arg_os.is_prefix_of(s))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
@ -855,7 +873,7 @@ where
|
||||||
return Some(sc);
|
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);
|
return Some(&sc.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -914,18 +932,18 @@ where
|
||||||
help_help = true;
|
help_help = true;
|
||||||
break; // Maybe?
|
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));
|
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();
|
c._build();
|
||||||
sc = c;
|
sc = c;
|
||||||
|
|
||||||
if i == cmds.len() - 1 {
|
if i == cmds.len() - 1 {
|
||||||
break;
|
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();
|
c._build();
|
||||||
sc = c;
|
sc = c;
|
||||||
|
|
||||||
|
@ -1045,7 +1063,7 @@ where
|
||||||
|
|
||||||
mid_string.push_str(" ");
|
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();
|
let id = x.id.clone();
|
||||||
self.app._propagate(Propagation::To(id));
|
self.app._propagate(Propagation::To(id));
|
||||||
}
|
}
|
||||||
|
@ -1387,7 +1405,7 @@ where
|
||||||
|
|
||||||
matcher.inc_occurrence_of(&opt.id);
|
matcher.inc_occurrence_of(&opt.id);
|
||||||
// Increment or create the group "args"
|
// 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);
|
matcher.inc_occurrence_of(&grp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1464,7 +1482,7 @@ where
|
||||||
matcher.add_index_to(&arg.id, self.cur_idx.get(), ty);
|
matcher.add_index_to(&arg.id, self.cur_idx.get(), ty);
|
||||||
|
|
||||||
// Increment or create the group "args"
|
// 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);
|
matcher.add_val_to(&grp, v.to_os_string(), ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1480,7 +1498,7 @@ where
|
||||||
matcher.inc_occurrence_of(&flag.id);
|
matcher.inc_occurrence_of(&flag.id);
|
||||||
matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueType::CommandLine);
|
matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueType::CommandLine);
|
||||||
// Increment or create the group "args"
|
// 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);
|
matcher.inc_occurrence_of(&grp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1560,12 +1578,12 @@ where
|
||||||
pub(crate) fn add_defaults(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
|
pub(crate) fn add_defaults(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
|
||||||
debug!("Parser::add_defaults");
|
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);
|
debug!("Parser::add_defaults:iter:{}:", o.name);
|
||||||
self.add_value(o, matcher, ValueType::DefaultValue)?;
|
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);
|
debug!("Parser::add_defaults:iter:{}:", p.name);
|
||||||
self.add_value(p, matcher, ValueType::DefaultValue)?;
|
self.add_value(p, matcher, ValueType::DefaultValue)?;
|
||||||
}
|
}
|
||||||
|
@ -1693,7 +1711,7 @@ where
|
||||||
.args
|
.args
|
||||||
.get(&KeyType::Long(OsString::from(name.0.clone())))
|
.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.inc_occurrence_of(&g);
|
||||||
}
|
}
|
||||||
matcher.insert(&opt.id);
|
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
|
// args in that group to the conflicts, as well as any args those args conflict
|
||||||
// with
|
// 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
|
if let Some(g) = self
|
||||||
.p
|
.p
|
||||||
.app
|
.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)
|
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> {
|
pub fn complex_app() -> App<'static> {
|
||||||
let opt3_vals = ["fast", "slow"];
|
let opt3_vals = ["fast", "slow"];
|
||||||
|
|
Loading…
Reference in a new issue