| 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT | 
| 2 | // file at the top-level directory of this distribution and at | 
|---|
| 3 | // http://rust-lang.org/COPYRIGHT. | 
|---|
| 4 | // | 
|---|
| 5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | 
|---|
| 6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | 
|---|
| 7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | 
|---|
| 8 | // option. This file may not be copied, modified, or distributed | 
|---|
| 9 | // except according to those terms. | 
|---|
| 10 | // | 
|---|
| 11 | // ignore-lexer-test FIXME #15677 | 
|---|
| 12 |  | 
|---|
| 13 | //! Simple getopt alternative. | 
|---|
| 14 | //! | 
|---|
| 15 | //! Construct a vector of options, either by using `reqopt`, `optopt`, and | 
|---|
| 16 | //! `optflag` or by building them from components yourself, and pass them to | 
|---|
| 17 | //! `getopts`, along with a vector of actual arguments (not including | 
|---|
| 18 | //! `argv[0]`). You'll either get a failure code back, or a match. You'll have | 
|---|
| 19 | //! to verify whether the amount of 'free' arguments in the match is what you | 
|---|
| 20 | //! expect. Use `opt_*` accessors to get argument values out of the matches | 
|---|
| 21 | //! object. | 
|---|
| 22 | //! | 
|---|
| 23 | //! Single-character options are expected to appear on the command line with a | 
|---|
| 24 | //! single preceding dash; multiple-character options are expected to be | 
|---|
| 25 | //! proceeded by two dashes. Options that expect an argument accept their | 
|---|
| 26 | //! argument following either a space or an equals sign. Single-character | 
|---|
| 27 | //! options don't require the space. | 
|---|
| 28 | //! | 
|---|
| 29 | //! # Usage | 
|---|
| 30 | //! | 
|---|
| 31 | //! This crate is [on crates.io](https://crates.io/crates/getopts) and can be | 
|---|
| 32 | //! used by adding `getopts` to the dependencies in your project's `Cargo.toml`. | 
|---|
| 33 | //! | 
|---|
| 34 | //! ```toml | 
|---|
| 35 | //! [dependencies] | 
|---|
| 36 | //! getopts = "0.2" | 
|---|
| 37 | //! ``` | 
|---|
| 38 | //! | 
|---|
| 39 | //! and this to your crate root: | 
|---|
| 40 | //! | 
|---|
| 41 | //! ```rust | 
|---|
| 42 | //! extern crate getopts; | 
|---|
| 43 | //! ``` | 
|---|
| 44 | //! | 
|---|
| 45 | //! # Example | 
|---|
| 46 | //! | 
|---|
| 47 | //! The following example shows simple command line parsing for an application | 
|---|
| 48 | //! that requires an input file to be specified, accepts an optional output file | 
|---|
| 49 | //! name following `-o`, and accepts both `-h` and `--help` as optional flags. | 
|---|
| 50 | //! | 
|---|
| 51 | //! ```{.rust} | 
|---|
| 52 | //! extern crate getopts; | 
|---|
| 53 | //! use getopts::Options; | 
|---|
| 54 | //! use std::env; | 
|---|
| 55 | //! | 
|---|
| 56 | //! fn do_work(inp: &str, out: Option<String>) { | 
|---|
| 57 | //!     println!("{}", inp); | 
|---|
| 58 | //!     match out { | 
|---|
| 59 | //!         Some(x) => println!("{}", x), | 
|---|
| 60 | //!         None => println!("No Output"), | 
|---|
| 61 | //!     } | 
|---|
| 62 | //! } | 
|---|
| 63 | //! | 
|---|
| 64 | //! fn print_usage(program: &str, opts: Options) { | 
|---|
| 65 | //!     let brief = format!("Usage: {} FILE [options]", program); | 
|---|
| 66 | //!     print!("{}", opts.usage(&brief)); | 
|---|
| 67 | //! } | 
|---|
| 68 | //! | 
|---|
| 69 | //! fn main() { | 
|---|
| 70 | //!     let args: Vec<String> = env::args().collect(); | 
|---|
| 71 | //!     let program = args[0].clone(); | 
|---|
| 72 | //! | 
|---|
| 73 | //!     let mut opts = Options::new(); | 
|---|
| 74 | //!     opts.optopt("o", "", "set output file name", "NAME"); | 
|---|
| 75 | //!     opts.optflag("h", "help", "print this help menu"); | 
|---|
| 76 | //!     let matches = match opts.parse(&args[1..]) { | 
|---|
| 77 | //!         Ok(m) => { m } | 
|---|
| 78 | //!         Err(f) => { panic!(f.to_string()) } | 
|---|
| 79 | //!     }; | 
|---|
| 80 | //!     if matches.opt_present("h") { | 
|---|
| 81 | //!         print_usage(&program, opts); | 
|---|
| 82 | //!         return; | 
|---|
| 83 | //!     } | 
|---|
| 84 | //!     let output = matches.opt_str("o"); | 
|---|
| 85 | //!     let input = if !matches.free.is_empty() { | 
|---|
| 86 | //!         matches.free[0].clone() | 
|---|
| 87 | //!     } else { | 
|---|
| 88 | //!         print_usage(&program, opts); | 
|---|
| 89 | //!         return; | 
|---|
| 90 | //!     }; | 
|---|
| 91 | //!     do_work(&input, output); | 
|---|
| 92 | //! } | 
|---|
| 93 | //! ``` | 
|---|
| 94 |  | 
|---|
| 95 | #![ doc( | 
|---|
| 96 | html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", | 
|---|
| 97 | html_favicon_url = "https://www.rust-lang.org/favicon.ico", | 
|---|
| 98 | html_root_url = "https://docs.rs/getopts/0.2.20" | 
|---|
| 99 | )] | 
|---|
| 100 | #![ deny(missing_docs)] | 
|---|
| 101 | #![ cfg_attr(test, deny(warnings))] | 
|---|
| 102 |  | 
|---|
| 103 | #[ cfg(test)] | 
|---|
| 104 | #[ macro_use] | 
|---|
| 105 | extern crate log; | 
|---|
| 106 | extern crate unicode_width; | 
|---|
| 107 |  | 
|---|
| 108 | use self::Fail::*; | 
|---|
| 109 | use self::HasArg::*; | 
|---|
| 110 | use self::Name::*; | 
|---|
| 111 | use self::Occur::*; | 
|---|
| 112 | use self::Optval::*; | 
|---|
| 113 |  | 
|---|
| 114 | use std::error::Error; | 
|---|
| 115 | use std::ffi::OsStr; | 
|---|
| 116 | use std::fmt; | 
|---|
| 117 | use std::iter::{repeat, IntoIterator}; | 
|---|
| 118 | use std::result; | 
|---|
| 119 | use std::str::FromStr; | 
|---|
| 120 |  | 
|---|
| 121 | use unicode_width::UnicodeWidthStr; | 
|---|
| 122 |  | 
|---|
| 123 | #[ cfg(test)] | 
|---|
| 124 | mod tests; | 
|---|
| 125 |  | 
|---|
| 126 | /// A description of the options that a program can handle. | 
|---|
| 127 | pub struct Options { | 
|---|
| 128 | grps: Vec<OptGroup>, | 
|---|
| 129 | parsing_style: ParsingStyle, | 
|---|
| 130 | long_only: bool, | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | impl Default for Options { | 
|---|
| 134 | fn default() -> Self { | 
|---|
| 135 | Self::new() | 
|---|
| 136 | } | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 139 | impl Options { | 
|---|
| 140 | /// Create a blank set of options. | 
|---|
| 141 | pub fn new() -> Options { | 
|---|
| 142 | Options { | 
|---|
| 143 | grps: Vec::new(), | 
|---|
| 144 | parsing_style: ParsingStyle::FloatingFrees, | 
|---|
| 145 | long_only: false, | 
|---|
| 146 | } | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | /// Set the parsing style. | 
|---|
| 150 | pub fn parsing_style(&mut self, style: ParsingStyle) -> &mut Options { | 
|---|
| 151 | self.parsing_style = style; | 
|---|
| 152 | self | 
|---|
| 153 | } | 
|---|
| 154 |  | 
|---|
| 155 | /// Set or clear "long options only" mode. | 
|---|
| 156 | /// | 
|---|
| 157 | /// In "long options only" mode, short options cannot be clustered | 
|---|
| 158 | /// together, and long options can be given with either a single | 
|---|
| 159 | /// "-" or the customary "--".  This mode also changes the meaning | 
|---|
| 160 | /// of "-a=b"; in the ordinary mode this will parse a short option | 
|---|
| 161 | /// "-a" with argument "=b"; whereas in long-options-only mode the | 
|---|
| 162 | /// argument will be simply "b". | 
|---|
| 163 | pub fn long_only(&mut self, long_only: bool) -> &mut Options { | 
|---|
| 164 | self.long_only = long_only; | 
|---|
| 165 | self | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | /// Create a generic option group, stating all parameters explicitly. | 
|---|
| 169 | pub fn opt( | 
|---|
| 170 | &mut self, | 
|---|
| 171 | short_name: &str, | 
|---|
| 172 | long_name: &str, | 
|---|
| 173 | desc: &str, | 
|---|
| 174 | hint: &str, | 
|---|
| 175 | hasarg: HasArg, | 
|---|
| 176 | occur: Occur, | 
|---|
| 177 | ) -> &mut Options { | 
|---|
| 178 | validate_names(short_name, long_name); | 
|---|
| 179 | self.grps.push(OptGroup { | 
|---|
| 180 | short_name: short_name.to_string(), | 
|---|
| 181 | long_name: long_name.to_string(), | 
|---|
| 182 | hint: hint.to_string(), | 
|---|
| 183 | desc: desc.to_string(), | 
|---|
| 184 | hasarg, | 
|---|
| 185 | occur, | 
|---|
| 186 | }); | 
|---|
| 187 | self | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | /// Create a long option that is optional and does not take an argument. | 
|---|
| 191 | /// | 
|---|
| 192 | /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none | 
|---|
| 193 | /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none | 
|---|
| 194 | /// * `desc` - Description for usage help | 
|---|
| 195 | pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut Options { | 
|---|
| 196 | validate_names(short_name, long_name); | 
|---|
| 197 | self.grps.push(OptGroup { | 
|---|
| 198 | short_name: short_name.to_string(), | 
|---|
| 199 | long_name: long_name.to_string(), | 
|---|
| 200 | hint: "".to_string(), | 
|---|
| 201 | desc: desc.to_string(), | 
|---|
| 202 | hasarg: No, | 
|---|
| 203 | occur: Optional, | 
|---|
| 204 | }); | 
|---|
| 205 | self | 
|---|
| 206 | } | 
|---|
| 207 |  | 
|---|
| 208 | /// Create a long option that can occur more than once and does not | 
|---|
| 209 | /// take an argument. | 
|---|
| 210 | /// | 
|---|
| 211 | /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none | 
|---|
| 212 | /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none | 
|---|
| 213 | /// * `desc` - Description for usage help | 
|---|
| 214 | pub fn optflagmulti(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut Options { | 
|---|
| 215 | validate_names(short_name, long_name); | 
|---|
| 216 | self.grps.push(OptGroup { | 
|---|
| 217 | short_name: short_name.to_string(), | 
|---|
| 218 | long_name: long_name.to_string(), | 
|---|
| 219 | hint: "".to_string(), | 
|---|
| 220 | desc: desc.to_string(), | 
|---|
| 221 | hasarg: No, | 
|---|
| 222 | occur: Multi, | 
|---|
| 223 | }); | 
|---|
| 224 | self | 
|---|
| 225 | } | 
|---|
| 226 |  | 
|---|
| 227 | /// Create a long option that is optional and takes an optional argument. | 
|---|
| 228 | /// | 
|---|
| 229 | /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none | 
|---|
| 230 | /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none | 
|---|
| 231 | /// * `desc` - Description for usage help | 
|---|
| 232 | /// * `hint` - Hint that is used in place of the argument in the usage help, | 
|---|
| 233 | ///   e.g. `"FILE"` for a `-o FILE` option | 
|---|
| 234 | pub fn optflagopt( | 
|---|
| 235 | &mut self, | 
|---|
| 236 | short_name: &str, | 
|---|
| 237 | long_name: &str, | 
|---|
| 238 | desc: &str, | 
|---|
| 239 | hint: &str, | 
|---|
| 240 | ) -> &mut Options { | 
|---|
| 241 | validate_names(short_name, long_name); | 
|---|
| 242 | self.grps.push(OptGroup { | 
|---|
| 243 | short_name: short_name.to_string(), | 
|---|
| 244 | long_name: long_name.to_string(), | 
|---|
| 245 | hint: hint.to_string(), | 
|---|
| 246 | desc: desc.to_string(), | 
|---|
| 247 | hasarg: Maybe, | 
|---|
| 248 | occur: Optional, | 
|---|
| 249 | }); | 
|---|
| 250 | self | 
|---|
| 251 | } | 
|---|
| 252 |  | 
|---|
| 253 | /// Create a long option that is optional, takes an argument, and may occur | 
|---|
| 254 | /// multiple times. | 
|---|
| 255 | /// | 
|---|
| 256 | /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none | 
|---|
| 257 | /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none | 
|---|
| 258 | /// * `desc` - Description for usage help | 
|---|
| 259 | /// * `hint` - Hint that is used in place of the argument in the usage help, | 
|---|
| 260 | ///   e.g. `"FILE"` for a `-o FILE` option | 
|---|
| 261 | pub fn optmulti( | 
|---|
| 262 | &mut self, | 
|---|
| 263 | short_name: &str, | 
|---|
| 264 | long_name: &str, | 
|---|
| 265 | desc: &str, | 
|---|
| 266 | hint: &str, | 
|---|
| 267 | ) -> &mut Options { | 
|---|
| 268 | validate_names(short_name, long_name); | 
|---|
| 269 | self.grps.push(OptGroup { | 
|---|
| 270 | short_name: short_name.to_string(), | 
|---|
| 271 | long_name: long_name.to_string(), | 
|---|
| 272 | hint: hint.to_string(), | 
|---|
| 273 | desc: desc.to_string(), | 
|---|
| 274 | hasarg: Yes, | 
|---|
| 275 | occur: Multi, | 
|---|
| 276 | }); | 
|---|
| 277 | self | 
|---|
| 278 | } | 
|---|
| 279 |  | 
|---|
| 280 | /// Create a long option that is optional and takes an argument. | 
|---|
| 281 | /// | 
|---|
| 282 | /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none | 
|---|
| 283 | /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none | 
|---|
| 284 | /// * `desc` - Description for usage help | 
|---|
| 285 | /// * `hint` - Hint that is used in place of the argument in the usage help, | 
|---|
| 286 | ///   e.g. `"FILE"` for a `-o FILE` option | 
|---|
| 287 | pub fn optopt( | 
|---|
| 288 | &mut self, | 
|---|
| 289 | short_name: &str, | 
|---|
| 290 | long_name: &str, | 
|---|
| 291 | desc: &str, | 
|---|
| 292 | hint: &str, | 
|---|
| 293 | ) -> &mut Options { | 
|---|
| 294 | validate_names(short_name, long_name); | 
|---|
| 295 | self.grps.push(OptGroup { | 
|---|
| 296 | short_name: short_name.to_string(), | 
|---|
| 297 | long_name: long_name.to_string(), | 
|---|
| 298 | hint: hint.to_string(), | 
|---|
| 299 | desc: desc.to_string(), | 
|---|
| 300 | hasarg: Yes, | 
|---|
| 301 | occur: Optional, | 
|---|
| 302 | }); | 
|---|
| 303 | self | 
|---|
| 304 | } | 
|---|
| 305 |  | 
|---|
| 306 | /// Create a long option that is required and takes an argument. | 
|---|
| 307 | /// | 
|---|
| 308 | /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none | 
|---|
| 309 | /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none | 
|---|
| 310 | /// * `desc` - Description for usage help | 
|---|
| 311 | /// * `hint` - Hint that is used in place of the argument in the usage help, | 
|---|
| 312 | ///   e.g. `"FILE"` for a `-o FILE` option | 
|---|
| 313 | pub fn reqopt( | 
|---|
| 314 | &mut self, | 
|---|
| 315 | short_name: &str, | 
|---|
| 316 | long_name: &str, | 
|---|
| 317 | desc: &str, | 
|---|
| 318 | hint: &str, | 
|---|
| 319 | ) -> &mut Options { | 
|---|
| 320 | validate_names(short_name, long_name); | 
|---|
| 321 | self.grps.push(OptGroup { | 
|---|
| 322 | short_name: short_name.to_string(), | 
|---|
| 323 | long_name: long_name.to_string(), | 
|---|
| 324 | hint: hint.to_string(), | 
|---|
| 325 | desc: desc.to_string(), | 
|---|
| 326 | hasarg: Yes, | 
|---|
| 327 | occur: Req, | 
|---|
| 328 | }); | 
|---|
| 329 | self | 
|---|
| 330 | } | 
|---|
| 331 |  | 
|---|
| 332 | /// Parse command line arguments according to the provided options. | 
|---|
| 333 | /// | 
|---|
| 334 | /// On success returns `Ok(Matches)`. Use methods such as `opt_present` | 
|---|
| 335 | /// `opt_str`, etc. to interrogate results. | 
|---|
| 336 | /// # Panics | 
|---|
| 337 | /// | 
|---|
| 338 | /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` | 
|---|
| 339 | /// to display information about it. | 
|---|
| 340 | pub fn parse<C: IntoIterator>(&self, args: C) -> Result | 
|---|
| 341 | where | 
|---|
| 342 | C::Item: AsRef<OsStr>, | 
|---|
| 343 | { | 
|---|
| 344 | let opts: Vec<Opt> = self.grps.iter().map(|x| x.long_to_short()).collect(); | 
|---|
| 345 |  | 
|---|
| 346 | let mut vals = (0..opts.len()) | 
|---|
| 347 | .map(|_| Vec::new()) | 
|---|
| 348 | .collect::<Vec<Vec<(usize, Optval)>>>(); | 
|---|
| 349 | let mut free: Vec<String> = Vec::new(); | 
|---|
| 350 | let args = args | 
|---|
| 351 | .into_iter() | 
|---|
| 352 | .map(|i| { | 
|---|
| 353 | i.as_ref() | 
|---|
| 354 | .to_str() | 
|---|
| 355 | .ok_or_else(|| Fail::UnrecognizedOption(format!( "{:?} ", i.as_ref()))) | 
|---|
| 356 | .map(|s| s.to_owned()) | 
|---|
| 357 | }) | 
|---|
| 358 | .collect::<::std::result::Result<Vec<_>, _>>()?; | 
|---|
| 359 | let mut args = args.into_iter().peekable(); | 
|---|
| 360 | let mut arg_pos = 0; | 
|---|
| 361 | while let Some(cur) = args.next() { | 
|---|
| 362 | if !is_arg(&cur) { | 
|---|
| 363 | free.push(cur); | 
|---|
| 364 | match self.parsing_style { | 
|---|
| 365 | ParsingStyle::FloatingFrees => {} | 
|---|
| 366 | ParsingStyle::StopAtFirstFree => { | 
|---|
| 367 | free.extend(args); | 
|---|
| 368 | break; | 
|---|
| 369 | } | 
|---|
| 370 | } | 
|---|
| 371 | } else if cur == "--"{ | 
|---|
| 372 | free.extend(args); | 
|---|
| 373 | break; | 
|---|
| 374 | } else { | 
|---|
| 375 | let mut names; | 
|---|
| 376 | let mut i_arg = None; | 
|---|
| 377 | let mut was_long = true; | 
|---|
| 378 | if cur.as_bytes()[1] == b'-'|| self.long_only { | 
|---|
| 379 | let tail = if cur.as_bytes()[1] == b'-'{ | 
|---|
| 380 | &cur[2..] | 
|---|
| 381 | } else { | 
|---|
| 382 | assert!(self.long_only); | 
|---|
| 383 | &cur[1..] | 
|---|
| 384 | }; | 
|---|
| 385 | let mut parts = tail.splitn(2, '='); | 
|---|
| 386 | names = vec![Name::from_str(parts.next().unwrap())]; | 
|---|
| 387 | if let Some(rest) = parts.next() { | 
|---|
| 388 | i_arg = Some(rest.to_string()); | 
|---|
| 389 | } | 
|---|
| 390 | } else { | 
|---|
| 391 | was_long = false; | 
|---|
| 392 | names = Vec::new(); | 
|---|
| 393 | for (j, ch) in cur.char_indices().skip(1) { | 
|---|
| 394 | let opt = Short(ch); | 
|---|
| 395 |  | 
|---|
| 396 | /* In a series of potential options (eg. -aheJ), if we | 
|---|
| 397 | see one which takes an argument, we assume all | 
|---|
| 398 | subsequent characters make up the argument. This | 
|---|
| 399 | allows options such as -L/usr/local/lib/foo to be | 
|---|
| 400 | interpreted correctly | 
|---|
| 401 | */ | 
|---|
| 402 |  | 
|---|
| 403 | let opt_id = match find_opt(&opts, &opt) { | 
|---|
| 404 | Some(id) => id, | 
|---|
| 405 | None => return Err(UnrecognizedOption(opt.to_string())), | 
|---|
| 406 | }; | 
|---|
| 407 |  | 
|---|
| 408 | names.push(opt); | 
|---|
| 409 |  | 
|---|
| 410 | let arg_follows = match opts[opt_id].hasarg { | 
|---|
| 411 | Yes | Maybe => true, | 
|---|
| 412 | No => false, | 
|---|
| 413 | }; | 
|---|
| 414 |  | 
|---|
| 415 | if arg_follows { | 
|---|
| 416 | let next = j + ch.len_utf8(); | 
|---|
| 417 | if next < cur.len() { | 
|---|
| 418 | i_arg = Some(cur[next..].to_string()); | 
|---|
| 419 | break; | 
|---|
| 420 | } | 
|---|
| 421 | } | 
|---|
| 422 | } | 
|---|
| 423 | } | 
|---|
| 424 | let mut name_pos = 0; | 
|---|
| 425 | for nm in names.iter() { | 
|---|
| 426 | name_pos += 1; | 
|---|
| 427 | let optid = match find_opt(&opts, &nm) { | 
|---|
| 428 | Some(id) => id, | 
|---|
| 429 | None => return Err(UnrecognizedOption(nm.to_string())), | 
|---|
| 430 | }; | 
|---|
| 431 | match opts[optid].hasarg { | 
|---|
| 432 | No => { | 
|---|
| 433 | if name_pos == names.len() && i_arg.is_some() { | 
|---|
| 434 | return Err(UnexpectedArgument(nm.to_string())); | 
|---|
| 435 | } | 
|---|
| 436 | vals[optid].push((arg_pos, Given)); | 
|---|
| 437 | } | 
|---|
| 438 | Maybe => { | 
|---|
| 439 | // Note that here we do not handle `--arg value`. | 
|---|
| 440 | // This matches GNU getopt behavior; but also | 
|---|
| 441 | // makes sense, because if this were accepted, | 
|---|
| 442 | // then users could only write a "Maybe" long | 
|---|
| 443 | // option at the end of the arguments when | 
|---|
| 444 | // FloatingFrees is in use. | 
|---|
| 445 | if let Some(i_arg) = i_arg.take() { | 
|---|
| 446 | vals[optid].push((arg_pos, Val(i_arg))); | 
|---|
| 447 | } else if was_long | 
|---|
| 448 | || name_pos < names.len() | 
|---|
| 449 | || args.peek().map_or(true, |n| is_arg(&n)) | 
|---|
| 450 | { | 
|---|
| 451 | vals[optid].push((arg_pos, Given)); | 
|---|
| 452 | } else { | 
|---|
| 453 | vals[optid].push((arg_pos, Val(args.next().unwrap()))); | 
|---|
| 454 | } | 
|---|
| 455 | } | 
|---|
| 456 | Yes => { | 
|---|
| 457 | if let Some(i_arg) = i_arg.take() { | 
|---|
| 458 | vals[optid].push((arg_pos, Val(i_arg))); | 
|---|
| 459 | } else if let Some(n) = args.next() { | 
|---|
| 460 | vals[optid].push((arg_pos, Val(n))); | 
|---|
| 461 | } else { | 
|---|
| 462 | return Err(ArgumentMissing(nm.to_string())); | 
|---|
| 463 | } | 
|---|
| 464 | } | 
|---|
| 465 | } | 
|---|
| 466 | } | 
|---|
| 467 | } | 
|---|
| 468 | arg_pos += 1; | 
|---|
| 469 | } | 
|---|
| 470 | debug_assert_eq!(vals.len(), opts.len()); | 
|---|
| 471 | for (vals, opt) in vals.iter().zip(opts.iter()) { | 
|---|
| 472 | if opt.occur == Req && vals.is_empty() { | 
|---|
| 473 | return Err(OptionMissing(opt.name.to_string())); | 
|---|
| 474 | } | 
|---|
| 475 | if opt.occur != Multi && vals.len() > 1 { | 
|---|
| 476 | return Err(OptionDuplicated(opt.name.to_string())); | 
|---|
| 477 | } | 
|---|
| 478 | } | 
|---|
| 479 | Ok(Matches { opts, vals, free }) | 
|---|
| 480 | } | 
|---|
| 481 |  | 
|---|
| 482 | /// Derive a short one-line usage summary from a set of long options. | 
|---|
| 483 | pub fn short_usage(&self, program_name: &str) -> String { | 
|---|
| 484 | let mut line = format!( "Usage: {}  ", program_name); | 
|---|
| 485 | line.push_str( | 
|---|
| 486 | &self | 
|---|
| 487 | .grps | 
|---|
| 488 | .iter() | 
|---|
| 489 | .map(format_option) | 
|---|
| 490 | .collect::<Vec<String>>() | 
|---|
| 491 | .join( " "), | 
|---|
| 492 | ); | 
|---|
| 493 | line | 
|---|
| 494 | } | 
|---|
| 495 |  | 
|---|
| 496 | /// Derive a formatted message from a set of options. | 
|---|
| 497 | pub fn usage(&self, brief: &str) -> String { | 
|---|
| 498 | self.usage_with_format(|opts| { | 
|---|
| 499 | format!( | 
|---|
| 500 | "{}\n\n Options:\n{}\n ", | 
|---|
| 501 | brief, | 
|---|
| 502 | opts.collect::<Vec<String>>().join( "\n ") | 
|---|
| 503 | ) | 
|---|
| 504 | }) | 
|---|
| 505 | } | 
|---|
| 506 |  | 
|---|
| 507 | /// Derive a custom formatted message from a set of options. The formatted options provided to | 
|---|
| 508 | /// a closure as an iterator. | 
|---|
| 509 | pub fn usage_with_format<F: FnMut(&mut dyn Iterator<Item = String>) -> String>( | 
|---|
| 510 | &self, | 
|---|
| 511 | mut formatter: F, | 
|---|
| 512 | ) -> String { | 
|---|
| 513 | formatter(&mut self.usage_items()) | 
|---|
| 514 | } | 
|---|
| 515 |  | 
|---|
| 516 | /// Derive usage items from a set of options. | 
|---|
| 517 | fn usage_items<'a>(&'a self) -> Box<dyn Iterator<Item = String> + 'a> { | 
|---|
| 518 | let desc_sep = format!( "\n{} ", repeat( " ").take(24).collect::<String>()); | 
|---|
| 519 |  | 
|---|
| 520 | let any_short = self.grps.iter().any(|optref| !optref.short_name.is_empty()); | 
|---|
| 521 |  | 
|---|
| 522 | let rows = self.grps.iter().map(move |optref| { | 
|---|
| 523 | let OptGroup { | 
|---|
| 524 | short_name, | 
|---|
| 525 | long_name, | 
|---|
| 526 | hint, | 
|---|
| 527 | desc, | 
|---|
| 528 | hasarg, | 
|---|
| 529 | .. | 
|---|
| 530 | } = (*optref).clone(); | 
|---|
| 531 |  | 
|---|
| 532 | let mut row = "    ".to_string(); | 
|---|
| 533 |  | 
|---|
| 534 | // short option | 
|---|
| 535 | match short_name.width() { | 
|---|
| 536 | 0 => { | 
|---|
| 537 | if any_short { | 
|---|
| 538 | row.push_str( "    "); | 
|---|
| 539 | } | 
|---|
| 540 | } | 
|---|
| 541 | 1 => { | 
|---|
| 542 | row.push( '-'); | 
|---|
| 543 | row.push_str(&short_name); | 
|---|
| 544 | if long_name.width() > 0 { | 
|---|
| 545 | row.push_str( ", "); | 
|---|
| 546 | } else { | 
|---|
| 547 | // Only a single space here, so that any | 
|---|
| 548 | // argument is printed in the correct spot. | 
|---|
| 549 | row.push( ' '); | 
|---|
| 550 | } | 
|---|
| 551 | } | 
|---|
| 552 | // FIXME: refer issue #7. | 
|---|
| 553 | _ => panic!( "the short name should only be 1 ascii char long"), | 
|---|
| 554 | } | 
|---|
| 555 |  | 
|---|
| 556 | // long option | 
|---|
| 557 | match long_name.width() { | 
|---|
| 558 | 0 => {} | 
|---|
| 559 | _ => { | 
|---|
| 560 | row.push_str(if self.long_only { "-"} else { "--"}); | 
|---|
| 561 | row.push_str(&long_name); | 
|---|
| 562 | row.push( ' '); | 
|---|
| 563 | } | 
|---|
| 564 | } | 
|---|
| 565 |  | 
|---|
| 566 | // arg | 
|---|
| 567 | match hasarg { | 
|---|
| 568 | No => {} | 
|---|
| 569 | Yes => row.push_str(&hint), | 
|---|
| 570 | Maybe => { | 
|---|
| 571 | row.push( '['); | 
|---|
| 572 | row.push_str(&hint); | 
|---|
| 573 | row.push( ']'); | 
|---|
| 574 | } | 
|---|
| 575 | } | 
|---|
| 576 |  | 
|---|
| 577 | let rowlen = row.width(); | 
|---|
| 578 | if rowlen < 24 { | 
|---|
| 579 | for _ in 0..24 - rowlen { | 
|---|
| 580 | row.push( ' '); | 
|---|
| 581 | } | 
|---|
| 582 | } else { | 
|---|
| 583 | row.push_str(&desc_sep) | 
|---|
| 584 | } | 
|---|
| 585 |  | 
|---|
| 586 | let desc_rows = each_split_within(&desc, 54); | 
|---|
| 587 | row.push_str(&desc_rows.join(&desc_sep)); | 
|---|
| 588 |  | 
|---|
| 589 | row | 
|---|
| 590 | }); | 
|---|
| 591 |  | 
|---|
| 592 | Box::new(rows) | 
|---|
| 593 | } | 
|---|
| 594 | } | 
|---|
| 595 |  | 
|---|
| 596 | fn validate_names(short_name: &str, long_name: &str) { | 
|---|
| 597 | let len: usize = short_name.len(); | 
|---|
| 598 | assert!( | 
|---|
| 599 | len == 1 || len == 0, | 
|---|
| 600 | "the short_name (first argument) should be a single character, \ | 
|---|
| 601 |          or an empty string for none" | 
|---|
| 602 | ); | 
|---|
| 603 | let len: usize = long_name.len(); | 
|---|
| 604 | assert!( | 
|---|
| 605 | len == 0 || len > 1, | 
|---|
| 606 | "the long_name (second argument) should be longer than a single \ | 
|---|
| 607 |          character, or an empty string for none" | 
|---|
| 608 | ); | 
|---|
| 609 | } | 
|---|
| 610 |  | 
|---|
| 611 | /// What parsing style to use when parsing arguments. | 
|---|
| 612 | #[ derive(Clone, Copy, PartialEq, Eq)] | 
|---|
| 613 | pub enum ParsingStyle { | 
|---|
| 614 | /// Flags and "free" arguments can be freely inter-mixed. | 
|---|
| 615 | FloatingFrees, | 
|---|
| 616 | /// As soon as a "free" argument (i.e. non-flag) is encountered, stop | 
|---|
| 617 | /// considering any remaining arguments as flags. | 
|---|
| 618 | StopAtFirstFree, | 
|---|
| 619 | } | 
|---|
| 620 |  | 
|---|
| 621 | /// Name of an option. Either a string or a single char. | 
|---|
| 622 | #[ derive(Clone, Debug, PartialEq, Eq)] | 
|---|
| 623 | enum Name { | 
|---|
| 624 | /// A string representing the long name of an option. | 
|---|
| 625 | /// For example: "help" | 
|---|
| 626 | Long(String), | 
|---|
| 627 | /// A char representing the short name of an option. | 
|---|
| 628 | /// For example: 'h' | 
|---|
| 629 | Short(char), | 
|---|
| 630 | } | 
|---|
| 631 |  | 
|---|
| 632 | /// Describes whether an option has an argument. | 
|---|
| 633 | #[ derive(Clone, Debug, Copy, PartialEq, Eq)] | 
|---|
| 634 | pub enum HasArg { | 
|---|
| 635 | /// The option requires an argument. | 
|---|
| 636 | Yes, | 
|---|
| 637 | /// The option takes no argument. | 
|---|
| 638 | No, | 
|---|
| 639 | /// The option argument is optional. | 
|---|
| 640 | Maybe, | 
|---|
| 641 | } | 
|---|
| 642 |  | 
|---|
| 643 | /// Describes how often an option may occur. | 
|---|
| 644 | #[ derive(Clone, Debug, Copy, PartialEq, Eq)] | 
|---|
| 645 | pub enum Occur { | 
|---|
| 646 | /// The option occurs once. | 
|---|
| 647 | Req, | 
|---|
| 648 | /// The option occurs at most once. | 
|---|
| 649 | Optional, | 
|---|
| 650 | /// The option occurs zero or more times. | 
|---|
| 651 | Multi, | 
|---|
| 652 | } | 
|---|
| 653 |  | 
|---|
| 654 | /// A description of a possible option. | 
|---|
| 655 | #[ derive(Clone, Debug, PartialEq, Eq)] | 
|---|
| 656 | struct Opt { | 
|---|
| 657 | /// Name of the option | 
|---|
| 658 | name: Name, | 
|---|
| 659 | /// Whether it has an argument | 
|---|
| 660 | hasarg: HasArg, | 
|---|
| 661 | /// How often it can occur | 
|---|
| 662 | occur: Occur, | 
|---|
| 663 | /// Which options it aliases | 
|---|
| 664 | aliases: Vec<Opt>, | 
|---|
| 665 | } | 
|---|
| 666 |  | 
|---|
| 667 | /// One group of options, e.g., both `-h` and `--help`, along with | 
|---|
| 668 | /// their shared description and properties. | 
|---|
| 669 | #[ derive(Clone, PartialEq, Eq)] | 
|---|
| 670 | struct OptGroup { | 
|---|
| 671 | /// Short name of the option, e.g. `h` for a `-h` option | 
|---|
| 672 | short_name: String, | 
|---|
| 673 | /// Long name of the option, e.g. `help` for a `--help` option | 
|---|
| 674 | long_name: String, | 
|---|
| 675 | /// Hint for argument, e.g. `FILE` for a `-o FILE` option | 
|---|
| 676 | hint: String, | 
|---|
| 677 | /// Description for usage help text | 
|---|
| 678 | desc: String, | 
|---|
| 679 | /// Whether option has an argument | 
|---|
| 680 | hasarg: HasArg, | 
|---|
| 681 | /// How often it can occur | 
|---|
| 682 | occur: Occur, | 
|---|
| 683 | } | 
|---|
| 684 |  | 
|---|
| 685 | /// Describes whether an option is given at all or has a value. | 
|---|
| 686 | #[ derive(Clone, Debug, PartialEq, Eq)] | 
|---|
| 687 | enum Optval { | 
|---|
| 688 | Val(String), | 
|---|
| 689 | Given, | 
|---|
| 690 | } | 
|---|
| 691 |  | 
|---|
| 692 | /// The result of checking command line arguments. Contains a vector | 
|---|
| 693 | /// of matches and a vector of free strings. | 
|---|
| 694 | #[ derive(Clone, Debug, PartialEq, Eq)] | 
|---|
| 695 | pub struct Matches { | 
|---|
| 696 | /// Options that matched | 
|---|
| 697 | opts: Vec<Opt>, | 
|---|
| 698 | /// Values of the Options that matched and their positions | 
|---|
| 699 | vals: Vec<Vec<(usize, Optval)>>, | 
|---|
| 700 | /// Free string fragments | 
|---|
| 701 | pub free: Vec<String>, | 
|---|
| 702 | } | 
|---|
| 703 |  | 
|---|
| 704 | /// The type returned when the command line does not conform to the | 
|---|
| 705 | /// expected format. Use the `Debug` implementation to output detailed | 
|---|
| 706 | /// information. | 
|---|
| 707 | #[ derive(Clone, Debug, PartialEq, Eq)] | 
|---|
| 708 | pub enum Fail { | 
|---|
| 709 | /// The option requires an argument but none was passed. | 
|---|
| 710 | ArgumentMissing(String), | 
|---|
| 711 | /// The passed option is not declared among the possible options. | 
|---|
| 712 | UnrecognizedOption(String), | 
|---|
| 713 | /// A required option is not present. | 
|---|
| 714 | OptionMissing(String), | 
|---|
| 715 | /// A single occurrence option is being used multiple times. | 
|---|
| 716 | OptionDuplicated(String), | 
|---|
| 717 | /// There's an argument being passed to a non-argument option. | 
|---|
| 718 | UnexpectedArgument(String), | 
|---|
| 719 | } | 
|---|
| 720 |  | 
|---|
| 721 | impl Error for Fail { | 
|---|
| 722 | fn description(&self) -> &str { | 
|---|
| 723 | match *self { | 
|---|
| 724 | ArgumentMissing(_) => "missing argument", | 
|---|
| 725 | UnrecognizedOption(_) => "unrecognized option", | 
|---|
| 726 | OptionMissing(_) => "missing option", | 
|---|
| 727 | OptionDuplicated(_) => "duplicated option", | 
|---|
| 728 | UnexpectedArgument(_) => "unexpected argument", | 
|---|
| 729 | } | 
|---|
| 730 | } | 
|---|
| 731 | } | 
|---|
| 732 |  | 
|---|
| 733 | /// The result of parsing a command line with a set of options. | 
|---|
| 734 | pub type Result = result::Result<Matches, Fail>; | 
|---|
| 735 |  | 
|---|
| 736 | impl Name { | 
|---|
| 737 | fn from_str(nm: &str) -> Name { | 
|---|
| 738 | if nm.len() == 1 { | 
|---|
| 739 | Short(nm.as_bytes()[0] as char) | 
|---|
| 740 | } else { | 
|---|
| 741 | Long(nm.to_string()) | 
|---|
| 742 | } | 
|---|
| 743 | } | 
|---|
| 744 |  | 
|---|
| 745 | fn to_string(&self) -> String { | 
|---|
| 746 | match *self { | 
|---|
| 747 | Short(ch: char) => ch.to_string(), | 
|---|
| 748 | Long(ref s: &String) => s.to_string(), | 
|---|
| 749 | } | 
|---|
| 750 | } | 
|---|
| 751 | } | 
|---|
| 752 |  | 
|---|
| 753 | impl OptGroup { | 
|---|
| 754 | /// Translate OptGroup into Opt. | 
|---|
| 755 | /// (Both short and long names correspond to different Opts). | 
|---|
| 756 | fn long_to_short(&self) -> Opt { | 
|---|
| 757 | let OptGroup { | 
|---|
| 758 | short_name, | 
|---|
| 759 | long_name, | 
|---|
| 760 | hasarg, | 
|---|
| 761 | occur, | 
|---|
| 762 | .. | 
|---|
| 763 | } = (*self).clone(); | 
|---|
| 764 |  | 
|---|
| 765 | match (short_name.len(), long_name.len()) { | 
|---|
| 766 | (0, 0) => panic!( "this long-format option was given no name"), | 
|---|
| 767 | (0, _) => Opt { | 
|---|
| 768 | name: Long(long_name), | 
|---|
| 769 | hasarg, | 
|---|
| 770 | occur, | 
|---|
| 771 | aliases: Vec::new(), | 
|---|
| 772 | }, | 
|---|
| 773 | (1, 0) => Opt { | 
|---|
| 774 | name: Short(short_name.as_bytes()[0] as char), | 
|---|
| 775 | hasarg, | 
|---|
| 776 | occur, | 
|---|
| 777 | aliases: Vec::new(), | 
|---|
| 778 | }, | 
|---|
| 779 | (1, _) => Opt { | 
|---|
| 780 | name: Long(long_name), | 
|---|
| 781 | hasarg, | 
|---|
| 782 | occur, | 
|---|
| 783 | aliases: vec![Opt { | 
|---|
| 784 | name: Short(short_name.as_bytes()[0] as char), | 
|---|
| 785 | hasarg: hasarg, | 
|---|
| 786 | occur: occur, | 
|---|
| 787 | aliases: Vec::new(), | 
|---|
| 788 | }], | 
|---|
| 789 | }, | 
|---|
| 790 | (_, _) => panic!( "something is wrong with the long-form opt"), | 
|---|
| 791 | } | 
|---|
| 792 | } | 
|---|
| 793 | } | 
|---|
| 794 |  | 
|---|
| 795 | impl Matches { | 
|---|
| 796 | fn opt_vals(&self, nm: &str) -> Vec<(usize, Optval)> { | 
|---|
| 797 | match find_opt(&self.opts, &Name::from_str(nm)) { | 
|---|
| 798 | Some(id) => self.vals[id].clone(), | 
|---|
| 799 | None => panic!( "No option '{} ' defined", nm), | 
|---|
| 800 | } | 
|---|
| 801 | } | 
|---|
| 802 |  | 
|---|
| 803 | fn opt_val(&self, nm: &str) -> Option<Optval> { | 
|---|
| 804 | self.opt_vals(nm).into_iter().map(|(_, o)| o).next() | 
|---|
| 805 | } | 
|---|
| 806 | /// Returns true if an option was defined | 
|---|
| 807 | pub fn opt_defined(&self, nm: &str) -> bool { | 
|---|
| 808 | find_opt(&self.opts, &Name::from_str(nm)).is_some() | 
|---|
| 809 | } | 
|---|
| 810 |  | 
|---|
| 811 | /// Returns true if an option was matched. | 
|---|
| 812 | pub fn opt_present(&self, nm: &str) -> bool { | 
|---|
| 813 | !self.opt_vals(nm).is_empty() | 
|---|
| 814 | } | 
|---|
| 815 |  | 
|---|
| 816 | /// Returns the number of times an option was matched. | 
|---|
| 817 | pub fn opt_count(&self, nm: &str) -> usize { | 
|---|
| 818 | self.opt_vals(nm).len() | 
|---|
| 819 | } | 
|---|
| 820 |  | 
|---|
| 821 | /// Returns a vector of all the positions in which an option was matched. | 
|---|
| 822 | pub fn opt_positions(&self, nm: &str) -> Vec<usize> { | 
|---|
| 823 | self.opt_vals(nm).into_iter().map(|(pos, _)| pos).collect() | 
|---|
| 824 | } | 
|---|
| 825 |  | 
|---|
| 826 | /// Returns true if any of several options were matched. | 
|---|
| 827 | pub fn opts_present(&self, names: &[String]) -> bool { | 
|---|
| 828 | names | 
|---|
| 829 | .iter() | 
|---|
| 830 | .any(|nm| match find_opt(&self.opts, &Name::from_str(&nm)) { | 
|---|
| 831 | Some(id) if !self.vals[id].is_empty() => true, | 
|---|
| 832 | _ => false, | 
|---|
| 833 | }) | 
|---|
| 834 | } | 
|---|
| 835 |  | 
|---|
| 836 | /// Returns the string argument supplied to one of several matching options or `None`. | 
|---|
| 837 | pub fn opts_str(&self, names: &[String]) -> Option<String> { | 
|---|
| 838 | names | 
|---|
| 839 | .iter() | 
|---|
| 840 | .filter_map(|nm| match self.opt_val(&nm) { | 
|---|
| 841 | Some(Val(s)) => Some(s), | 
|---|
| 842 | _ => None, | 
|---|
| 843 | }) | 
|---|
| 844 | .next() | 
|---|
| 845 | } | 
|---|
| 846 |  | 
|---|
| 847 | /// Returns a vector of the arguments provided to all matches of the given | 
|---|
| 848 | /// option. | 
|---|
| 849 | /// | 
|---|
| 850 | /// Used when an option accepts multiple values. | 
|---|
| 851 | pub fn opt_strs(&self, nm: &str) -> Vec<String> { | 
|---|
| 852 | self.opt_vals(nm) | 
|---|
| 853 | .into_iter() | 
|---|
| 854 | .filter_map(|(_, v)| match v { | 
|---|
| 855 | Val(s) => Some(s), | 
|---|
| 856 | _ => None, | 
|---|
| 857 | }) | 
|---|
| 858 | .collect() | 
|---|
| 859 | } | 
|---|
| 860 |  | 
|---|
| 861 | /// Returns a vector of the arguments provided to all matches of the given | 
|---|
| 862 | /// option, together with their positions. | 
|---|
| 863 | /// | 
|---|
| 864 | /// Used when an option accepts multiple values. | 
|---|
| 865 | pub fn opt_strs_pos(&self, nm: &str) -> Vec<(usize, String)> { | 
|---|
| 866 | self.opt_vals(nm) | 
|---|
| 867 | .into_iter() | 
|---|
| 868 | .filter_map(|(p, v)| match v { | 
|---|
| 869 | Val(s) => Some((p, s)), | 
|---|
| 870 | _ => None, | 
|---|
| 871 | }) | 
|---|
| 872 | .collect() | 
|---|
| 873 | } | 
|---|
| 874 |  | 
|---|
| 875 | /// Returns the string argument supplied to a matching option or `None`. | 
|---|
| 876 | pub fn opt_str(&self, nm: &str) -> Option<String> { | 
|---|
| 877 | match self.opt_val(nm) { | 
|---|
| 878 | Some(Val(s)) => Some(s), | 
|---|
| 879 | _ => None, | 
|---|
| 880 | } | 
|---|
| 881 | } | 
|---|
| 882 |  | 
|---|
| 883 | /// Returns the matching string, a default, or `None`. | 
|---|
| 884 | /// | 
|---|
| 885 | /// Returns `None` if the option was not present, `def` if the option was | 
|---|
| 886 | /// present but no argument was provided, and the argument if the option was | 
|---|
| 887 | /// present and an argument was provided. | 
|---|
| 888 | pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> { | 
|---|
| 889 | match self.opt_val(nm) { | 
|---|
| 890 | Some(Val(s)) => Some(s), | 
|---|
| 891 | Some(_) => Some(def.to_string()), | 
|---|
| 892 | None => None, | 
|---|
| 893 | } | 
|---|
| 894 | } | 
|---|
| 895 |  | 
|---|
| 896 | /// Returns some matching value or `None`. | 
|---|
| 897 | /// | 
|---|
| 898 | /// Similar to opt_str, also converts matching argument using FromStr. | 
|---|
| 899 | pub fn opt_get<T>(&self, nm: &str) -> result::Result<Option<T>, T::Err> | 
|---|
| 900 | where | 
|---|
| 901 | T: FromStr, | 
|---|
| 902 | { | 
|---|
| 903 | match self.opt_val(nm) { | 
|---|
| 904 | Some(Val(s)) => Ok(Some(s.parse()?)), | 
|---|
| 905 | Some(Given) => Ok(None), | 
|---|
| 906 | None => Ok(None), | 
|---|
| 907 | } | 
|---|
| 908 | } | 
|---|
| 909 |  | 
|---|
| 910 | /// Returns a matching value or default. | 
|---|
| 911 | /// | 
|---|
| 912 | /// Similar to opt_default, except the two differences. | 
|---|
| 913 | /// Instead of returning None when argument was not present, return `def`. | 
|---|
| 914 | /// Instead of returning &str return type T, parsed using str::parse(). | 
|---|
| 915 | pub fn opt_get_default<T>(&self, nm: &str, def: T) -> result::Result<T, T::Err> | 
|---|
| 916 | where | 
|---|
| 917 | T: FromStr, | 
|---|
| 918 | { | 
|---|
| 919 | match self.opt_val(nm) { | 
|---|
| 920 | Some(Val(s)) => s.parse(), | 
|---|
| 921 | Some(Given) => Ok(def), | 
|---|
| 922 | None => Ok(def), | 
|---|
| 923 | } | 
|---|
| 924 | } | 
|---|
| 925 | } | 
|---|
| 926 |  | 
|---|
| 927 | fn is_arg(arg: &str) -> bool { | 
|---|
| 928 | arg.as_bytes().get(index:0) == Some(& b'-') && arg.len() > 1 | 
|---|
| 929 | } | 
|---|
| 930 |  | 
|---|
| 931 | fn find_opt(opts: &[Opt], nm: &Name) -> Option<usize> { | 
|---|
| 932 | // Search main options. | 
|---|
| 933 | let pos: Option = opts.iter().position(|opt: &Opt| &opt.name == nm); | 
|---|
| 934 | if pos.is_some() { | 
|---|
| 935 | return pos; | 
|---|
| 936 | } | 
|---|
| 937 |  | 
|---|
| 938 | // Search in aliases. | 
|---|
| 939 | for candidate: &Opt in opts.iter() { | 
|---|
| 940 | if candidate.aliases.iter().any(|opt: &Opt| &opt.name == nm) { | 
|---|
| 941 | return opts.iter().position(|opt: &Opt| opt.name == candidate.name); | 
|---|
| 942 | } | 
|---|
| 943 | } | 
|---|
| 944 |  | 
|---|
| 945 | None | 
|---|
| 946 | } | 
|---|
| 947 |  | 
|---|
| 948 | impl fmt::Display for Fail { | 
|---|
| 949 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
|---|
| 950 | match *self { | 
|---|
| 951 | ArgumentMissing(ref nm: &String) => write!(f, "Argument to option '{} ' missing", *nm), | 
|---|
| 952 | UnrecognizedOption(ref nm: &String) => write!(f, "Unrecognized option: '{} '", *nm), | 
|---|
| 953 | OptionMissing(ref nm: &String) => write!(f, "Required option '{} ' missing", *nm), | 
|---|
| 954 | OptionDuplicated(ref nm: &String) => write!(f, "Option '{} ' given more than once", *nm), | 
|---|
| 955 | UnexpectedArgument(ref nm: &String) => write!(f, "Option '{} ' does not take an argument", *nm), | 
|---|
| 956 | } | 
|---|
| 957 | } | 
|---|
| 958 | } | 
|---|
| 959 |  | 
|---|
| 960 | fn format_option(opt: &OptGroup) -> String { | 
|---|
| 961 | let mut line = String::new(); | 
|---|
| 962 |  | 
|---|
| 963 | if opt.occur != Req { | 
|---|
| 964 | line.push( '['); | 
|---|
| 965 | } | 
|---|
| 966 |  | 
|---|
| 967 | // Use short_name if possible, but fall back to long_name. | 
|---|
| 968 | if !opt.short_name.is_empty() { | 
|---|
| 969 | line.push( '-'); | 
|---|
| 970 | line.push_str(&opt.short_name); | 
|---|
| 971 | } else { | 
|---|
| 972 | line.push_str( "--"); | 
|---|
| 973 | line.push_str(&opt.long_name); | 
|---|
| 974 | } | 
|---|
| 975 |  | 
|---|
| 976 | if opt.hasarg != No { | 
|---|
| 977 | line.push( ' '); | 
|---|
| 978 | if opt.hasarg == Maybe { | 
|---|
| 979 | line.push( '['); | 
|---|
| 980 | } | 
|---|
| 981 | line.push_str(&opt.hint); | 
|---|
| 982 | if opt.hasarg == Maybe { | 
|---|
| 983 | line.push( ']'); | 
|---|
| 984 | } | 
|---|
| 985 | } | 
|---|
| 986 |  | 
|---|
| 987 | if opt.occur != Req { | 
|---|
| 988 | line.push( ']'); | 
|---|
| 989 | } | 
|---|
| 990 | if opt.occur == Multi { | 
|---|
| 991 | line.push_str( ".."); | 
|---|
| 992 | } | 
|---|
| 993 |  | 
|---|
| 994 | line | 
|---|
| 995 | } | 
|---|
| 996 |  | 
|---|
| 997 | /// Splits a string into substrings with possibly internal whitespace, | 
|---|
| 998 | /// each of them at most `lim` bytes long, if possible. The substrings | 
|---|
| 999 | /// have leading and trailing whitespace removed, and are only cut at | 
|---|
| 1000 | /// whitespace boundaries. | 
|---|
| 1001 | fn each_split_within(desc: &str, lim: usize) -> Vec<String> { | 
|---|
| 1002 | let mut rows = Vec::new(); | 
|---|
| 1003 | for line in desc.trim().lines() { | 
|---|
| 1004 | let line_chars = line.chars().chain(Some( ' ')); | 
|---|
| 1005 | let words = line_chars | 
|---|
| 1006 | .fold((Vec::new(), 0, 0), |(mut words, a, z), c| { | 
|---|
| 1007 | let idx = z + c.len_utf8(); // Get the current byte offset | 
|---|
| 1008 |  | 
|---|
| 1009 | // If the char is whitespace, advance the word start and maybe push a word | 
|---|
| 1010 | if c.is_whitespace() { | 
|---|
| 1011 | if a != z { | 
|---|
| 1012 | words.push(&line[a..z]); | 
|---|
| 1013 | } | 
|---|
| 1014 | (words, idx, idx) | 
|---|
| 1015 | } | 
|---|
| 1016 | // If the char is not whitespace, continue, retaining the current | 
|---|
| 1017 | else { | 
|---|
| 1018 | (words, a, idx) | 
|---|
| 1019 | } | 
|---|
| 1020 | }) | 
|---|
| 1021 | .0; | 
|---|
| 1022 |  | 
|---|
| 1023 | let mut row = String::new(); | 
|---|
| 1024 | for word in words.iter() { | 
|---|
| 1025 | let sep = if !row.is_empty() { Some( " ") } else { None }; | 
|---|
| 1026 | let width = row.width() + word.width() + sep.map(UnicodeWidthStr::width).unwrap_or(0); | 
|---|
| 1027 |  | 
|---|
| 1028 | if width <= lim { | 
|---|
| 1029 | if let Some(sep) = sep { | 
|---|
| 1030 | row.push_str(sep) | 
|---|
| 1031 | } | 
|---|
| 1032 | row.push_str(word); | 
|---|
| 1033 | continue; | 
|---|
| 1034 | } | 
|---|
| 1035 | if !row.is_empty() { | 
|---|
| 1036 | rows.push(row.clone()); | 
|---|
| 1037 | row.clear(); | 
|---|
| 1038 | } | 
|---|
| 1039 | row.push_str(word); | 
|---|
| 1040 | } | 
|---|
| 1041 | if !row.is_empty() { | 
|---|
| 1042 | rows.push(row); | 
|---|
| 1043 | } | 
|---|
| 1044 | } | 
|---|
| 1045 | rows | 
|---|
| 1046 | } | 
|---|
| 1047 |  | 
|---|