From 94a0819fe3a0d679c3042a77bfe6a2afc505daea Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:28 +0200 Subject: Adding upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/clap-3.2.20/src/output/usage.rs | 449 +++++++++++++++++++++++++++++++++ 1 file changed, 449 insertions(+) create mode 100644 vendor/clap-3.2.20/src/output/usage.rs (limited to 'vendor/clap-3.2.20/src/output/usage.rs') diff --git a/vendor/clap-3.2.20/src/output/usage.rs b/vendor/clap-3.2.20/src/output/usage.rs new file mode 100644 index 000000000..6f7a2cad4 --- /dev/null +++ b/vendor/clap-3.2.20/src/output/usage.rs @@ -0,0 +1,449 @@ +use indexmap::IndexSet; + +// Internal +use crate::builder::AppSettings as AS; +use crate::builder::{Arg, ArgPredicate, Command}; +use crate::parser::ArgMatcher; +use crate::util::ChildGraph; +use crate::util::Id; +use crate::INTERNAL_ERROR_MSG; + +pub(crate) struct Usage<'help, 'cmd> { + cmd: &'cmd Command<'help>, + required: Option<&'cmd ChildGraph>, +} + +impl<'help, 'cmd> Usage<'help, 'cmd> { + pub(crate) fn new(cmd: &'cmd Command<'help>) -> Self { + Usage { + cmd, + required: None, + } + } + + pub(crate) fn required(mut self, required: &'cmd ChildGraph) -> Self { + self.required = Some(required); + self + } + + // Creates a usage string for display. This happens just after all arguments were parsed, but before + // any subcommands have been parsed (so as to give subcommands their own usage recursively) + pub(crate) fn create_usage_with_title(&self, used: &[Id]) -> String { + debug!("Usage::create_usage_with_title"); + let mut usage = String::with_capacity(75); + usage.push_str("USAGE:\n "); + usage.push_str(&*self.create_usage_no_title(used)); + usage + } + + // 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.cmd.get_override_usage() { + String::from(&*u) + } else if used.is_empty() { + self.create_help_usage(true) + } else { + self.create_smart_usage(used) + } + } + + // Creates a usage string for display in help messages (i.e. not for errors) + fn create_help_usage(&self, incl_reqs: bool) -> String { + debug!("Usage::create_help_usage; incl_reqs={:?}", incl_reqs); + let mut usage = String::with_capacity(75); + let name = self + .cmd + .get_usage_name() + .or_else(|| self.cmd.get_bin_name()) + .unwrap_or_else(|| self.cmd.get_name()); + usage.push_str(name); + let req_string = if incl_reqs { + self.get_required_usage_from(&[], None, false) + .iter() + .fold(String::new(), |a, s| a + " " + s) + } else { + String::new() + }; + + if self.needs_options_tag() { + usage.push_str(" [OPTIONS]"); + } + + let allow_missing_positional = self.cmd.is_allow_missing_positional_set(); + if !allow_missing_positional { + usage.push_str(&req_string); + } + + let has_last = self.cmd.get_positionals().any(|p| p.is_last_set()); + // places a '--' in the usage string if there are args and options + // supporting multiple values + if self + .cmd + .get_non_positionals() + .any(|o| o.is_multiple_values_set()) + && self.cmd.get_positionals().any(|p| !p.is_required_set()) + && !(self.cmd.has_visible_subcommands() || self.cmd.is_allow_external_subcommands_set()) + && !has_last + { + usage.push_str(" [--]"); + } + let not_req_or_hidden = + |p: &Arg| (!p.is_required_set() || p.is_last_set()) && !p.is_hide_set(); + if self.cmd.get_positionals().any(not_req_or_hidden) { + if let Some(args_tag) = self.get_args_tag(incl_reqs) { + usage.push_str(&*args_tag); + } else { + usage.push_str(" [ARGS]"); + } + if has_last && incl_reqs { + let pos = self + .cmd + .get_positionals() + .find(|p| p.is_last_set()) + .expect(INTERNAL_ERROR_MSG); + debug!("Usage::create_help_usage: '{}' has .last(true)", pos.name); + let req = pos.is_required_set(); + if req && self.cmd.get_positionals().any(|p| !p.is_required_set()) { + usage.push_str(" -- <"); + } else if req { + usage.push_str(" [--] <"); + } else { + usage.push_str(" [-- <"); + } + usage.push_str(&*pos.name_no_brackets()); + usage.push('>'); + usage.push_str(pos.multiple_str()); + if !req { + usage.push(']'); + } + } + } + + if allow_missing_positional { + usage.push_str(&req_string); + } + + // incl_reqs is only false when this function is called recursively + if self.cmd.has_visible_subcommands() && incl_reqs + || self.cmd.is_allow_external_subcommands_set() + { + let placeholder = self.cmd.get_subcommand_value_name().unwrap_or("SUBCOMMAND"); + #[allow(deprecated)] + if self.cmd.is_subcommand_negates_reqs_set() + || self.cmd.is_args_conflicts_with_subcommands_set() + { + usage.push_str("\n "); + if !self.cmd.is_args_conflicts_with_subcommands_set() { + usage.push_str(&*self.create_help_usage(false)); + } else { + usage.push_str(&*name); + } + usage.push_str(" <"); + usage.push_str(placeholder); + usage.push('>'); + } else if self.cmd.is_subcommand_required_set() + || self.cmd.is_set(AS::SubcommandRequiredElseHelp) + { + usage.push_str(" <"); + usage.push_str(placeholder); + usage.push('>'); + } else { + usage.push_str(" ["); + usage.push_str(placeholder); + usage.push(']'); + } + } + let usage = usage.trim().to_owned(); + debug!("Usage::create_help_usage: usage={}", usage); + usage + } + + // Creates a context aware usage string, or "smart usage" from currently used + // args, and requirements + fn create_smart_usage(&self, used: &[Id]) -> String { + debug!("Usage::create_smart_usage"); + let mut usage = String::with_capacity(75); + + let r_string = self + .get_required_usage_from(used, None, true) + .iter() + .fold(String::new(), |acc, s| acc + " " + s); + + usage.push_str( + self.cmd + .get_usage_name() + .or_else(|| self.cmd.get_bin_name()) + .unwrap_or_else(|| self.cmd.get_name()), + ); + usage.push_str(&*r_string); + if self.cmd.is_subcommand_required_set() { + usage.push_str(" <"); + usage.push_str(self.cmd.get_subcommand_value_name().unwrap_or("SUBCOMMAND")); + usage.push('>'); + } + usage.shrink_to_fit(); + usage + } + + // Gets the `[ARGS]` tag for the usage string + fn get_args_tag(&self, incl_reqs: bool) -> Option { + debug!("Usage::get_args_tag; incl_reqs = {:?}", incl_reqs); + let mut count = 0; + for pos in self + .cmd + .get_positionals() + .filter(|pos| !pos.is_required_set()) + .filter(|pos| !pos.is_hide_set()) + .filter(|pos| !pos.is_last_set()) + { + debug!("Usage::get_args_tag:iter:{}", pos.name); + let required = self.cmd.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.cmd.get_groups().any(|g| g.required && (g.id == grp_s)) + }); + if !required { + count += 1; + debug!( + "Usage::get_args_tag:iter: {} Args not required or hidden", + count + ); + } + } + + if !self.cmd.is_dont_collapse_args_in_usage_set() && count > 1 { + debug!("Usage::get_args_tag:iter: More than one, returning [ARGS]"); + + // [ARGS] + None + } else if count == 1 && incl_reqs { + let pos = self + .cmd + .get_positionals() + .find(|pos| { + !pos.is_required_set() + && !pos.is_hide_set() + && !pos.is_last_set() + && !self.cmd.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.cmd.get_groups().any(|g| g.required && (g.id == grp_s)) + }) + }) + .expect(INTERNAL_ERROR_MSG); + + debug!( + "Usage::get_args_tag:iter: Exactly one, returning '{}'", + pos.name + ); + + Some(format!( + " [{}]{}", + pos.name_no_brackets(), + pos.multiple_str() + )) + } else if self.cmd.is_dont_collapse_args_in_usage_set() + && self.cmd.has_positionals() + && incl_reqs + { + debug!("Usage::get_args_tag:iter: Don't collapse returning all"); + Some( + self.cmd + .get_positionals() + .filter(|pos| !pos.is_required_set()) + .filter(|pos| !pos.is_hide_set()) + .filter(|pos| !pos.is_last_set()) + .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str())) + .collect::>() + .join(""), + ) + } else if !incl_reqs { + debug!("Usage::get_args_tag:iter: incl_reqs=false, building secondary usage string"); + let highest_req_pos = self + .cmd + .get_positionals() + .filter_map(|pos| { + if pos.is_required_set() && !pos.is_last_set() { + Some(pos.index) + } else { + None + } + }) + .max() + .unwrap_or_else(|| Some(self.cmd.get_positionals().count())); + Some( + self.cmd + .get_positionals() + .filter(|pos| pos.index <= highest_req_pos) + .filter(|pos| !pos.is_required_set()) + .filter(|pos| !pos.is_hide_set()) + .filter(|pos| !pos.is_last_set()) + .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str())) + .collect::>() + .join(""), + ) + } else { + Some("".into()) + } + } + + // Determines if we need the `[OPTIONS]` tag in the usage string + fn needs_options_tag(&self) -> bool { + debug!("Usage::needs_options_tag"); + 'outer: for f in self.cmd.get_non_positionals() { + debug!("Usage::needs_options_tag:iter: f={}", f.name); + + // Don't print `[OPTIONS]` just for help or version + if f.long == Some("help") || f.long == Some("version") { + debug!("Usage::needs_options_tag:iter Option is built-in"); + continue; + } + + if f.is_hide_set() { + debug!("Usage::needs_options_tag:iter Option is hidden"); + continue; + } + if f.is_required_set() { + debug!("Usage::needs_options_tag:iter Option is required"); + continue; + } + for grp_s in self.cmd.groups_for_arg(&f.id) { + debug!("Usage::needs_options_tag:iter:iter: grp_s={:?}", grp_s); + if self.cmd.get_groups().any(|g| g.id == grp_s && g.required) { + debug!("Usage::needs_options_tag:iter:iter: Group is required"); + continue 'outer; + } + } + + debug!("Usage::needs_options_tag:iter: [OPTIONS] required"); + return true; + } + + debug!("Usage::needs_options_tag: [OPTIONS] not required"); + false + } + + // Returns the required args in usage string form by fully unrolling all groups + // `incl_last`: should we include args that are Arg::Last? (i.e. `prog [foo] -- [last]). We + // can't do that for required usages being built for subcommands because it would look like: + // `prog [foo] -- [last] ` which is totally wrong. + pub(crate) fn get_required_usage_from( + &self, + incls: &[Id], + matcher: Option<&ArgMatcher>, + incl_last: bool, + ) -> IndexSet { + debug!( + "Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}", + incls, + matcher.is_some(), + incl_last + ); + let mut ret_val = IndexSet::new(); + + let mut unrolled_reqs = IndexSet::new(); + + let required_owned; + let required = if let Some(required) = self.required { + required + } else { + required_owned = self.cmd.required_graph(); + &required_owned + }; + + for a in required.iter() { + let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option { + let required = match val { + ArgPredicate::Equals(_) => { + if let Some(matcher) = matcher { + matcher.check_explicit(a, *val) + } else { + false + } + } + ArgPredicate::IsPresent => true, + }; + required.then(|| req_arg.clone()) + }; + + for aa in self.cmd.unroll_arg_requires(is_relevant, a) { + // if we don't check for duplicates here this causes duplicate error messages + // see https://github.com/clap-rs/clap/issues/2770 + unrolled_reqs.insert(aa); + } + // always include the required arg itself. it will not be enumerated + // by unroll_requirements_for_arg. + unrolled_reqs.insert(a.clone()); + } + + debug!( + "Usage::get_required_usage_from: unrolled_reqs={:?}", + unrolled_reqs + ); + + let mut required_groups_members = IndexSet::new(); + let mut required_opts = IndexSet::new(); + let mut required_groups = IndexSet::new(); + let mut required_positionals = Vec::new(); + for req in unrolled_reqs.iter().chain(incls.iter()) { + if let Some(arg) = self.cmd.find(req) { + let is_present = matcher + .map(|m| m.check_explicit(req, ArgPredicate::IsPresent)) + .unwrap_or(false); + debug!( + "Usage::get_required_usage_from:iter:{:?} arg is_present={}", + req, is_present + ); + if !is_present { + if arg.is_positional() { + if incl_last || !arg.is_last_set() { + required_positionals.push((arg.index.unwrap(), arg.to_string())); + } + } else { + required_opts.insert(arg.to_string()); + } + } + } else { + debug_assert!(self.cmd.find_group(req).is_some()); + let group_members = self.cmd.unroll_args_in_group(req); + let is_present = matcher + .map(|m| { + group_members + .iter() + .any(|arg| m.check_explicit(arg, ArgPredicate::IsPresent)) + }) + .unwrap_or(false); + debug!( + "Usage::get_required_usage_from:iter:{:?} group is_present={}", + req, is_present + ); + if !is_present { + let elem = self.cmd.format_group(req); + required_groups.insert(elem); + required_groups_members.extend( + group_members + .iter() + .flat_map(|id| self.cmd.find(id)) + .map(|arg| arg.to_string()), + ); + } + } + } + + required_opts.retain(|arg| !required_groups_members.contains(arg)); + ret_val.extend(required_opts); + + ret_val.extend(required_groups); + + required_positionals.sort_by_key(|(ind, _)| *ind); // sort by index + for (_, p) in required_positionals { + if !required_groups_members.contains(&p) { + ret_val.insert(p); + } + } + + debug!("Usage::get_required_usage_from: ret_val={:?}", ret_val); + ret_val + } +} -- cgit v1.2.3