Ditch opts! and positionals!

This commit is contained in:
CreepySkeleton 2020-06-18 23:16:46 +03:00
parent f46af5b96b
commit b7be22e90b
10 changed files with 105 additions and 66 deletions

View file

@ -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(" "),
); );

View file

@ -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);

View file

@ -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() {

View file

@ -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);

View file

@ -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
) )
} }
@ -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) {

View file

@ -151,6 +151,12 @@ impl<'b> App<'b> {
&self.args.args &self.args.args
} }
/// Get the list of *positional* arguments.
#[inline]
pub fn get_positionals(&self) -> impl Iterator<Item = &Arg<'b>> {
self.get_arguments().iter().filter(|a| a.is_positional())
}
/// Iterate through the *flags* that don't have custom heading. /// Iterate through the *flags* that don't have custom heading.
pub fn get_flags_no_heading(&self) -> impl Iterator<Item = &Arg<'b>> { pub fn get_flags_no_heading(&self) -> impl Iterator<Item = &Arg<'b>> {
self.get_arguments() self.get_arguments()
@ -159,6 +165,14 @@ impl<'b> App<'b> {
.filter(|a| !a.get_help_heading().is_some()) .filter(|a| !a.get_help_heading().is_some())
} }
/// 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()
.iter()
.filter(|a| a.is_set(ArgSettings::TakesValue) && a.get_index().is_none())
.filter(|a| !a.get_help_heading().is_some())
}
/// Get the list of arguments the given argument conflicts with /// Get the list of arguments the given argument conflicts with
/// ///
/// ### Panics /// ### Panics
@ -1963,7 +1977,7 @@ impl<'b> App<'b> {
} }
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
} }
pub(crate) fn has_flags(&self) -> bool { pub(crate) fn has_flags(&self) -> bool {

View file

@ -553,30 +553,6 @@ macro_rules! debug {
($($arg:tt)*) => {}; ($($arg:tt)*) => {};
} }
#[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 { macro_rules! groups_for_arg {
($app:expr, $grp:expr) => {{ ($app:expr, $grp:expr) => {{
debug!("groups_for_arg: name={:?}", $grp); debug!("groups_for_arg: name={:?}", $grp);

View file

@ -656,15 +656,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();
@ -680,7 +683,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
@ -1059,10 +1062,10 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
self.write_args(&self.parser.app.get_flags_no_heading().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)?;

View file

@ -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,7 +205,10 @@ 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))
@ -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,9 +286,11 @@ 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
.app
.get_positionals()
.filter_map(|pos| { .filter_map(|pos| {
if pos.index <= highest_req_pos { if pos.index <= highest_req_pos {
Some(pos) Some(pos)
@ -356,7 +393,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 +404,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 +420,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)))

View file

@ -166,7 +166,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
@ -201,7 +201,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
@ -222,7 +224,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),
@ -278,13 +280,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)
@ -331,7 +336,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
@ -341,7 +346,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);
@ -555,8 +560,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 {
@ -1469,12 +1476,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)?;
} }