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