refactor: Reduce visibility on App members

The long term goals are
- Easier refactoring
- Identify needs for reflection API

Shorter term, if I want to rename `App` to `Command` and deprecate
`App`, it will mark all member access as deprecated.  This works around
that.

I gave up in exploring abstractions when it came to `MKeyMap` access.
This can be refined in the future.
This commit is contained in:
Ed Page 2022-02-14 11:23:04 -06:00
parent e759001d93
commit 77542a1138
11 changed files with 283 additions and 189 deletions

View file

@ -2,7 +2,7 @@ use clap::AppSettings;
use roff::{bold, italic, roman, Inline, Roff};
pub(crate) fn subcommand_heading(app: &clap::App) -> String {
match app.get_subommand_help_heading() {
match app.get_subcommand_help_heading() {
Some(title) => title.to_string(),
None => "SUBCOMMANDS".to_string(),
}

View file

@ -3,6 +3,7 @@
// Std
use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fmt;
use std::io;
@ -62,40 +63,40 @@ use crate::build::debug_asserts::assert_app;
/// [`App::get_matches`]: App::get_matches()
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct App<'help> {
pub(crate) id: Id,
pub(crate) name: String,
pub(crate) long_flag: Option<&'help str>,
pub(crate) short_flag: Option<char>,
pub(crate) bin_name: Option<String>,
pub(crate) author: Option<&'help str>,
pub(crate) version: Option<&'help str>,
pub(crate) long_version: Option<&'help str>,
pub(crate) about: Option<&'help str>,
pub(crate) long_about: Option<&'help str>,
pub(crate) before_help: Option<&'help str>,
pub(crate) before_long_help: Option<&'help str>,
pub(crate) after_help: Option<&'help str>,
pub(crate) after_long_help: Option<&'help str>,
pub(crate) aliases: Vec<(&'help str, bool)>, // (name, visible)
pub(crate) short_flag_aliases: Vec<(char, bool)>, // (name, visible)
pub(crate) long_flag_aliases: Vec<(&'help str, bool)>, // (name, visible)
pub(crate) usage_str: Option<&'help str>,
pub(crate) usage_name: Option<String>,
pub(crate) help_str: Option<&'help str>,
pub(crate) disp_ord: Option<usize>,
pub(crate) term_w: Option<usize>,
pub(crate) max_w: Option<usize>,
pub(crate) template: Option<&'help str>,
pub(crate) settings: AppFlags,
pub(crate) g_settings: AppFlags,
pub(crate) args: MKeyMap<'help>,
pub(crate) subcommands: Vec<App<'help>>,
pub(crate) replacers: HashMap<&'help str, &'help [&'help str]>,
pub(crate) groups: Vec<ArgGroup<'help>>,
pub(crate) current_help_heading: Option<&'help str>,
pub(crate) current_disp_ord: Option<usize>,
pub(crate) subcommand_value_name: Option<&'help str>,
pub(crate) subcommand_heading: Option<&'help str>,
id: Id,
name: String,
long_flag: Option<&'help str>,
short_flag: Option<char>,
bin_name: Option<String>,
author: Option<&'help str>,
version: Option<&'help str>,
long_version: Option<&'help str>,
about: Option<&'help str>,
long_about: Option<&'help str>,
before_help: Option<&'help str>,
before_long_help: Option<&'help str>,
after_help: Option<&'help str>,
after_long_help: Option<&'help str>,
aliases: Vec<(&'help str, bool)>, // (name, visible)
short_flag_aliases: Vec<(char, bool)>, // (name, visible)
long_flag_aliases: Vec<(&'help str, bool)>, // (name, visible)
usage_str: Option<&'help str>,
usage_name: Option<String>,
help_str: Option<&'help str>,
disp_ord: Option<usize>,
term_w: Option<usize>,
max_w: Option<usize>,
template: Option<&'help str>,
settings: AppFlags,
g_settings: AppFlags,
args: MKeyMap<'help>,
subcommands: Vec<App<'help>>,
replacers: HashMap<&'help OsStr, &'help [&'help str]>,
groups: Vec<ArgGroup<'help>>,
current_help_heading: Option<&'help str>,
current_disp_ord: Option<usize>,
subcommand_value_name: Option<&'help str>,
subcommand_heading: Option<&'help str>,
}
/// Basic API
@ -1933,7 +1934,7 @@ impl<'help> App<'help> {
#[cfg(feature = "unstable-replace")]
#[must_use]
pub fn replace(mut self, name: &'help str, target: &'help [&'help str]) -> Self {
self.replacers.insert(name, target);
self.replacers.insert(OsStr::new(name), target);
self
}
@ -3151,6 +3152,11 @@ impl<'help> App<'help> {
/// Reflection
impl<'help> App<'help> {
#[inline]
pub(crate) fn get_usage_name(&self) -> Option<&str> {
self.usage_name.as_deref()
}
/// Get the name of the binary.
#[inline]
pub fn get_bin_name(&self) -> Option<&str> {
@ -3324,16 +3330,38 @@ impl<'help> App<'help> {
/// Returns the help heading for listing subcommands.
#[inline]
pub fn get_subommand_help_heading(&self) -> Option<&str> {
pub fn get_subcommand_help_heading(&self) -> Option<&str> {
self.subcommand_heading
}
/// Deprecated, replaced with [`App::get_subcommand_help_heading`]
#[inline]
#[deprecated(
since = "3.1.0",
note = "Replaced with `App::get_subcommand_help_heading`"
)]
pub fn get_subommand_help_heading(&self) -> Option<&str> {
self.get_subcommand_help_heading()
}
/// Returns the subcommand value name.
#[inline]
pub fn get_subcommand_value_name(&self) -> Option<&str> {
self.subcommand_value_name
}
/// Returns the help heading for listing subcommands.
#[inline]
pub fn get_before_help(&self) -> Option<&str> {
self.before_help
}
/// Returns the help heading for listing subcommands.
#[inline]
pub fn get_before_long_help(&self) -> Option<&str> {
self.before_long_help
}
/// Returns the help heading for listing subcommands.
#[inline]
pub fn get_after_help(&self) -> Option<&str> {
@ -3369,6 +3397,12 @@ impl<'help> App<'help> {
self.get_subcommands_mut().find(|s| s.aliases_to(name))
}
/// Iterate through the set of groups.
#[inline]
pub fn get_groups(&self) -> impl Iterator<Item = &ArgGroup<'help>> {
self.groups.iter()
}
/// Iterate through the set of arguments.
#[inline]
pub fn get_arguments(&self) -> impl Iterator<Item = &Arg<'help>> {
@ -3867,6 +3901,38 @@ impl<'help> App<'help> {
// Internally used only
impl<'help> App<'help> {
pub(crate) fn get_id(&self) -> Id {
self.id.clone()
}
pub(crate) fn get_override_usage(&self) -> Option<&str> {
self.usage_str
}
pub(crate) fn get_override_help(&self) -> Option<&str> {
self.help_str
}
pub(crate) fn get_help_template(&self) -> Option<&str> {
self.template
}
pub(crate) fn get_term_width(&self) -> Option<usize> {
self.term_w
}
pub(crate) fn get_max_term_width(&self) -> Option<usize> {
self.max_w
}
pub(crate) fn get_replacement(&self, key: &OsStr) -> Option<&[&str]> {
self.replacers.get(key).copied()
}
pub(crate) fn get_keymap(&self) -> &MKeyMap<'help> {
&self.args
}
fn get_used_global_args(&self, matches: &ArgMatches, global_arg_vec: &mut Vec<Id>) {
global_arg_vec.extend(
self.args

View file

@ -7,7 +7,10 @@ fn propagate_version() {
.version("1.1")
.subcommand(App::new("sub1"));
app._propagate();
assert_eq!(app.subcommands[0].version, Some("1.1"));
assert_eq!(
app.get_subcommands().next().unwrap().get_version(),
Some("1.1")
);
}
#[test]
@ -17,9 +20,8 @@ fn global_setting() {
.subcommand(App::new("subcmd"));
app._propagate();
assert!(app
.subcommands
.iter()
.find(|s| s.name == "subcmd")
.get_subcommands()
.find(|s| s.get_name() == "subcmd")
.unwrap()
.is_disable_version_flag_set());
}
@ -38,5 +40,9 @@ fn issue_2090() {
.subcommand(App::new("sub"));
app._build();
assert!(app.subcommands[0].is_disable_version_flag_set());
assert!(app
.get_subcommands()
.next()
.unwrap()
.is_disable_version_flag_set());
}

View file

@ -12,7 +12,7 @@ pub(crate) fn assert_app(app: &App) {
let mut long_flags = vec![];
// Invalid version flag settings
if app.version.is_none() && app.long_version.is_none() {
if app.get_version().is_none() && app.get_long_version().is_none() {
// PropagateVersion is meaningless if there is no version
assert!(
!app.is_propagate_version_set(),
@ -22,37 +22,36 @@ pub(crate) fn assert_app(app: &App) {
// Used `App::mut_arg("version", ..) but did not provide any version information to display
let has_mutated_version = app
.args
.args()
.get_arguments()
.any(|x| x.id == Id::version_hash() && x.provider == ArgProvider::GeneratedMutated);
if has_mutated_version {
assert!(app.settings.is_set(AppSettings::NoAutoVersion),
assert!(app.is_set(AppSettings::NoAutoVersion),
"App {}: Used App::mut_arg(\"version\", ..) without providing App::version, App::long_version or using AppSettings::NoAutoVersion"
,app.get_name()
);
}
}
for sc in &app.subcommands {
if let Some(s) = sc.short_flag.as_ref() {
short_flags.push(Flag::App(format!("-{}", s), &sc.name));
for sc in app.get_subcommands() {
if let Some(s) = sc.get_short_flag().as_ref() {
short_flags.push(Flag::App(format!("-{}", s), sc.get_name()));
}
for (short_alias, _) in &sc.short_flag_aliases {
short_flags.push(Flag::App(format!("-{}", short_alias), &sc.name));
for short_alias in sc.get_all_short_flag_aliases() {
short_flags.push(Flag::App(format!("-{}", short_alias), sc.get_name()));
}
if let Some(l) = sc.long_flag.as_ref() {
long_flags.push(Flag::App(format!("--{}", l), &sc.name));
if let Some(l) = sc.get_long_flag().as_ref() {
long_flags.push(Flag::App(format!("--{}", l), sc.get_name()));
}
for (long_alias, _) in &sc.long_flag_aliases {
long_flags.push(Flag::App(format!("--{}", long_alias), &sc.name));
for long_alias in sc.get_all_long_flag_aliases() {
long_flags.push(Flag::App(format!("--{}", long_alias), sc.get_name()));
}
}
for arg in app.args.args() {
for arg in app.get_arguments() {
assert_arg(arg);
if let Some(s) = arg.short.as_ref() {
@ -223,10 +222,10 @@ pub(crate) fn assert_app(app: &App) {
}
}
for group in &app.groups {
for group in app.get_groups() {
// Name conflicts
assert!(
app.groups.iter().filter(|x| x.id == group.id).count() < 2,
app.get_groups().filter(|x| x.id == group.id).count() < 2,
"App {}: Argument group name must be unique\n\n\t'{}' is already in use",
app.get_name(),
group.name,
@ -234,7 +233,7 @@ pub(crate) fn assert_app(app: &App) {
// Groups should not have naming conflicts with Args
assert!(
!app.args.args().any(|x| x.id == group.id),
!app.get_arguments().any(|x| x.id == group.id),
"App {}: Argument group name '{}' must not conflict with argument name",
app.get_name(),
group.name,
@ -244,8 +243,7 @@ pub(crate) fn assert_app(app: &App) {
if group.required && !group.args.is_empty() {
assert!(
group.args.iter().any(|arg| {
app.args
.args()
app.get_arguments()
.any(|x| x.id == *arg && x.default_vals.is_empty())
}),
"App {}: Argument group '{}' is required but all of it's arguments have a default value.",
@ -257,7 +255,7 @@ pub(crate) fn assert_app(app: &App) {
for arg in &group.args {
// Args listed inside groups should exist
assert!(
app.args.args().any(|x| x.id == *arg),
app.get_arguments().any(|x| x.id == *arg),
"App {}: Argument group '{}' contains non-existent argument '{:?}'",
app.get_name(),
group.name,
@ -276,7 +274,7 @@ pub(crate) fn assert_app(app: &App) {
_verify_positionals(app);
if let Some(help_template) = app.template {
if let Some(help_template) = app.get_help_template() {
assert!(
!help_template.contains("{flags}"),
"App {}: {}",
@ -422,7 +420,7 @@ fn _verify_positionals(app: &App) -> bool {
// but no 2)
let highest_idx = app
.args
.get_keymap()
.keys()
.filter_map(|x| {
if let KeyType::Position(n) = x {
@ -434,7 +432,7 @@ fn _verify_positionals(app: &App) -> bool {
.max()
.unwrap_or(0);
let num_p = app.args.keys().filter(|x| x.is_position()).count();
let num_p = app.get_keymap().keys().filter(|x| x.is_position()).count();
assert!(
highest_idx == num_p,
@ -455,8 +453,8 @@ fn _verify_positionals(app: &App) -> bool {
// We can't pass the closure (it.next()) to the macro directly because each call to
// find() (iterator, not macro) gets called repeatedly.
let last = &app.args[&KeyType::Position(highest_idx)];
let second_to_last = &app.args[&KeyType::Position(highest_idx - 1)];
let last = &app.get_keymap()[&KeyType::Position(highest_idx)];
let second_to_last = &app.get_keymap()[&KeyType::Position(highest_idx - 1)];
// Either the final positional is required
// Or the second to last has a terminator or .last(true) set
@ -535,7 +533,7 @@ fn _verify_positionals(app: &App) -> bool {
} else {
// Check that if a required positional argument is found, all positions with a lower
// index are also required
for p in (1..=num_p).rev().filter_map(|n| app.args.get(&n)) {
for p in (1..=num_p).rev().filter_map(|n| app.get_keymap().get(&n)) {
if found {
assert!(
p.is_required_set(),

View file

@ -182,7 +182,7 @@ impl Error {
}
pub(crate) fn with_app(self, app: &App) -> Self {
self.set_wait_on_exit(app.settings.is_set(AppSettings::WaitOnError))
self.set_wait_on_exit(app.is_set(AppSettings::WaitOnError))
.set_color(app.get_color())
.set_help_flag(get_help_flag(app))
}
@ -1047,9 +1047,9 @@ fn put_usage(c: &mut Colorizer, usage: impl Into<String>) {
}
fn get_help_flag(app: &App) -> Option<&'static str> {
if !app.settings.is_set(AppSettings::DisableHelpFlag) {
if !app.is_disable_help_flag_set() {
Some("--help")
} else if app.has_subcommands() && !app.settings.is_set(AppSettings::DisableHelpSubcommand) {
} else if app.has_subcommands() && !app.is_disable_help_subcommand_set() {
Some("help")
} else {
None

View file

@ -53,12 +53,12 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
use_long: bool,
) -> Self {
debug!("Help::new");
let term_w = match app.term_w {
let term_w = match app.get_term_width() {
Some(0) => usize::MAX,
Some(w) => w,
None => cmp::min(
dimensions().map_or(100, |(w, _)| w),
match app.max_w {
match app.get_max_term_width() {
None | Some(0) => usize::MAX,
Some(mw) => mw,
},
@ -80,9 +80,9 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
pub(crate) fn write_help(&mut self) -> io::Result<()> {
debug!("Help::write_help");
if let Some(h) = self.app.help_str {
if let Some(h) = self.app.get_override_help() {
self.none(h)?;
} else if let Some(tmpl) = self.app.template {
} else if let Some(tmpl) = self.app.get_help_template() {
self.write_templated_help(tmpl)?;
} else {
let pos = self
@ -353,9 +353,11 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
fn write_before_help(&mut self) -> io::Result<()> {
debug!("Help::write_before_help");
let before_help = if self.use_long {
self.app.before_long_help.or(self.app.before_help)
self.app
.get_before_long_help()
.or_else(|| self.app.get_before_help())
} else {
self.app.before_help
self.app.get_before_help()
};
if let Some(output) = before_help {
self.none(text_wrapper(&output.replace("{n}", "\n"), self.term_w))?;
@ -367,9 +369,11 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
fn write_after_help(&mut self) -> io::Result<()> {
debug!("Help::write_after_help");
let after_help = if self.use_long {
self.app.after_long_help.or(self.app.after_help)
self.app
.get_after_long_help()
.or_else(|| self.app.get_after_help())
} else {
self.app.after_help
self.app.get_after_help()
};
if let Some(output) = after_help {
self.none("\n\n")?;
@ -606,9 +610,9 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
fn write_about(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> {
let about = if self.use_long {
self.app.long_about.or(self.app.about)
self.app.get_long_about().or_else(|| self.app.get_about())
} else {
self.app.about
self.app.get_about()
};
if let Some(output) = about {
if before_new_line {
@ -623,7 +627,7 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
}
fn write_author(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> {
if let Some(author) = self.app.author {
if let Some(author) = self.app.get_author() {
if before_new_line {
self.none("\n")?;
}
@ -636,7 +640,10 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
}
fn write_version(&mut self) -> io::Result<()> {
let version = self.app.version.or(self.app.long_version);
let version = self
.app
.get_version()
.or_else(|| self.app.get_long_version());
if let Some(output) = version {
self.none(text_wrapper(output, self.term_w))?;
}
@ -657,20 +664,26 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
let spec_vals = &self.sc_spec_vals(app);
let about = app.about.or(app.long_about).unwrap_or("");
let about = app
.get_about()
.or_else(|| app.get_long_about())
.unwrap_or("");
self.subcmd(sc_str, next_line_help, longest)?;
self.help(false, about, spec_vals, next_line_help, longest)
}
fn sc_spec_vals(&self, a: &App) -> String {
debug!("Help::sc_spec_vals: a={}", a.name);
debug!("Help::sc_spec_vals: a={}", a.get_name());
let mut spec_vals = vec![];
if !a.aliases.is_empty() || !a.short_flag_aliases.is_empty() {
debug!("Help::spec_vals: Found aliases...{:?}", a.aliases);
if 0 < a.get_all_aliases().count() || 0 < a.get_all_short_flag_aliases().count() {
debug!(
"Help::spec_vals: Found aliases...{:?}",
a.get_all_aliases().collect::<Vec<_>>()
);
debug!(
"Help::spec_vals: Found short flag aliases...{:?}",
a.short_flag_aliases
a.get_all_short_flag_aliases().collect::<Vec<_>>()
);
let mut short_als = a
@ -697,7 +710,7 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
true
} else {
// force_next_line
let h = app.about.unwrap_or("");
let h = app.get_about().unwrap_or("");
let h_w = display_width(h) + display_width(spec_vals);
let taken = longest + 12;
self.term_w >= taken
@ -738,8 +751,7 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
let custom_headings = self
.app
.args
.args()
.get_arguments()
.filter_map(|arg| arg.get_help_heading())
.collect::<IndexSet<_>>();
@ -764,8 +776,7 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
for heading in custom_headings {
let args = self
.app
.args
.args()
.get_arguments()
.filter(|a| {
if let Some(help_heading) = a.get_help_heading() {
return help_heading == heading;
@ -791,7 +802,11 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
self.none("\n\n")?;
}
self.warning(self.app.subcommand_heading.unwrap_or("SUBCOMMANDS"))?;
self.warning(
self.app
.get_subcommand_help_heading()
.unwrap_or("SUBCOMMANDS"),
)?;
self.warning(":\n")?;
self.write_subcommands(self.app)?;
@ -801,9 +816,16 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
}
/// Will use next line help on writing subcommands.
fn will_subcommands_wrap(&self, subcommands: &[App<'help>], longest: usize) -> bool {
fn will_subcommands_wrap<'a>(
&self,
subcommands: impl IntoIterator<Item = &'a App<'help>>,
longest: usize,
) -> bool
where
'help: 'a,
{
subcommands
.iter()
.into_iter()
.filter(|&subcommand| should_show_subcommand(subcommand))
.any(|subcommand| {
let spec_vals = &self.sc_spec_vals(subcommand);
@ -818,18 +840,17 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
let mut longest = 2;
let mut ord_v = Vec::new();
for subcommand in app
.subcommands
.iter()
.get_subcommands()
.filter(|subcommand| should_show_subcommand(subcommand))
{
let mut sc_str = String::new();
if let Some(short) = subcommand.short_flag {
if let Some(short) = subcommand.get_short_flag() {
write!(sc_str, "-{}", short).unwrap();
}
if let Some(long) = subcommand.long_flag {
if let Some(long) = subcommand.get_long_flag() {
write!(sc_str, "--{}", long).unwrap();
}
sc_str.push_str(&subcommand.name);
sc_str.push_str(subcommand.get_name());
longest = longest.max(display_width(&sc_str));
ord_v.push((subcommand.get_display_order(), sc_str, subcommand));
}
@ -837,7 +858,7 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
debug!("Help::write_subcommands longest = {}", longest);
let next_line_help = self.will_subcommands_wrap(&app.subcommands, longest);
let next_line_help = self.will_subcommands_wrap(app.get_subcommands(), longest);
let mut first = true;
for (_, sc_str, sc) in &ord_v {
@ -855,15 +876,15 @@ impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
fn write_bin_name(&mut self) -> io::Result<()> {
debug!("Help::write_bin_name");
let bin_name = if let Some(bn) = self.app.bin_name.as_ref() {
let bin_name = if let Some(bn) = self.app.get_bin_name() {
if bn.contains(' ') {
// In case we're dealing with subcommands i.e. git mv is translated to git-mv
bn.replace(' ', "-")
} else {
text_wrapper(&self.app.name.replace("{n}", "\n"), self.term_w)
text_wrapper(&self.app.get_name().replace("{n}", "\n"), self.term_w)
}
} else {
text_wrapper(&self.app.name.replace("{n}", "\n"), self.term_w)
text_wrapper(&self.app.get_name().replace("{n}", "\n"), self.term_w)
};
self.good(&bin_name)?;
Ok(())

View file

@ -39,7 +39,7 @@ impl<'help, 'app> Usage<'help, 'app> {
// Creates a usage string (*without title*) if one was not provided by the user manually.
pub(crate) fn create_usage_no_title(&self, used: &[Id]) -> String {
debug!("Usage::create_usage_no_title");
if let Some(u) = self.app.usage_str {
if let Some(u) = self.app.get_override_usage() {
String::from(&*u)
} else if used.is_empty() {
self.create_help_usage(true)
@ -54,11 +54,10 @@ impl<'help, 'app> Usage<'help, 'app> {
let mut usage = String::with_capacity(75);
let name = self
.app
.usage_name
.as_ref()
.or_else(|| self.app.bin_name.as_ref())
.unwrap_or(&self.app.name);
usage.push_str(&*name);
.get_usage_name()
.or_else(|| self.app.get_bin_name())
.unwrap_or_else(|| self.app.get_name());
usage.push_str(name);
let req_string = if incl_reqs {
self.get_required_usage_from(&[], None, false)
.iter()
@ -129,7 +128,7 @@ impl<'help, 'app> Usage<'help, 'app> {
if self.app.has_visible_subcommands() && incl_reqs
|| self.app.is_allow_external_subcommands_set()
{
let placeholder = self.app.subcommand_value_name.unwrap_or("SUBCOMMAND");
let placeholder = self.app.get_subcommand_value_name().unwrap_or("SUBCOMMAND");
#[allow(deprecated)]
if self.app.is_subcommand_negates_reqs_set()
|| self.app.is_args_conflicts_with_subcommands_set()
@ -172,17 +171,15 @@ impl<'help, 'app> Usage<'help, 'app> {
.fold(String::new(), |acc, s| acc + " " + s);
usage.push_str(
&self
.app
.usage_name
.as_ref()
.or_else(|| self.app.bin_name.as_ref())
.unwrap_or(&self.app.name)[..],
self.app
.get_usage_name()
.or_else(|| self.app.get_bin_name())
.unwrap_or_else(|| self.app.get_name()),
);
usage.push_str(&*r_string);
if self.app.is_subcommand_required_set() {
usage.push_str(" <");
usage.push_str(self.app.subcommand_value_name.unwrap_or("SUBCOMMAND"));
usage.push_str(self.app.get_subcommand_value_name().unwrap_or("SUBCOMMAND"));
usage.push('>');
}
usage.shrink_to_fit();
@ -204,10 +201,7 @@ impl<'help, 'app> Usage<'help, 'app> {
let required = self.app.groups_for_arg(&pos.id).any(|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
self.app
.groups
.iter()
.any(|g| g.required && (g.id == grp_s))
self.app.get_groups().any(|g| g.required && (g.id == grp_s))
});
if !required {
count += 1;
@ -234,10 +228,7 @@ impl<'help, 'app> Usage<'help, 'app> {
&& !self.app.groups_for_arg(&pos.id).any(|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
self.app
.groups
.iter()
.any(|g| g.required && (g.id == grp_s))
self.app.get_groups().any(|g| g.required && (g.id == grp_s))
})
})
.expect(INTERNAL_ERROR_MSG);
@ -319,7 +310,7 @@ impl<'help, 'app> Usage<'help, 'app> {
}
for grp_s in self.app.groups_for_arg(&f.id) {
debug!("Usage::needs_options_tag:iter:iter: grp_s={:?}", grp_s);
if self.app.groups.iter().any(|g| g.id == grp_s && g.required) {
if self.app.get_groups().any(|g| g.id == grp_s && g.required) {
debug!("Usage::needs_options_tag:iter:iter: Group is required");
continue 'outer;
}
@ -393,8 +384,7 @@ impl<'help, 'app> Usage<'help, 'app> {
let args_in_groups = self
.app
.groups
.iter()
.get_groups()
.filter(|gn| required.contains(&gn.id))
.flat_map(|g| self.app.unroll_args_in_group(&g.id))
.collect::<Vec<_>>();
@ -403,7 +393,7 @@ impl<'help, 'app> Usage<'help, 'app> {
.iter()
.chain(incls.iter())
.filter(|name| !self.app.get_positionals().any(|p| &&p.id == name))
.filter(|name| !self.app.groups.iter().any(|g| &&g.id == name))
.filter(|name| !self.app.get_groups().any(|g| &&g.id == name))
.filter(|name| !args_in_groups.contains(name))
.filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name)))
{
@ -414,7 +404,7 @@ impl<'help, 'app> Usage<'help, 'app> {
let mut g_vec: Vec<String> = vec![];
for g in unrolled_reqs
.iter()
.filter(|n| self.app.groups.iter().any(|g| g.id == **n))
.filter(|n| self.app.get_groups().any(|g| g.id == **n))
{
// don't print requirement for required groups that have an arg.
if let Some(m) = matcher {

View file

@ -19,12 +19,12 @@ impl ArgMatcher {
ArgMatcher(ArgMatches {
#[cfg(debug_assertions)]
valid_args: {
let args = _app.args.args().map(|a| a.id.clone());
let groups = _app.groups.iter().map(|g| g.id.clone());
let args = _app.get_arguments().map(|a| a.id.clone());
let groups = _app.get_groups().map(|g| g.id.clone());
args.chain(groups).collect()
},
#[cfg(debug_assertions)]
valid_subcommands: _app.subcommands.iter().map(|sc| sc.id.clone()).collect(),
valid_subcommands: _app.get_subcommands().map(|sc| sc.get_id()).collect(),
// HACK: Allow an external subcommand's ArgMatches be a stand-in for any ArgMatches
// since users can't detect it and avoid the asserts.
//

View file

@ -33,13 +33,14 @@ where
}
/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
pub(crate) fn did_you_mean_flag<I, T>(
pub(crate) fn did_you_mean_flag<'a, 'help, I, T>(
arg: &str,
remaining_args: &[&str],
longs: I,
subcommands: &mut [App],
subcommands: impl IntoIterator<Item = &'a mut App<'help>>,
) -> Option<(String, Option<String>)>
where
'help: 'a,
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
@ -48,11 +49,11 @@ where
match did_you_mean(arg, longs).pop() {
Some(candidate) => Some((candidate, None)),
None => subcommands
.iter_mut()
.into_iter()
.filter_map(|subcommand| {
subcommand._build();
let longs = subcommand.args.keys().filter_map(|a| {
let longs = subcommand.get_keymap().keys().filter_map(|a| {
if let KeyType::Long(v) = a {
Some(v.to_string_lossy().into_owned())
} else {

View file

@ -79,23 +79,23 @@ impl<'help, 'app> Parser<'help, 'app> {
let mut trailing_values = false;
// Count of positional args
let positional_count = self.app.args.keys().filter(|x| x.is_position()).count();
let positional_count = self
.app
.get_keymap()
.keys()
.filter(|x| x.is_position())
.count();
// If any arg sets .last(true)
let contains_last = self.app.args.args().any(|x| x.is_last_set());
let contains_last = self.app.get_arguments().any(|x| x.is_last_set());
while let Some((arg_os, remaining_args)) = it.next() {
// Recover the replaced items if any.
if let Some((_replacer, replaced_items)) = self
.app
.replacers
.iter()
.find(|(key, _)| OsStr::new(key) == arg_os)
{
it.insert(replaced_items);
if let Some(replaced_items) = self.app.get_replacement(arg_os) {
debug!(
"Parser::get_matches_with: found replacer: {:?}, target: {:?}",
_replacer, replaced_items
arg_os, replaced_items
);
it.insert(replaced_items);
continue;
}
@ -370,7 +370,7 @@ impl<'help, 'app> Parser<'help, 'app> {
}
}
if let Some(p) = self.app.args.get(&pos_counter) {
if let Some(p) = self.app.get_keymap().get(&pos_counter) {
if p.is_last_set() && !trailing_values {
return Err(ClapError::unknown_argument(
self.app,
@ -464,8 +464,8 @@ impl<'help, 'app> Parser<'help, 'app> {
.app
.find_subcommand(pos_sc_name)
.expect(INTERNAL_ERROR_MSG)
.name
.clone();
.get_name()
.to_owned();
self.parse_subcommand(&sc_name, matcher, it, keep_state)?;
}
@ -502,10 +502,9 @@ impl<'help, 'app> Parser<'help, 'app> {
arg_os.to_str_lossy().into_owned(),
candidates.join(" or "),
self.app
.bin_name
.as_ref()
.unwrap_or(&self.app.name)
.to_string(),
.get_bin_name()
.unwrap_or_else(|| self.app.get_name())
.to_owned(),
Usage::new(self.app).create_usage_with_title(&[]),
);
}
@ -516,10 +515,9 @@ impl<'help, 'app> Parser<'help, 'app> {
self.app,
arg_os.to_str_lossy().into_owned(),
self.app
.bin_name
.as_ref()
.unwrap_or(&self.app.name)
.to_string(),
.get_bin_name()
.unwrap_or_else(|| self.app.get_name())
.to_owned(),
);
}
ClapError::unknown_argument(
@ -552,7 +550,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// search.
}
if let Some(sc) = self.app.find_subcommand(arg_os) {
return Some(&sc.name);
return Some(sc.get_name());
}
}
None
@ -566,7 +564,7 @@ impl<'help, 'app> Parser<'help, 'app> {
.app
.get_subcommands()
.fold(Vec::new(), |mut options, sc| {
if let Some(long) = sc.long_flag {
if let Some(long) = sc.get_long_flag() {
if RawOsStr::from_str(long).starts_with_os(arg_os) {
options.push(long);
}
@ -595,7 +593,11 @@ impl<'help, 'app> Parser<'help, 'app> {
fn parse_help_subcommand(&self, cmds: &[OsString]) -> ClapResult<ParseResult> {
debug!("Parser::parse_help_subcommand");
let mut bin_name = self.app.bin_name.as_ref().unwrap_or(&self.app.name).clone();
let mut bin_name = self
.app
.get_bin_name()
.unwrap_or_else(|| self.app.get_name())
.to_owned();
let mut sc = {
let mut sc = self.app.clone();
@ -610,17 +612,16 @@ impl<'help, 'app> Parser<'help, 'app> {
self.app,
cmd.to_string_lossy().into_owned(),
self.app
.bin_name
.as_ref()
.unwrap_or(&self.app.name)
.to_string(),
.get_bin_name()
.unwrap_or_else(|| self.app.get_name())
.to_owned(),
));
}
.clone();
sc._build();
bin_name.push(' ');
bin_name.push_str(&sc.name);
bin_name.push_str(sc.get_name());
}
sc
@ -676,7 +677,10 @@ impl<'help, 'app> Parser<'help, 'app> {
if let Some(sc) = self.app._build_subcommand(sc_name) {
let mut sc_matcher = ArgMatcher::new(sc);
debug!("Parser::parse_subcommand: About to parse sc={}", sc.name);
debug!(
"Parser::parse_subcommand: About to parse sc={}",
sc.get_name()
);
{
let mut p = Parser::new(sc);
@ -699,8 +703,8 @@ impl<'help, 'app> Parser<'help, 'app> {
}
}
matcher.subcommand(SubCommand {
id: sc.id.clone(),
name: sc.name.clone(),
id: sc.get_id(),
name: sc.get_name().to_owned(),
matches: sc_matcher.into_inner(),
});
}
@ -787,10 +791,10 @@ impl<'help, 'app> Parser<'help, 'app> {
// Subcommands aren't checked because we prefer short help for them, deferring to
// `cmd subcmd --help` for more.
self.app.long_about.is_some()
|| self.app.before_long_help.is_some()
|| self.app.after_long_help.is_some()
|| self.app.args.args().any(should_long)
self.app.get_long_about().is_some()
|| self.app.get_before_long_help().is_some()
|| self.app.get_after_long_help().is_some()
|| self.app.get_arguments().any(should_long)
}
fn parse_long_arg(
@ -827,7 +831,7 @@ impl<'help, 'app> Parser<'help, 'app> {
(long_arg, None)
};
let opt = if let Some(opt) = self.app.args.get(&*arg.to_os_str()) {
let opt = if let Some(opt) = self.app.get_keymap().get(&*arg.to_os_str()) {
debug!(
"Parser::parse_long_arg: Found valid opt or flag '{}'",
opt.to_string()
@ -835,7 +839,7 @@ impl<'help, 'app> Parser<'help, 'app> {
Some(opt)
} else if self.app.is_infer_long_args_set() {
let arg_str = arg.to_str_lossy();
self.app.args.args().find(|a| {
self.app.get_arguments().find(|a| {
a.long.map_or(false, |long| long.starts_with(&*arg_str))
|| a.aliases
.iter()
@ -916,9 +920,14 @@ impl<'help, 'app> Parser<'help, 'app> {
{
debug!("Parser::parse_short_args: prior arg accepts hyphenated values",);
return ParseResult::MaybeHyphenValue;
} else if self.app.args.get(&pos_counter).map_or(false, |arg| {
arg.is_allow_hyphen_values_set() && !arg.is_last_set()
}) {
} else if self
.app
.get_keymap()
.get(&pos_counter)
.map_or(false, |arg| {
arg.is_allow_hyphen_values_set() && !arg.is_last_set()
})
{
debug!(
"Parser::parse_short_args: positional at {} allows hyphens",
pos_counter
@ -945,7 +954,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// concatenated value: -oval
// Option: -o
// Value: val
if let Some(opt) = self.app.args.get(&c) {
if let Some(opt) = self.app.get_keymap().get(&c) {
debug!(
"Parser::parse_short_arg:iter:{}: Found valid opt or flag",
c
@ -1353,7 +1362,7 @@ impl<'help, 'app> Parser<'help, 'app> {
) -> ClapResult<()> {
use crate::util::str_to_bool;
self.app.args.args().try_for_each(|a| {
self.app.get_arguments().try_for_each(|a| {
// Use env only if the arg was absent among command line args,
// early return if this is not the case.
if matcher
@ -1434,7 +1443,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// Didn't match a flag or option
let longs = self
.app
.args
.get_keymap()
.keys()
.filter_map(|x| match x {
KeyType::Long(l) => Some(l.to_string_lossy().into_owned()),
@ -1447,12 +1456,12 @@ impl<'help, 'app> Parser<'help, 'app> {
arg,
remaining_args,
longs.iter().map(|x| &x[..]),
self.app.subcommands.as_mut_slice(),
self.app.get_subcommands_mut(),
);
// Add the arg to the matches to build a proper usage string
if let Some((name, _)) = did_you_mean.as_ref() {
if let Some(opt) = self.app.args.get(&name.as_ref()) {
if let Some(opt) = self.app.get_keymap().get(&name.as_ref()) {
self.inc_occurrence_of_arg(matcher, opt);
}
}

View file

@ -68,7 +68,11 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
}
#[allow(deprecated)]
if !has_subcmd && self.p.app.is_subcommand_required_set() {
let bn = self.p.app.bin_name.as_ref().unwrap_or(&self.p.app.name);
let bn = self
.p
.app
.get_bin_name()
.unwrap_or_else(|| self.p.app.get_name());
return Err(Error::missing_subcommand(
self.p.app,
bn.to_string(),
@ -487,7 +491,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
}
// Validate the conditionally required args
for a in self.p.app.args.args() {
for a in self.p.app.get_arguments() {
for (other, val) in &a.r_ifs {
if matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val)))
&& !matcher.contains(&a.id)
@ -531,8 +535,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
let failed_args: Vec<_> = self
.p
.app
.args
.args()
.get_arguments()
.filter(|&a| {
(!a.r_unless.is_empty() || !a.r_unless_all.is_empty())
&& !matcher.contains(&a.id)