]> git.proxmox.com Git - rustc.git/blame - vendor/clap_builder-4.3.10/src/output/usage.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / vendor / clap_builder-4.3.10 / src / output / usage.rs
CommitLineData
add651ee
FG
1#![cfg_attr(not(feature = "usage"), allow(unused_imports))]
2#![cfg_attr(not(feature = "usage"), allow(unused_variables))]
3#![cfg_attr(not(feature = "usage"), allow(clippy::manual_map))]
4#![cfg_attr(not(feature = "usage"), allow(dead_code))]
5
6// Internal
7use crate::builder::StyledStr;
8use crate::builder::Styles;
9use crate::builder::{ArgPredicate, Command};
10use crate::parser::ArgMatcher;
11use crate::util::ChildGraph;
12use crate::util::FlatSet;
13use crate::util::Id;
14
15static DEFAULT_SUB_VALUE_NAME: &str = "COMMAND";
16
17pub(crate) struct Usage<'cmd> {
18 cmd: &'cmd Command,
19 styles: &'cmd Styles,
20 required: Option<&'cmd ChildGraph<Id>>,
21}
22
23impl<'cmd> Usage<'cmd> {
24 pub(crate) fn new(cmd: &'cmd Command) -> Self {
25 Usage {
26 cmd,
27 styles: cmd.get_styles(),
28 required: None,
29 }
30 }
31
32 pub(crate) fn required(mut self, required: &'cmd ChildGraph<Id>) -> Self {
33 self.required = Some(required);
34 self
35 }
36
37 // Creates a usage string for display. This happens just after all arguments were parsed, but before
38 // any subcommands have been parsed (so as to give subcommands their own usage recursively)
39 pub(crate) fn create_usage_with_title(&self, used: &[Id]) -> Option<StyledStr> {
40 debug!("Usage::create_usage_with_title");
41 let usage = some!(self.create_usage_no_title(used));
42
43 use std::fmt::Write as _;
44 let mut styled = StyledStr::new();
45 let _ = write!(
46 styled,
47 "{}Usage:{} ",
48 self.styles.get_usage().render(),
49 self.styles.get_usage().render_reset()
50 );
51 styled.push_styled(&usage);
52 Some(styled)
53 }
54
55 // Creates a usage string (*without title*) if one was not provided by the user manually.
56 pub(crate) fn create_usage_no_title(&self, used: &[Id]) -> Option<StyledStr> {
57 debug!("Usage::create_usage_no_title");
58 if let Some(u) = self.cmd.get_override_usage() {
59 Some(u.clone())
60 } else {
61 #[cfg(feature = "usage")]
62 {
63 if used.is_empty() {
64 Some(self.create_help_usage(true))
65 } else {
66 Some(self.create_smart_usage(used))
67 }
68 }
69
70 #[cfg(not(feature = "usage"))]
71 {
72 None
73 }
74 }
75 }
76}
77
78#[cfg(feature = "usage")]
79impl<'cmd> Usage<'cmd> {
80 // Creates a usage string for display in help messages (i.e. not for errors)
81 fn create_help_usage(&self, incl_reqs: bool) -> StyledStr {
82 debug!("Usage::create_help_usage; incl_reqs={incl_reqs:?}");
83 use std::fmt::Write as _;
84 let literal = &self.styles.get_literal();
85 let placeholder = &self.styles.get_placeholder();
86 let mut styled = StyledStr::new();
87
88 let name = self
89 .cmd
90 .get_usage_name()
91 .or_else(|| self.cmd.get_bin_name())
92 .unwrap_or_else(|| self.cmd.get_name());
93 if !name.is_empty() {
94 // the trim won't properly remove a leading space due to the formatting
95 let _ = write!(
96 styled,
97 "{}{name}{}",
98 literal.render(),
99 literal.render_reset()
100 );
101 }
102
103 if self.needs_options_tag() {
104 let _ = write!(
105 styled,
106 "{} [OPTIONS]{}",
107 placeholder.render(),
108 placeholder.render_reset()
109 );
110 }
111
112 self.write_args(&[], !incl_reqs, &mut styled);
113
114 // incl_reqs is only false when this function is called recursively
115 if self.cmd.has_visible_subcommands() && incl_reqs
116 || self.cmd.is_allow_external_subcommands_set()
117 {
118 let value_name = self
119 .cmd
120 .get_subcommand_value_name()
121 .unwrap_or(DEFAULT_SUB_VALUE_NAME);
122 if self.cmd.is_subcommand_negates_reqs_set()
123 || self.cmd.is_args_conflicts_with_subcommands_set()
124 {
125 let _ = write!(styled, "\n ");
126 if self.cmd.is_args_conflicts_with_subcommands_set() {
127 // Short-circuit full usage creation since no args will be relevant
128 let _ = write!(
129 styled,
130 "{}{name}{}",
131 literal.render(),
132 literal.render_reset()
133 );
134 } else {
135 styled.push_styled(&self.create_help_usage(false));
136 }
137 let _ = write!(
138 styled,
139 " {}<{value_name}>{}",
140 placeholder.render(),
141 placeholder.render_reset()
142 );
143 } else if self.cmd.is_subcommand_required_set() {
144 let _ = write!(
145 styled,
146 " {}<{value_name}>{}",
147 placeholder.render(),
148 placeholder.render_reset()
149 );
150 } else {
151 let _ = write!(
152 styled,
153 " {}[{value_name}]{}",
154 placeholder.render(),
155 placeholder.render_reset()
156 );
157 }
158 }
159 styled.trim();
160 debug!("Usage::create_help_usage: usage={styled}");
161 styled
162 }
163
164 // Creates a context aware usage string, or "smart usage" from currently used
165 // args, and requirements
166 fn create_smart_usage(&self, used: &[Id]) -> StyledStr {
167 debug!("Usage::create_smart_usage");
168 use std::fmt::Write;
169 let literal = &self.styles.get_literal();
170 let placeholder = &self.styles.get_placeholder();
171 let mut styled = StyledStr::new();
172
173 let bin_name = self
174 .cmd
175 .get_usage_name()
176 .or_else(|| self.cmd.get_bin_name())
177 .unwrap_or_else(|| self.cmd.get_name());
178 let _ = write!(
179 styled,
180 "{}{bin_name}{}",
181 literal.render(),
182 literal.render_reset()
183 );
184
185 self.write_args(used, false, &mut styled);
186
187 if self.cmd.is_subcommand_required_set() {
188 let value_name = self
189 .cmd
190 .get_subcommand_value_name()
191 .unwrap_or(DEFAULT_SUB_VALUE_NAME);
192 let _ = write!(
193 styled,
194 " {}<{value_name}>{}",
195 placeholder.render(),
196 placeholder.render_reset()
197 );
198 }
199 styled
200 }
201
202 // Determines if we need the `[OPTIONS]` tag in the usage string
203 fn needs_options_tag(&self) -> bool {
204 debug!("Usage::needs_options_tag");
205 'outer: for f in self.cmd.get_non_positionals() {
206 debug!("Usage::needs_options_tag:iter: f={}", f.get_id());
207
208 // Don't print `[OPTIONS]` just for help or version
209 if f.get_long() == Some("help") || f.get_long() == Some("version") {
210 debug!("Usage::needs_options_tag:iter Option is built-in");
211 continue;
212 }
213
214 if f.is_hide_set() {
215 debug!("Usage::needs_options_tag:iter Option is hidden");
216 continue;
217 }
218 if f.is_required_set() {
219 debug!("Usage::needs_options_tag:iter Option is required");
220 continue;
221 }
222 for grp_s in self.cmd.groups_for_arg(f.get_id()) {
223 debug!("Usage::needs_options_tag:iter:iter: grp_s={grp_s:?}");
224 if self.cmd.get_groups().any(|g| g.id == grp_s && g.required) {
225 debug!("Usage::needs_options_tag:iter:iter: Group is required");
226 continue 'outer;
227 }
228 }
229
230 debug!("Usage::needs_options_tag:iter: [OPTIONS] required");
231 return true;
232 }
233
234 debug!("Usage::needs_options_tag: [OPTIONS] not required");
235 false
236 }
237
238 // Returns the required args in usage string form by fully unrolling all groups
239 pub(crate) fn write_args(&self, incls: &[Id], force_optional: bool, styled: &mut StyledStr) {
240 for required in self.get_args(incls, force_optional) {
241 styled.push_str(" ");
242 styled.push_styled(&required);
243 }
244 }
245
246 pub(crate) fn get_args(&self, incls: &[Id], force_optional: bool) -> Vec<StyledStr> {
247 debug!("Usage::get_args: incls={incls:?}",);
248 use std::fmt::Write as _;
249 let literal = &self.styles.get_literal();
250
251 let required_owned;
252 let required = if let Some(required) = self.required {
253 required
254 } else {
255 required_owned = self.cmd.required_graph();
256 &required_owned
257 };
258
259 let mut unrolled_reqs = Vec::new();
260 for a in required.iter() {
261 let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
262 let required = match val {
263 ArgPredicate::Equals(_) => false,
264 ArgPredicate::IsPresent => true,
265 };
266 required.then(|| req_arg.clone())
267 };
268
269 for aa in self.cmd.unroll_arg_requires(is_relevant, a) {
270 // if we don't check for duplicates here this causes duplicate error messages
271 // see https://github.com/clap-rs/clap/issues/2770
272 unrolled_reqs.push(aa);
273 }
274 // always include the required arg itself. it will not be enumerated
275 // by unroll_requirements_for_arg.
276 unrolled_reqs.push(a.clone());
277 }
278 debug!("Usage::get_args: unrolled_reqs={unrolled_reqs:?}");
279
280 let mut required_groups_members = FlatSet::new();
281 let mut required_groups = FlatSet::new();
282 for req in unrolled_reqs.iter().chain(incls.iter()) {
283 if self.cmd.find_group(req).is_some() {
284 let group_members = self.cmd.unroll_args_in_group(req);
285 let elem = self.cmd.format_group(req);
286 required_groups.insert(elem);
287 required_groups_members.extend(group_members);
288 } else {
289 debug_assert!(self.cmd.find(req).is_some());
290 }
291 }
292
293 let mut required_opts = FlatSet::new();
294 let mut required_positionals = Vec::new();
295 for req in unrolled_reqs.iter().chain(incls.iter()) {
296 if let Some(arg) = self.cmd.find(req) {
297 if required_groups_members.contains(arg.get_id()) {
298 continue;
299 }
300
301 let stylized = arg.stylized(self.styles, Some(!force_optional));
302 if let Some(index) = arg.get_index() {
303 let new_len = index + 1;
304 if required_positionals.len() < new_len {
305 required_positionals.resize(new_len, None);
306 }
307 required_positionals[index] = Some(stylized);
308 } else {
309 required_opts.insert(stylized);
310 }
311 } else {
312 debug_assert!(self.cmd.find_group(req).is_some());
313 }
314 }
315
316 for pos in self.cmd.get_positionals() {
317 if pos.is_hide_set() {
318 continue;
319 }
320 if required_groups_members.contains(pos.get_id()) {
321 continue;
322 }
323
324 let index = pos.get_index().unwrap();
325 let new_len = index + 1;
326 if required_positionals.len() < new_len {
327 required_positionals.resize(new_len, None);
328 }
329 if required_positionals[index].is_some() {
330 if pos.is_last_set() {
331 let styled = required_positionals[index].take().unwrap();
332 let mut new = StyledStr::new();
333 let _ = write!(new, "{}--{} ", literal.render(), literal.render_reset());
334 new.push_styled(&styled);
335 required_positionals[index] = Some(new);
336 }
337 } else {
338 let mut styled;
339 if pos.is_last_set() {
340 styled = StyledStr::new();
341 let _ = write!(styled, "{}[--{} ", literal.render(), literal.render_reset());
342 styled.push_styled(&pos.stylized(self.styles, Some(true)));
343 let _ = write!(styled, "{}]{}", literal.render(), literal.render_reset());
344 } else {
345 styled = pos.stylized(self.styles, Some(false));
346 }
347 required_positionals[index] = Some(styled);
348 }
349 if pos.is_last_set() && force_optional {
350 required_positionals[index] = None;
351 }
352 }
353
354 let mut ret_val = Vec::new();
355 if !force_optional {
356 ret_val.extend(required_opts);
357 ret_val.extend(required_groups);
358 }
359 for pos in required_positionals.into_iter().flatten() {
360 ret_val.push(pos);
361 }
362
363 debug!("Usage::get_args: ret_val={ret_val:?}");
364 ret_val
365 }
366
367 pub(crate) fn get_required_usage_from(
368 &self,
369 incls: &[Id],
370 matcher: Option<&ArgMatcher>,
371 incl_last: bool,
372 ) -> Vec<StyledStr> {
373 debug!(
374 "Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}",
375 incls,
376 matcher.is_some(),
377 incl_last
378 );
379
380 let required_owned;
381 let required = if let Some(required) = self.required {
382 required
383 } else {
384 required_owned = self.cmd.required_graph();
385 &required_owned
386 };
387
388 let mut unrolled_reqs = Vec::new();
389 for a in required.iter() {
390 let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
391 let required = match val {
392 ArgPredicate::Equals(_) => {
393 if let Some(matcher) = matcher {
394 matcher.check_explicit(a, val)
395 } else {
396 false
397 }
398 }
399 ArgPredicate::IsPresent => true,
400 };
401 required.then(|| req_arg.clone())
402 };
403
404 for aa in self.cmd.unroll_arg_requires(is_relevant, a) {
405 // if we don't check for duplicates here this causes duplicate error messages
406 // see https://github.com/clap-rs/clap/issues/2770
407 unrolled_reqs.push(aa);
408 }
409 // always include the required arg itself. it will not be enumerated
410 // by unroll_requirements_for_arg.
411 unrolled_reqs.push(a.clone());
412 }
413 debug!("Usage::get_required_usage_from: unrolled_reqs={unrolled_reqs:?}");
414
415 let mut required_groups_members = FlatSet::new();
416 let mut required_groups = FlatSet::new();
417 for req in unrolled_reqs.iter().chain(incls.iter()) {
418 if self.cmd.find_group(req).is_some() {
419 let group_members = self.cmd.unroll_args_in_group(req);
420 let is_present = matcher
421 .map(|m| {
422 group_members
423 .iter()
424 .any(|arg| m.check_explicit(arg, &ArgPredicate::IsPresent))
425 })
426 .unwrap_or(false);
427 debug!("Usage::get_required_usage_from:iter:{req:?} group is_present={is_present}");
428 if is_present {
429 continue;
430 }
431
432 let elem = self.cmd.format_group(req);
433 required_groups.insert(elem);
434 required_groups_members.extend(group_members);
435 } else {
436 debug_assert!(self.cmd.find(req).is_some(), "`{req}` must exist");
437 }
438 }
439
440 let mut required_opts = FlatSet::new();
441 let mut required_positionals = Vec::new();
442 for req in unrolled_reqs.iter().chain(incls.iter()) {
443 if let Some(arg) = self.cmd.find(req) {
444 if required_groups_members.contains(arg.get_id()) {
445 continue;
446 }
447
448 let is_present = matcher
449 .map(|m| m.check_explicit(req, &ArgPredicate::IsPresent))
450 .unwrap_or(false);
451 debug!("Usage::get_required_usage_from:iter:{req:?} arg is_present={is_present}");
452 if is_present {
453 continue;
454 }
455
456 let stylized = arg.stylized(self.styles, Some(true));
457 if let Some(index) = arg.get_index() {
458 if !arg.is_last_set() || incl_last {
459 let new_len = index + 1;
460 if required_positionals.len() < new_len {
461 required_positionals.resize(new_len, None);
462 }
463 required_positionals[index] = Some(stylized);
464 }
465 } else {
466 required_opts.insert(stylized);
467 }
468 } else {
469 debug_assert!(self.cmd.find_group(req).is_some());
470 }
471 }
472
473 let mut ret_val = Vec::new();
474 ret_val.extend(required_opts);
475 ret_val.extend(required_groups);
476 for pos in required_positionals.into_iter().flatten() {
477 ret_val.push(pos);
478 }
479
480 debug!("Usage::get_required_usage_from: ret_val={ret_val:?}");
481 ret_val
482 }
483}