(index<- )        ./libgetopts/lib.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
    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  //! Simple getopt alternative.
   12  //!
   13  //! Construct a vector of options, either by using `reqopt`, `optopt`, and `optflag`
   14  //! or by building them from components yourself, and pass them to `getopts`,
   15  //! along with a vector of actual arguments (not including `argv[0]`). You'll
   16  //! either get a failure code back, or a match. You'll have to verify whether
   17  //! the amount of 'free' arguments in the match is what you expect. Use `opt_*`
   18  //! accessors to get argument values out of the matches object.
   19  //!
   20  //! Single-character options are expected to appear on the command line with a
   21  //! single preceding dash; multiple-character options are expected to be
   22  //! proceeded by two dashes. Options that expect an argument accept their
   23  //! argument following either a space or an equals sign. Single-character
   24  //! options don't require the space.
   25  //!
   26  //! # Example
   27  //!
   28  //! The following example shows simple command line parsing for an application
   29  //! that requires an input file to be specified, accepts an optional output
   30  //! file name following `-o`, and accepts both `-h` and `--help` as optional flags.
   31  //!
   32  //! ~~~{.rust}
   33  //! extern crate getopts;
   34  //! use getopts::{optopt,optflag,getopts,OptGroup};
   35  //! use std::os;
   36  //!
   37  //! fn do_work(inp: &str, out: Option<~str>) {
   38  //!     println!("{}", inp);
   39  //!     match out {
   40  //!         Some(x) => println!("{}", x),
   41  //!         None => println!("No Output"),
   42  //!     }
   43  //! }
   44  //!
   45  //! fn print_usage(program: &str, _opts: &[OptGroup]) {
   46  //!     println!("Usage: {} [options]", program);
   47  //!     println!("-o\t\tOutput");
   48  //!     println!("-h --help\tUsage");
   49  //! }
   50  //!
   51  //! fn main() {
   52  //!     let args = os::args();
   53  //!
   54  //!     let program = args.get(0).clone();
   55  //!
   56  //!     let opts = [
   57  //!         optopt("o", "", "set output file name", "NAME"),
   58  //!         optflag("h", "help", "print this help menu")
   59  //!     ];
   60  //!     let matches = match getopts(args.tail(), opts) {
   61  //!         Ok(m) => { m }
   62  //!         Err(f) => { fail!(f.to_err_msg()) }
   63  //!     };
   64  //!     if matches.opt_present("h") {
   65  //!         print_usage(program, opts);
   66  //!         return;
   67  //!     }
   68  //!     let output = matches.opt_str("o");
   69  //!     let input: &str = if !matches.free.is_empty() {
   70  //!         (*matches.free.get(0)).clone()
   71  //!     } else {
   72  //!         print_usage(program, opts);
   73  //!         return;
   74  //!     };
   75  //!     do_work(input, output);
   76  //! }
   77  //! ~~~
   78  
   79  #![crate_id = "getopts#0.11-pre"]
   80  #![crate_type = "rlib"]
   81  #![crate_type = "dylib"]
   82  #![license = "MIT/ASL2"]
   83  #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
   84         html_favicon_url = "http://www.rust-lang.org/favicon.ico",
   85         html_root_url = "http://static.rust-lang.org/doc/master")]
   86  #![feature(globs, phase)]
   87  #![deny(missing_doc)]
   88  #![deny(deprecated_owned_vector)]
   89  
   90  #[cfg(test)] #[phase(syntax, link)] extern crate log;
   91  
   92  use std::cmp::Eq;
   93  use std::result::{Err, Ok};
   94  use std::result;
   95  use std::strbuf::StrBuf;
   96  
   97  /// Name of an option. Either a string or a single char.
   98  #[deriving(Clone, Eq)]
   99  pub enum Name {
  100      /// A string representing the long name of an option.
  101      /// For example: "help"
  102      Long(~str),
  103      /// A char representing the short name of an option.
  104      /// For example: 'h'
  105      Short(char),
  106  }
  107  
  108  /// Describes whether an option has an argument.
  109  #[deriving(Clone, Eq)]
  110  pub enum HasArg {
  111      /// The option requires an argument.
  112      Yes,
  113      /// The option is just a flag, therefore no argument.
  114      No,
  115      /// The option argument is optional and it could or not exist.
  116      Maybe,
  117  }
  118  
  119  /// Describes how often an option may occur.
  120  #[deriving(Clone, Eq)]
  121  pub enum Occur {
  122      /// The option occurs once.
  123      Req,
  124      /// The option could or not occur.
  125      Optional,
  126      /// The option occurs once or multiple times.
  127      Multi,
  128  }
  129  
  130  /// A description of a possible option.
  131  #[deriving(Clone, Eq)]
  132  pub struct Opt {
  133      /// Name of the option
  134      pub name: Name,
  135      /// Whether it has an argument
  136      pub hasarg: HasArg,
  137      /// How often it can occur
  138      pub occur: Occur,
  139      /// Which options it aliases
  140      pub aliases: Vec<Opt> ,
  141  }
  142  
  143  /// One group of options, e.g., both -h and --help, along with
  144  /// their shared description and properties.
  145  #[deriving(Clone, Eq)]
  146  pub struct OptGroup {
  147      /// Short Name of the `OptGroup`
  148      pub short_name: ~str,
  149      /// Long Name of the `OptGroup`
  150      pub long_name: ~str,
  151      /// Hint
  152      pub hint: ~str,
  153      /// Description
  154      pub desc: ~str,
  155      /// Whether it has an argument
  156      pub hasarg: HasArg,
  157      /// How often it can occur
  158      pub occur: Occur
  159  }
  160  
  161  /// Describes wether an option is given at all or has a value.
  162  #[deriving(Clone, Eq)]
  163  enum Optval {
  164      Val(~str),
  165      Given,
  166  }
  167  
  168  /// The result of checking command line arguments. Contains a vector
  169  /// of matches and a vector of free strings.
  170  #[deriving(Clone, Eq)]
  171  pub struct Matches {
  172      /// Options that matched
  173      opts: Vec<Opt> ,
  174      /// Values of the Options that matched
  175      vals: Vec<Vec<Optval> > ,
  176      /// Free string fragments
  177      pub free: Vec<~str>,
  178  }
  179  
  180  /// The type returned when the command line does not conform to the
  181  /// expected format. Call the `to_err_msg` method to retrieve the
  182  /// error as a string.
  183  #[deriving(Clone, Eq, Show)]
  184  pub enum Fail_ {
  185      /// The option requires an argument but none was passed.
  186      ArgumentMissing(~str),
  187      /// The passed option is not declared among the possible options.
  188      UnrecognizedOption(~str),
  189      /// A required option is not present.
  190      OptionMissing(~str),
  191      /// A single occurence option is being used multiple times.
  192      OptionDuplicated(~str),
  193      /// There's an argument being passed to a non-argument option.
  194      UnexpectedArgument(~str),
  195  }
  196  
  197  /// The type of failure that occurred.
  198  #[deriving(Eq)]
  199  #[allow(missing_doc)]
  200  pub enum FailType {
  201      ArgumentMissing_,
  202      UnrecognizedOption_,
  203      OptionMissing_,
  204      OptionDuplicated_,
  205      UnexpectedArgument_,
  206  }
  207  
  208  /// The result of parsing a command line with a set of options.
  209  pub type Result = result::Result<Matches, Fail_>;
  210  
  211  impl Name {
  212      fn from_str(nm&str) -> Name {
  213          if nm.len() == 1u {
  214              Short(nm.char_at(0u))
  215          } else {
  216              Long(nm.to_owned())
  217          }
  218      }
  219  
  220      fn to_str(&self) -> ~str {
  221          match *self {
  222              Short(ch) => ch.to_str(),
  223              Long(ref s) => s.to_owned()
  224          }
  225      }
  226  }
  227  
  228  impl OptGroup {
  229      /// Translate OptGroup into Opt.
  230      /// (Both short and long names correspond to different Opts).
  231      pub fn long_to_short(&self) -> Opt {
  232          let OptGroup {
  233              short_name: short_name,
  234              long_name: long_name,
  235              hasarg: hasarg,
  236              occur: occur,
  237              ..
  238          } = (*self).clone();
  239  
  240          match (short_name.len(), long_name.len()) {
  241              (0,0) => fail!("this long-format option was given no name"),
  242              (0,_) => Opt {
  243                  name: Long((long_name)),
  244                  hasarg: hasarg,
  245                  occur: occur,
  246                  aliases: Vec::new()
  247              },
  248              (1,0) => Opt {
  249                  name: Short(short_name.char_at(0)),
  250                  hasarg: hasarg,
  251                  occur: occur,
  252                  aliases: Vec::new()
  253              },
  254              (1,_) => Opt {
  255                  name: Long((long_name)),
  256                  hasarg: hasarg,
  257                  occur:  occur,
  258                  aliases: vec!(
  259                      Opt {
  260                          name: Short(short_name.char_at(0)),
  261                          hasarg: hasarg,
  262                          occur:  occur,
  263                          aliases: Vec::new()
  264                      }
  265                  )
  266              },
  267              (_,_) => fail!("something is wrong with the long-form opt")
  268          }
  269      }
  270  }
  271  
  272  impl Matches {
  273      fn opt_vals(&self, nm&str) -> Vec<Optval> {
  274          match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
  275              Some(id) => (*self.vals.get(id)).clone(),
  276              None => fail!("No option '{}' defined", nm)
  277          }
  278      }
  279  
  280      fn opt_val(&self, nm&str) -> Option<Optval> {
  281          let vals = self.opt_vals(nm);
  282          if vals.is_empty() {
  283              None
  284          } else {
  285              Some((*vals.get(0)).clone())
  286          }
  287      }
  288  
  289      /// Returns true if an option was matched.
  290      pub fn opt_present(&self, nm&str) -> bool {
  291          !self.opt_vals(nm).is_empty()
  292      }
  293  
  294      /// Returns the number of times an option was matched.
  295      pub fn opt_count(&self, nm&str) -> uint {
  296          self.opt_vals(nm).len()
  297      }
  298  
  299      /// Returns true if any of several options were matched.
  300      pub fn opts_present(&self, names&[~str]) -> bool {
  301          for nm in names.iter() {
  302              match find_opt(self.opts.as_slice(), Name::from_str(*nm)) {
  303                  Some(id) if !self.vals.get(id).is_empty() => return true,
  304                  _ => (),
  305              };
  306          }
  307          false
  308      }
  309  
  310      /// Returns the string argument supplied to one of several matching options or `None`.
  311      pub fn opts_str(&self, names&[~str]) -> Option<~str> {
  312          for nm in names.iter() {
  313              match self.opt_val(*nm) {
  314                  Some(Val(ref s)) => return Some(s.clone()),
  315                  _ => ()
  316              }
  317          }
  318          None
  319      }
  320  
  321      /// Returns a vector of the arguments provided to all matches of the given
  322      /// option.
  323      ///
  324      /// Used when an option accepts multiple values.
  325      pub fn opt_strs(&self, nm&str) -> Vec<~str> {
  326          let mut accVec<~str> = Vec::new();
  327          let r = self.opt_vals(nm);
  328          for v in r.iter() {
  329              match *v {
  330                  Val(ref s) => acc.push((*s).clone()),
  331                  _ => ()
  332              }
  333          }
  334          acc
  335      }
  336  
  337      /// Returns the string argument supplied to a matching option or `None`.
  338      pub fn opt_str(&self, nm&str) -> Option<~str> {
  339          let vals = self.opt_vals(nm);
  340          if vals.is_empty() {
  341              return None::<~str>;
  342          }
  343          match vals.get(0) {
  344              &Val(ref s) => Some((*s).clone()),
  345              _ => None
  346          }
  347      }
  348  
  349  
  350      /// Returns the matching string, a default, or none.
  351      ///
  352      /// Returns none if the option was not present, `def` if the option was
  353      /// present but no argument was provided, and the argument if the option was
  354      /// present and an argument was provided.
  355      pub fn opt_default(&self, nm&str, def&str) -> Option<~str> {
  356          let vals = self.opt_vals(nm);
  357          if vals.is_empty() { return None; }
  358          match vals.get(0) {
  359              &Val(ref s) => Some((*s).clone()),
  360              _ => Some(def.to_owned())
  361          }
  362      }
  363  
  364  }
  365  
  366  fn is_arg(arg: &str) -> bool {
  367      arg.len() > 1 && arg[0] == '-' as u8
  368  }
  369  
  370  fn find_opt(opts: &[Opt], nmName) -> Option<uint> {
  371      // Search main options.
  372      let pos = opts.iter().position(|opt| opt.name == nm);
  373      if pos.is_some() {
  374          return pos
  375      }
  376  
  377      // Search in aliases.
  378      for candidate in opts.iter() {
  379          if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
  380              return opts.iter().position(|opt| opt.name == candidate.name);
  381          }
  382      }
  383  
  384      None
  385  }
  386  
  387  /// Create a long option that is required and takes an argument.
  388  pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
  389      let len = short_name.len();
  390      assert!(len == 1 || len == 0);
  391      OptGroup {
  392          short_name: short_name.to_owned(),
  393          long_name: long_name.to_owned(),
  394          hint: hint.to_owned(),
  395          desc: desc.to_owned(),
  396          hasarg: Yes,
  397          occur: Req
  398      }
  399  }
  400  
  401  /// Create a long option that is optional and takes an argument.
  402  pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
  403      let len = short_name.len();
  404      assert!(len == 1 || len == 0);
  405      OptGroup {
  406          short_name: short_name.to_owned(),
  407          long_name: long_name.to_owned(),
  408          hint: hint.to_owned(),
  409          desc: desc.to_owned(),
  410          hasarg: Yes,
  411          occur: Optional
  412      }
  413  }
  414  
  415  /// Create a long option that is optional and does not take an argument.
  416  pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
  417      let len = short_name.len();
  418      assert!(len == 1 || len == 0);
  419      OptGroup {
  420          short_name: short_name.to_owned(),
  421          long_name: long_name.to_owned(),
  422          hint: "".to_owned(),
  423          desc: desc.to_owned(),
  424          hasarg: No,
  425          occur: Optional
  426      }
  427  }
  428  
  429  /// Create a long option that can occur more than once and does not
  430  /// take an argument.
  431  pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
  432      let len = short_name.len();
  433      assert!(len == 1 || len == 0);
  434      OptGroup {
  435          short_name: short_name.to_owned(),
  436          long_name: long_name.to_owned(),
  437          hint: "".to_owned(),
  438          desc: desc.to_owned(),
  439          hasarg: No,
  440          occur: Multi
  441      }
  442  }
  443  
  444  /// Create a long option that is optional and takes an optional argument.
  445  pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
  446      let len = short_name.len();
  447      assert!(len == 1 || len == 0);
  448      OptGroup {
  449          short_name: short_name.to_owned(),
  450          long_name: long_name.to_owned(),
  451          hint: hint.to_owned(),
  452          desc: desc.to_owned(),
  453          hasarg: Maybe,
  454          occur: Optional
  455      }
  456  }
  457  
  458  /// Create a long option that is optional, takes an argument, and may occur
  459  /// multiple times.
  460  pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
  461      let len = short_name.len();
  462      assert!(len == 1 || len == 0);
  463      OptGroup {
  464          short_name: short_name.to_owned(),
  465          long_name: long_name.to_owned(),
  466          hint: hint.to_owned(),
  467          desc: desc.to_owned(),
  468          hasarg: Yes,
  469          occur: Multi
  470      }
  471  }
  472  
  473  /// Create a generic option group, stating all parameters explicitly
  474  pub fn opt(short_name: &str,
  475             long_name: &str,
  476             desc: &str,
  477             hint: &str,
  478             hasargHasArg,
  479             occurOccur) -> OptGroup {
  480      let len = short_name.len();
  481      assert!(len == 1 || len == 0);
  482      OptGroup {
  483          short_name: short_name.to_owned(),
  484          long_name: long_name.to_owned(),
  485          hint: hint.to_owned(),
  486          desc: desc.to_owned(),
  487          hasarg: hasarg,
  488          occur: occur
  489      }
  490  }
  491  
  492  impl Fail_ {
  493      /// Convert a `Fail_` enum into an error string.
  494      pub fn to_err_msg(self) -> ~str {
  495          match self {
  496              ArgumentMissing(ref nm) => {
  497                  format!("Argument to option '{}' missing.", *nm)
  498              }
  499              UnrecognizedOption(ref nm) => {
  500                  format!("Unrecognized option: '{}'.", *nm)
  501              }
  502              OptionMissing(ref nm) => {
  503                  format!("Required option '{}' missing.", *nm)
  504              }
  505              OptionDuplicated(ref nm) => {
  506                  format!("Option '{}' given more than once.", *nm)
  507              }
  508              UnexpectedArgument(ref nm) => {
  509                  format!("Option '{}' does not take an argument.", *nm)
  510              }
  511          }
  512      }
  513  }
  514  
  515  /// Parse command line arguments according to the provided options.
  516  ///
  517  /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
  518  /// `opt_str`, etc. to interrogate results.  Returns `Err(Fail_)` on failure.
  519  /// Use `to_err_msg` to get an error message.
  520  pub fn getopts(args: &[~str], optgrps: &[OptGroup]) -> Result {
  521      let optsVec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
  522      let n_opts = opts.len();
  523  
  524      fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
  525  
  526      let mut vals = Vec::from_fn(n_opts, f);
  527      let mut freeVec<~str> = Vec::new();
  528      let l = args.len();
  529      let mut i = 0;
  530      while i < l {
  531          let cur = args[i].clone();
  532          let curlen = cur.len();
  533          if !is_arg(cur) {
  534              free.push(cur);
  535          } else if cur == "--".to_owned() {
  536              let mut j = i + 1;
  537              while j < l { free.push(args[j].clone()); j += 1; }
  538              break;
  539          } else {
  540              let mut names;
  541              let mut i_arg = None;
  542              if cur[1] == '-' as u8 {
  543                  let tail = cur.slice(2, curlen);
  544                  let tail_eqVec<&str> = tail.split('=').collect();
  545                  if tail_eq.len() <= 1 {
  546                      names = vec!(Long(tail.to_owned()));
  547                  } else {
  548                      names =
  549                          vec!(Long((*tail_eq.get(0)).to_owned()));
  550                      i_arg = Some((*tail_eq.get(1)).to_owned());
  551                  }
  552              } else {
  553                  let mut j = 1;
  554                  let mut last_valid_opt_id = None;
  555                  names = Vec::new();
  556                  while j < curlen {
  557                      let range = cur.char_range_at(j);
  558                      let opt = Short(range.ch);
  559  
  560                      /* In a series of potential options (eg. -aheJ), if we
  561                         see one which takes an argument, we assume all
  562                         subsequent characters make up the argument. This
  563                         allows options such as -L/usr/local/lib/foo to be
  564                         interpreted correctly
  565                      */
  566  
  567                      match find_opt(opts.as_slice(), opt.clone()) {
  568                        Some(id) => last_valid_opt_id = Some(id),
  569                        None => {
  570                          let arg_follows =
  571                              last_valid_opt_id.is_some() &&
  572                              match opts.get(last_valid_opt_id.unwrap())
  573                                .hasarg {
  574  
  575                                Yes | Maybe => true,
  576                                No => false
  577                              };
  578                          if arg_follows && j < curlen {
  579                              i_arg = Some(cur.slice(j, curlen).to_owned());
  580                              break;
  581                          } else {
  582                              last_valid_opt_id = None;
  583                          }
  584                        }
  585                      }
  586                      names.push(opt);
  587                      j = range.next;
  588                  }
  589              }
  590              let mut name_pos = 0;
  591              for nm in names.iter() {
  592                  name_pos += 1;
  593                  let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
  594                    Some(id) => id,
  595                    None => return Err(UnrecognizedOption(nm.to_str()))
  596                  };
  597                  match opts.get(optid).hasarg {
  598                    No => {
  599                      if !i_arg.is_none() {
  600                          return Err(UnexpectedArgument(nm.to_str()));
  601                      }
  602                      vals.get_mut(optid).push(Given);
  603                    }
  604                    Maybe => {
  605                      if !i_arg.is_none() {
  606                          vals.get_mut(optid)
  607                              .push(Val((i_arg.clone())
  608                              .unwrap()));
  609                      } else if name_pos < names.len() ||
  610                                    i + 1 == l || is_arg(args[i + 1]) {
  611                          vals.get_mut(optid).push(Given);
  612                      } else {
  613                          i += 1;
  614                          vals.get_mut(optid).push(Val(args[i].clone()));
  615                      }
  616                    }
  617                    Yes => {
  618                      if !i_arg.is_none() {
  619                          vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
  620                      } else if i + 1 == l {
  621                          return Err(ArgumentMissing(nm.to_str()));
  622                      } else {
  623                          i += 1;
  624                          vals.get_mut(optid).push(Val(args[i].clone()));
  625                      }
  626                    }
  627                  }
  628              }
  629          }
  630          i += 1;
  631      }
  632      i = 0u;
  633      while i < n_opts {
  634          let n = vals.get(i).len();
  635          let occ = opts.get(i).occur;
  636          if occ == Req {
  637              if n == 0 {
  638                  return Err(OptionMissing(opts.get(i).name.to_str()));
  639              }
  640          }
  641          if occ != Multi {
  642              if n > 1 {
  643                  return Err(OptionDuplicated(opts.get(i).name.to_str()));
  644              }
  645          }
  646          i += 1;
  647      }
  648      Ok(Matches {
  649          opts: opts,
  650          vals: vals,
  651          free: free
  652      })
  653  }
  654  
  655  /// Derive a usage message from a set of long options.
  656  pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {
  657  
  658      let desc_sep = "\n" + " ".repeat(24);
  659  
  660      let mut rows = opts.iter().map(|optref| {
  661          let OptGroup{short_name: short_name,
  662                       long_name: long_name,
  663                       hint: hint,
  664                       desc: desc,
  665                       hasarg: hasarg,
  666                       ..} = (*optref).clone();
  667  
  668          let mut row = StrBuf::from_owned_str(" ".repeat(4));
  669  
  670          // short option
  671          match short_name.len() {
  672              0 => {}
  673              1 => {
  674                  row.push_char('-');
  675                  row.push_str(short_name);
  676                  row.push_char(' ');
  677              }
  678              _ => fail!("the short name should only be 1 ascii char long"),
  679          }
  680  
  681          // long option
  682          match long_name.len() {
  683              0 => {}
  684              _ => {
  685                  row.push_str("--");
  686                  row.push_str(long_name);
  687                  row.push_char(' ');
  688              }
  689          }
  690  
  691          // arg
  692          match hasarg {
  693              No => {}
  694              Yes => row.push_str(hint),
  695              Maybe => {
  696                  row.push_char('[');
  697                  row.push_str(hint);
  698                  row.push_char(']');
  699              }
  700          }
  701  
  702          // FIXME: #5516 should be graphemes not codepoints
  703          // here we just need to indent the start of the description
  704          let rowlen = row.as_slice().char_len();
  705          if rowlen < 24 {
  706              for _ in range(0, 24 - rowlen) {
  707                  row.push_char(' ');
  708              }
  709          } else {
  710              row.push_str(desc_sep)
  711          }
  712  
  713          // Normalize desc to contain words separated by one space character
  714          let mut desc_normalized_whitespace = StrBuf::new();
  715          for word in desc.words() {
  716              desc_normalized_whitespace.push_str(word);
  717              desc_normalized_whitespace.push_char(' ');
  718          }
  719  
  720          // FIXME: #5516 should be graphemes not codepoints
  721          let mut desc_rows = Vec::new();
  722          each_split_within(desc_normalized_whitespace.as_slice(),
  723                            54,
  724                            |substr| {
  725              desc_rows.push(substr.to_owned());
  726              true
  727          });
  728  
  729          // FIXME: #5516 should be graphemes not codepoints
  730          // wrapped description
  731          row.push_str(desc_rows.connect(desc_sep));
  732  
  733          row.into_owned()
  734      });
  735  
  736      format!("{}\n\nOptions:\n{}\n", brief, rows.collect::<Vec<~str> >().connect("\n"))
  737  }
  738  
  739  fn format_option(opt: &OptGroup) -> ~str {
  740      let mut line = StrBuf::new();
  741  
  742      if opt.occur != Req {
  743          line.push_char('[');
  744      }
  745  
  746      // Use short_name is possible, but fallback to long_name.
  747      if opt.short_name.len() > 0 {
  748          line.push_char('-');
  749          line.push_str(opt.short_name);
  750      } else {
  751          line.push_str("--");
  752          line.push_str(opt.long_name);
  753      }
  754  
  755      if opt.hasarg != No {
  756          line.push_char(' ');
  757          if opt.hasarg == Maybe {
  758              line.push_char('[');
  759          }
  760          line.push_str(opt.hint);
  761          if opt.hasarg == Maybe {
  762              line.push_char(']');
  763          }
  764      }
  765  
  766      if opt.occur != Req {
  767          line.push_char(']');
  768      }
  769      if opt.occur == Multi {
  770          line.push_str("..");
  771      }
  772  
  773      line.into_owned()
  774  }
  775  
  776  /// Derive a short one-line usage summary from a set of long options.
  777  pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> ~str {
  778      let mut line = StrBuf::from_str("Usage: " + program_name + " ");
  779      line.push_str(opts.iter().map(format_option).collect::<Vec<~str>>().connect(" "));
  780      line.into_owned()
  781  }
  782  
  783  
  784  /// Splits a string into substrings with possibly internal whitespace,
  785  /// each of them at most `lim` bytes long. The substrings have leading and trailing
  786  /// whitespace removed, and are only cut at whitespace boundaries.
  787  ///
  788  /// Note: Function was moved here from `std::str` because this module is the only place that
  789  /// uses it, and because it was to specific for a general string function.
  790  ///
  791  /// #Failure:
  792  ///
  793  /// Fails during iteration if the string contains a non-whitespace
  794  /// sequence longer than the limit.
  795  fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
  796                       -> bool {
  797      // Just for fun, let's write this as a state machine:
  798  
  799      enum SplitWithinState {
  800          A,  // leading whitespace, initial state
  801          B,  // words
  802          C,  // internal and trailing whitespace
  803      }
  804      enum Whitespace {
  805          Ws, // current char is whitespace
  806          Cr  // current char is not whitespace
  807      }
  808      enum LengthLimit {
  809          UnderLim, // current char makes current substring still fit in limit
  810          OverLim   // current char makes current substring no longer fit in limit
  811      }
  812  
  813      let mut slice_start = 0;
  814      let mut last_start = 0;
  815      let mut last_end = 0;
  816      let mut state = A;
  817      let mut fake_i = ss.len();
  818      let mut lim = lim;
  819  
  820      let mut cont = true;
  821  
  822      // if the limit is larger than the string, lower it to save cycles
  823      if lim >= fake_i {
  824          lim = fake_i;
  825      }
  826  
  827      let machine|&mut bool, (uint, char)-> bool = |cont, (i, c)| {
  828          let whitespace = if ::std::char::is_whitespace(c) { Ws }       else { Cr };
  829          let limit      = if (i - slice_start + 1) <= lim  { UnderLim } else { OverLim };
  830  
  831          state = match (state, whitespace, limit) {
  832              (A, Ws, _)        => { A }
  833              (A, Cr, _)        => { slice_start = i; last_start = i; B }
  834  
  835              (B, Cr, UnderLim) => { B }
  836              (B, Cr, OverLim)  if (i - last_start + 1) > lim
  837                              => fail!("word starting with {} longer than limit!",
  838                                      ss.slice(last_start, i + 1)),
  839              (B, Cr, OverLim)  => {
  840                  *cont = it(ss.slice(slice_start, last_end));
  841                  slice_start = last_start;
  842                  B
  843              }
  844              (B, Ws, UnderLim) => {
  845                  last_end = i;
  846                  C
  847              }
  848              (B, Ws, OverLim)  => {
  849                  last_end = i;
  850                  *cont = it(ss.slice(slice_start, last_end));
  851                  A
  852              }
  853  
  854              (C, Cr, UnderLim) => {
  855                  last_start = i;
  856                  B
  857              }
  858              (C, Cr, OverLim)  => {
  859                  *cont = it(ss.slice(slice_start, last_end));
  860                  slice_start = i;
  861                  last_start = i;
  862                  last_end = i;
  863                  B
  864              }
  865              (C, Ws, OverLim)  => {
  866                  *cont = it(ss.slice(slice_start, last_end));
  867                  A
  868              }
  869              (C, Ws, UnderLim) => {
  870                  C
  871              }
  872          };
  873  
  874          *cont
  875      };
  876  
  877      ss.char_indices().advance(|x| machine(&mut cont, x));
  878  
  879      // Let the automaton 'run out' by supplying trailing whitespace
  880      while cont && match state { B | C => true, A => false } {
  881          machine(&mut cont, (fake_i, ' '));
  882          fake_i += 1;
  883      }
  884      return cont;
  885  }
  886  
  887  #[test]
  888  fn test_split_within() {
  889      fn t(s: &str, i: uint, u: &[~str]) {
  890          let mut v = Vec::new();
  891          each_split_within(s, i, |s| { v.push(s.to_owned()); true });
  892          assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
  893      }
  894      t("", 0, []);
  895      t("", 15, []);
  896      t("hello", 15, ["hello".to_owned()]);
  897      t("\nMary had a little lamb\nLittle lamb\n", 15,
  898          ["Mary had a".to_owned(), "little lamb".to_owned(), "Little lamb".to_owned()]);
  899      t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
  900          ["Mary had a little lamb\nLittle lamb".to_owned()]);
  901  }
  902  
  903  #[cfg(test)]
  904  mod tests {
  905      use super::*;
  906  
  907      use std::result::{Err, Ok};
  908      use std::result;
  909  
  910      fn check_fail_type(f: Fail_, ft: FailType) {
  911          match f {
  912            ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
  913            UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
  914            OptionMissing(_) => assert!(ft == OptionMissing_),
  915            OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
  916            UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
  917          }
  918      }
  919  
  920      // Tests for reqopt
  921      #[test]
  922      fn test_reqopt() {
  923          let long_args = vec!("--test=20".to_owned());
  924          let opts = vec!(reqopt("t", "test", "testing", "TEST"));
  925          let rs = getopts(long_args.as_slice(), opts.as_slice());
  926          match rs {
  927            Ok(ref m) => {
  928              assert!(m.opt_present("test"));
  929              assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
  930              assert!(m.opt_present("t"));
  931              assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
  932            }
  933            _ => { fail!("test_reqopt failed (long arg)"); }
  934          }
  935          let short_args = vec!("-t".to_owned(), "20".to_owned());
  936          match getopts(short_args.as_slice(), opts.as_slice()) {
  937            Ok(ref m) => {
  938              assert!((m.opt_present("test")));
  939              assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
  940              assert!((m.opt_present("t")));
  941              assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
  942            }
  943            _ => { fail!("test_reqopt failed (short arg)"); }
  944          }
  945      }
  946  
  947      #[test]
  948      fn test_reqopt_missing() {
  949          let args = vec!("blah".to_owned());
  950          let opts = vec!(reqopt("t", "test", "testing", "TEST"));
  951          let rs = getopts(args.as_slice(), opts.as_slice());
  952          match rs {
  953            Err(f) => check_fail_type(f, OptionMissing_),
  954            _ => fail!()
  955          }
  956      }
  957  
  958      #[test]
  959      fn test_reqopt_no_arg() {
  960          let long_args = vec!("--test".to_owned());
  961          let opts = vec!(reqopt("t", "test", "testing", "TEST"));
  962          let rs = getopts(long_args.as_slice(), opts.as_slice());
  963          match rs {
  964            Err(f) => check_fail_type(f, ArgumentMissing_),
  965            _ => fail!()
  966          }
  967          let short_args = vec!("-t".to_owned());
  968          match getopts(short_args.as_slice(), opts.as_slice()) {
  969            Err(f) => check_fail_type(f, ArgumentMissing_),
  970            _ => fail!()
  971          }
  972      }
  973  
  974      #[test]
  975      fn test_reqopt_multi() {
  976          let args = vec!("--test=20".to_owned(), "-t".to_owned(), "30".to_owned());
  977          let opts = vec!(reqopt("t", "test", "testing", "TEST"));
  978          let rs = getopts(args.as_slice(), opts.as_slice());
  979          match rs {
  980            Err(f) => check_fail_type(f, OptionDuplicated_),
  981            _ => fail!()
  982          }
  983      }
  984  
  985      // Tests for optopt
  986      #[test]
  987      fn test_optopt() {
  988          let long_args = vec!("--test=20".to_owned());
  989          let opts = vec!(optopt("t", "test", "testing", "TEST"));
  990          let rs = getopts(long_args.as_slice(), opts.as_slice());
  991          match rs {
  992            Ok(ref m) => {
  993              assert!(m.opt_present("test"));
  994              assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
  995              assert!((m.opt_present("t")));
  996              assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
  997            }
  998            _ => fail!()
  999          }
 1000          let short_args = vec!("-t".to_owned(), "20".to_owned());
 1001          match getopts(short_args.as_slice(), opts.as_slice()) {
 1002            Ok(ref m) => {
 1003              assert!((m.opt_present("test")));
 1004              assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
 1005              assert!((m.opt_present("t")));
 1006              assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
 1007            }
 1008            _ => fail!()
 1009          }
 1010      }
 1011  
 1012      #[test]
 1013      fn test_optopt_missing() {
 1014          let args = vec!("blah".to_owned());
 1015          let opts = vec!(optopt("t", "test", "testing", "TEST"));
 1016          let rs = getopts(args.as_slice(), opts.as_slice());
 1017          match rs {
 1018            Ok(ref m) => {
 1019              assert!(!m.opt_present("test"));
 1020              assert!(!m.opt_present("t"));
 1021            }
 1022            _ => fail!()
 1023          }
 1024      }
 1025  
 1026      #[test]
 1027      fn test_optopt_no_arg() {
 1028          let long_args = vec!("--test".to_owned());
 1029          let opts = vec!(optopt("t", "test", "testing", "TEST"));
 1030          let rs = getopts(long_args.as_slice(), opts.as_slice());
 1031          match rs {
 1032            Err(f) => check_fail_type(f, ArgumentMissing_),
 1033            _ => fail!()
 1034          }
 1035          let short_args = vec!("-t".to_owned());
 1036          match getopts(short_args.as_slice(), opts.as_slice()) {
 1037            Err(f) => check_fail_type(f, ArgumentMissing_),
 1038            _ => fail!()
 1039          }
 1040      }
 1041  
 1042      #[test]
 1043      fn test_optopt_multi() {
 1044          let args = vec!("--test=20".to_owned(), "-t".to_owned(), "30".to_owned());
 1045          let opts = vec!(optopt("t", "test", "testing", "TEST"));
 1046          let rs = getopts(args.as_slice(), opts.as_slice());
 1047          match rs {
 1048            Err(f) => check_fail_type(f, OptionDuplicated_),
 1049            _ => fail!()
 1050          }
 1051      }
 1052  
 1053      // Tests for optflag
 1054      #[test]
 1055      fn test_optflag() {
 1056          let long_args = vec!("--test".to_owned());
 1057          let opts = vec!(optflag("t", "test", "testing"));
 1058          let rs = getopts(long_args.as_slice(), opts.as_slice());
 1059          match rs {
 1060            Ok(ref m) => {
 1061              assert!(m.opt_present("test"));
 1062              assert!(m.opt_present("t"));
 1063            }
 1064            _ => fail!()
 1065          }
 1066          let short_args = vec!("-t".to_owned());
 1067          match getopts(short_args.as_slice(), opts.as_slice()) {
 1068            Ok(ref m) => {
 1069              assert!(m.opt_present("test"));
 1070              assert!(m.opt_present("t"));
 1071            }
 1072            _ => fail!()
 1073          }
 1074      }
 1075  
 1076      #[test]
 1077      fn test_optflag_missing() {
 1078          let args = vec!("blah".to_owned());
 1079          let opts = vec!(optflag("t", "test", "testing"));
 1080          let rs = getopts(args.as_slice(), opts.as_slice());
 1081          match rs {
 1082            Ok(ref m) => {
 1083              assert!(!m.opt_present("test"));
 1084              assert!(!m.opt_present("t"));
 1085            }
 1086            _ => fail!()
 1087          }
 1088      }
 1089  
 1090      #[test]
 1091      fn test_optflag_long_arg() {
 1092          let args = vec!("--test=20".to_owned());
 1093          let opts = vec!(optflag("t", "test", "testing"));
 1094          let rs = getopts(args.as_slice(), opts.as_slice());
 1095          match rs {
 1096            Err(f) => {
 1097              error!("{:?}", f.clone().to_err_msg());
 1098              check_fail_type(f, UnexpectedArgument_);
 1099            }
 1100            _ => fail!()
 1101          }
 1102      }
 1103  
 1104      #[test]
 1105      fn test_optflag_multi() {
 1106          let args = vec!("--test".to_owned(), "-t".to_owned());
 1107          let opts = vec!(optflag("t", "test", "testing"));
 1108          let rs = getopts(args.as_slice(), opts.as_slice());
 1109          match rs {
 1110            Err(f) => check_fail_type(f, OptionDuplicated_),
 1111            _ => fail!()
 1112          }
 1113      }
 1114  
 1115      #[test]
 1116      fn test_optflag_short_arg() {
 1117          let args = vec!("-t".to_owned(), "20".to_owned());
 1118          let opts = vec!(optflag("t", "test", "testing"));
 1119          let rs = getopts(args.as_slice(), opts.as_slice());
 1120          match rs {
 1121            Ok(ref m) => {
 1122              // The next variable after the flag is just a free argument
 1123  
 1124              assert!(*m.free.get(0) == "20".to_owned());
 1125            }
 1126            _ => fail!()
 1127          }
 1128      }
 1129  
 1130      // Tests for optflagmulti
 1131      #[test]
 1132      fn test_optflagmulti_short1() {
 1133          let args = vec!("-v".to_owned());
 1134          let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
 1135          let rs = getopts(args.as_slice(), opts.as_slice());
 1136          match rs {
 1137            Ok(ref m) => {
 1138              assert_eq!(m.opt_count("v"), 1);
 1139            }
 1140            _ => fail!()
 1141          }
 1142      }
 1143  
 1144      #[test]
 1145      fn test_optflagmulti_short2a() {
 1146          let args = vec!("-v".to_owned(), "-v".to_owned());
 1147          let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
 1148          let rs = getopts(args.as_slice(), opts.as_slice());
 1149          match rs {
 1150            Ok(ref m) => {
 1151              assert_eq!(m.opt_count("v"), 2);
 1152            }
 1153            _ => fail!()
 1154          }
 1155      }
 1156  
 1157      #[test]
 1158      fn test_optflagmulti_short2b() {
 1159          let args = vec!("-vv".to_owned());
 1160          let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
 1161          let rs = getopts(args.as_slice(), opts.as_slice());
 1162          match rs {
 1163            Ok(ref m) => {
 1164              assert_eq!(m.opt_count("v"), 2);
 1165            }
 1166            _ => fail!()
 1167          }
 1168      }
 1169  
 1170      #[test]
 1171      fn test_optflagmulti_long1() {
 1172          let args = vec!("--verbose".to_owned());
 1173          let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
 1174          let rs = getopts(args.as_slice(), opts.as_slice());
 1175          match rs {
 1176            Ok(ref m) => {
 1177              assert_eq!(m.opt_count("verbose"), 1);
 1178            }
 1179            _ => fail!()
 1180          }
 1181      }
 1182  
 1183      #[test]
 1184      fn test_optflagmulti_long2() {
 1185          let args = vec!("--verbose".to_owned(), "--verbose".to_owned());
 1186          let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
 1187          let rs = getopts(args.as_slice(), opts.as_slice());
 1188          match rs {
 1189            Ok(ref m) => {
 1190              assert_eq!(m.opt_count("verbose"), 2);
 1191            }
 1192            _ => fail!()
 1193          }
 1194      }
 1195  
 1196      #[test]
 1197      fn test_optflagmulti_mix() {
 1198          let args = vec!("--verbose".to_owned(), "-v".to_owned(),
 1199                          "-vv".to_owned(), "verbose".to_owned());
 1200          let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
 1201          let rs = getopts(args.as_slice(), opts.as_slice());
 1202          match rs {
 1203            Ok(ref m) => {
 1204              assert_eq!(m.opt_count("verbose"), 4);
 1205              assert_eq!(m.opt_count("v"), 4);
 1206            }
 1207            _ => fail!()
 1208          }
 1209      }
 1210  
 1211      // Tests for optmulti
 1212      #[test]
 1213      fn test_optmulti() {
 1214          let long_args = vec!("--test=20".to_owned());
 1215          let opts = vec!(optmulti("t", "test", "testing", "TEST"));
 1216          let rs = getopts(long_args.as_slice(), opts.as_slice());
 1217          match rs {
 1218            Ok(ref m) => {
 1219              assert!((m.opt_present("test")));
 1220              assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
 1221              assert!((m.opt_present("t")));
 1222              assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
 1223            }
 1224            _ => fail!()
 1225          }
 1226          let short_args = vec!("-t".to_owned(), "20".to_owned());
 1227          match getopts(short_args.as_slice(), opts.as_slice()) {
 1228            Ok(ref m) => {
 1229              assert!((m.opt_present("test")));
 1230              assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
 1231              assert!((m.opt_present("t")));
 1232              assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
 1233            }
 1234            _ => fail!()
 1235          }
 1236      }
 1237  
 1238      #[test]
 1239      fn test_optmulti_missing() {
 1240          let args = vec!("blah".to_owned());
 1241          let opts = vec!(optmulti("t", "test", "testing", "TEST"));
 1242          let rs = getopts(args.as_slice(), opts.as_slice());
 1243          match rs {
 1244            Ok(ref m) => {
 1245              assert!(!m.opt_present("test"));
 1246              assert!(!m.opt_present("t"));
 1247            }
 1248            _ => fail!()
 1249          }
 1250      }
 1251  
 1252      #[test]
 1253      fn test_optmulti_no_arg() {
 1254          let long_args = vec!("--test".to_owned());
 1255          let opts = vec!(optmulti("t", "test", "testing", "TEST"));
 1256          let rs = getopts(long_args.as_slice(), opts.as_slice());
 1257          match rs {
 1258            Err(f) => check_fail_type(f, ArgumentMissing_),
 1259            _ => fail!()
 1260          }
 1261          let short_args = vec!("-t".to_owned());
 1262          match getopts(short_args.as_slice(), opts.as_slice()) {
 1263            Err(f) => check_fail_type(f, ArgumentMissing_),
 1264            _ => fail!()
 1265          }
 1266      }
 1267  
 1268      #[test]
 1269      fn test_optmulti_multi() {
 1270          let args = vec!("--test=20".to_owned(), "-t".to_owned(), "30".to_owned());
 1271          let opts = vec!(optmulti("t", "test", "testing", "TEST"));
 1272          let rs = getopts(args.as_slice(), opts.as_slice());
 1273          match rs {
 1274            Ok(ref m) => {
 1275                assert!(m.opt_present("test"));
 1276                assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
 1277                assert!(m.opt_present("t"));
 1278                assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
 1279                let pair = m.opt_strs("test");
 1280                assert!(*pair.get(0) == "20".to_owned());
 1281                assert!(*pair.get(1) == "30".to_owned());
 1282            }
 1283            _ => fail!()
 1284          }
 1285      }
 1286  
 1287      #[test]
 1288      fn test_unrecognized_option() {
 1289          let long_args = vec!("--untest".to_owned());
 1290          let opts = vec!(optmulti("t", "test", "testing", "TEST"));
 1291          let rs = getopts(long_args.as_slice(), opts.as_slice());
 1292          match rs {
 1293            Err(f) => check_fail_type(f, UnrecognizedOption_),
 1294            _ => fail!()
 1295          }
 1296          let short_args = vec!("-u".to_owned());
 1297          match getopts(short_args.as_slice(), opts.as_slice()) {
 1298            Err(f) => check_fail_type(f, UnrecognizedOption_),
 1299            _ => fail!()
 1300          }
 1301      }
 1302  
 1303      #[test]
 1304      fn test_combined() {
 1305          let args =
 1306              vec!("prog".to_owned(), "free1".to_owned(), "-s".to_owned(), "20".to_owned(),
 1307              "free2".to_owned(), "--flag".to_owned(), "--long=30".to_owned(), "-f".to_owned(),
 1308              "-m".to_owned(), "40".to_owned(), "-m".to_owned(), "50".to_owned(), "-n".to_owned(),
 1309              "-A B".to_owned(), "-n".to_owned(), "-60 70".to_owned());
 1310          let opts =
 1311              vec!(optopt("s", "something", "something", "SOMETHING"),
 1312                optflag("", "flag", "a flag"),
 1313                reqopt("", "long", "hi", "LONG"),
 1314                optflag("f", "", "another flag"),
 1315                optmulti("m", "", "mmmmmm", "YUM"),
 1316                optmulti("n", "", "nothing", "NOTHING"),
 1317                optopt("", "notpresent", "nothing to see here", "NOPE"));
 1318          let rs = getopts(args.as_slice(), opts.as_slice());
 1319          match rs {
 1320            Ok(ref m) => {
 1321              assert!(*m.free.get(0) == "prog".to_owned());
 1322              assert!(*m.free.get(1) == "free1".to_owned());
 1323              assert_eq!(m.opt_str("s").unwrap(), "20".to_owned());
 1324              assert!(*m.free.get(2) == "free2".to_owned());
 1325              assert!((m.opt_present("flag")));
 1326              assert_eq!(m.opt_str("long").unwrap(), "30".to_owned());
 1327              assert!((m.opt_present("f")));
 1328              let pair = m.opt_strs("m");
 1329              assert!(*pair.get(0) == "40".to_owned());
 1330              assert!(*pair.get(1) == "50".to_owned());
 1331              let pair = m.opt_strs("n");
 1332              assert!(*pair.get(0) == "-A B".to_owned());
 1333              assert!(*pair.get(1) == "-60 70".to_owned());
 1334              assert!((!m.opt_present("notpresent")));
 1335            }
 1336            _ => fail!()
 1337          }
 1338      }
 1339  
 1340      #[test]
 1341      fn test_multi() {
 1342          let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
 1343                       optopt("", "encrypt", "encrypt", "ENCRYPT"),
 1344                       optopt("f", "", "flag", "FLAG"));
 1345  
 1346          let args_single = vec!("-e".to_owned(), "foo".to_owned());
 1347          let matches_single = &match getopts(args_single.as_slice(),
 1348                                              opts.as_slice()) {
 1349            result::Ok(m) => m,
 1350            result::Err(_) => fail!()
 1351          };
 1352          assert!(matches_single.opts_present(["e".to_owned()]));
 1353          assert!(matches_single.opts_present(["encrypt".to_owned(), "e".to_owned()]));
 1354          assert!(matches_single.opts_present(["e".to_owned(), "encrypt".to_owned()]));
 1355          assert!(!matches_single.opts_present(["encrypt".to_owned()]));
 1356          assert!(!matches_single.opts_present(["thing".to_owned()]));
 1357          assert!(!matches_single.opts_present([]));
 1358  
 1359          assert_eq!(matches_single.opts_str(["e".to_owned()]).unwrap(), "foo".to_owned());
 1360          assert_eq!(matches_single.opts_str(["e".to_owned(), "encrypt".to_owned()]).unwrap(),
 1361                     "foo".to_owned());
 1362          assert_eq!(matches_single.opts_str(["encrypt".to_owned(), "e".to_owned()]).unwrap(),
 1363                     "foo".to_owned());
 1364  
 1365          let args_both = vec!("-e".to_owned(), "foo".to_owned(), "--encrypt".to_owned(),
 1366                               "foo".to_owned());
 1367          let matches_both = &match getopts(args_both.as_slice(),
 1368                                            opts.as_slice()) {
 1369            result::Ok(m) => m,
 1370            result::Err(_) => fail!()
 1371          };
 1372          assert!(matches_both.opts_present(["e".to_owned()]));
 1373          assert!(matches_both.opts_present(["encrypt".to_owned()]));
 1374          assert!(matches_both.opts_present(["encrypt".to_owned(), "e".to_owned()]));
 1375          assert!(matches_both.opts_present(["e".to_owned(), "encrypt".to_owned()]));
 1376          assert!(!matches_both.opts_present(["f".to_owned()]));
 1377          assert!(!matches_both.opts_present(["thing".to_owned()]));
 1378          assert!(!matches_both.opts_present([]));
 1379  
 1380          assert_eq!(matches_both.opts_str(["e".to_owned()]).unwrap(), "foo".to_owned());
 1381          assert_eq!(matches_both.opts_str(["encrypt".to_owned()]).unwrap(), "foo".to_owned());
 1382          assert_eq!(matches_both.opts_str(["e".to_owned(), "encrypt".to_owned()]).unwrap(),
 1383                     "foo".to_owned());
 1384          assert_eq!(matches_both.opts_str(["encrypt".to_owned(), "e".to_owned()]).unwrap(),
 1385                     "foo".to_owned());
 1386      }
 1387  
 1388      #[test]
 1389      fn test_nospace() {
 1390          let args = vec!("-Lfoo".to_owned(), "-M.".to_owned());
 1391          let opts = vec!(optmulti("L", "", "library directory", "LIB"),
 1392                       optmulti("M", "", "something", "MMMM"));
 1393          let matches = &match getopts(args.as_slice(), opts.as_slice()) {
 1394            result::Ok(m) => m,
 1395            result::Err(_) => fail!()
 1396          };
 1397          assert!(matches.opts_present(["L".to_owned()]));
 1398          assert_eq!(matches.opts_str(["L".to_owned()]).unwrap(), "foo".to_owned());
 1399          assert!(matches.opts_present(["M".to_owned()]));
 1400          assert_eq!(matches.opts_str(["M".to_owned()]).unwrap(), ".".to_owned());
 1401  
 1402      }
 1403  
 1404      #[test]
 1405      fn test_long_to_short() {
 1406          let mut short = Opt {
 1407              name: Long("banana".to_owned()),
 1408              hasarg: Yes,
 1409              occur: Req,
 1410              aliases: Vec::new(),
 1411          };
 1412          short.aliases = vec!(Opt { name: Short('b'),
 1413                                  hasarg: Yes,
 1414                                  occur: Req,
 1415                                  aliases: Vec::new() });
 1416          let verbose = reqopt("b", "banana", "some bananas", "VAL");
 1417  
 1418          assert!(verbose.long_to_short() == short);
 1419      }
 1420  
 1421      #[test]
 1422      fn test_aliases_long_and_short() {
 1423          let opts = vec!(
 1424              optflagmulti("a", "apple", "Desc"));
 1425  
 1426          let args = vec!("-a".to_owned(), "--apple".to_owned(), "-a".to_owned());
 1427  
 1428          let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
 1429          assert_eq!(3, matches.opt_count("a"));
 1430          assert_eq!(3, matches.opt_count("apple"));
 1431      }
 1432  
 1433      #[test]
 1434      fn test_usage() {
 1435          let optgroups = vec!(
 1436              reqopt("b", "banana", "Desc", "VAL"),
 1437              optopt("a", "012345678901234567890123456789",
 1438                               "Desc", "VAL"),
 1439              optflag("k", "kiwi", "Desc"),
 1440              optflagopt("p", "", "Desc", "VAL"),
 1441              optmulti("l", "", "Desc", "VAL"));
 1442  
 1443          let expected =
 1444  "Usage: fruits
 1445  
 1446  Options:
 1447      -b --banana VAL     Desc
 1448      -a --012345678901234567890123456789 VAL
 1449                          Desc
 1450      -k --kiwi           Desc
 1451      -p [VAL]            Desc
 1452      -l VAL              Desc
 1453  ".to_owned();
 1454  
 1455          let generated_usage = usage("Usage: fruits", optgroups.as_slice());
 1456  
 1457          debug!("expected: <<{}>>", expected);
 1458          debug!("generated: <<{}>>", generated_usage);
 1459          assert_eq!(generated_usage, expected);
 1460      }
 1461  
 1462      #[test]
 1463      fn test_usage_description_wrapping() {
 1464          // indentation should be 24 spaces
 1465          // lines wrap after 78: or rather descriptions wrap after 54
 1466  
 1467          let optgroups = vec!(
 1468              optflag("k", "kiwi",
 1469                  "This is a long description which won't be wrapped..+.."), // 54
 1470              optflag("a", "apple",
 1471                  "This is a long description which _will_ be wrapped..+.."));
 1472  
 1473          let expected =
 1474  "Usage: fruits
 1475  
 1476  Options:
 1477      -k --kiwi           This is a long description which won't be wrapped..+..
 1478      -a --apple          This is a long description which _will_ be
 1479                          wrapped..+..
 1480  ".to_owned();
 1481  
 1482          let usage = usage("Usage: fruits", optgroups.as_slice());
 1483  
 1484          debug!("expected: <<{}>>", expected);
 1485          debug!("generated: <<{}>>", usage);
 1486          assert!(usage == expected)
 1487      }
 1488  
 1489      #[test]
 1490      fn test_usage_description_multibyte_handling() {
 1491          let optgroups = vec!(
 1492              optflag("k", "k\u2013w\u2013",
 1493                  "The word kiwi is normally spelled with two i's"),
 1494              optflag("a", "apple",
 1495                  "This \u201Cdescription\u201D has some characters that could \
 1496  confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
 1497  
 1498          let expected =
 1499  "Usage: fruits
 1500  
 1501  Options:
 1502      -k --k–w–           The word kiwi is normally spelled with two i's
 1503      -a --apple          This â€œdescription” has some characters that could
 1504                          confuse the line wrapping; an apple costs 0.51€ in
 1505                          some parts of Europe.
 1506  ".to_owned();
 1507  
 1508          let usage = usage("Usage: fruits", optgroups.as_slice());
 1509  
 1510          debug!("expected: <<{}>>", expected);
 1511          debug!("generated: <<{}>>", usage);
 1512          assert!(usage == expected)
 1513      }
 1514  
 1515      #[test]
 1516      fn test_short_usage() {
 1517          let optgroups = vec!(
 1518              reqopt("b", "banana", "Desc", "VAL"),
 1519              optopt("a", "012345678901234567890123456789",
 1520                       "Desc", "VAL"),
 1521              optflag("k", "kiwi", "Desc"),
 1522              optflagopt("p", "", "Desc", "VAL"),
 1523              optmulti("l", "", "Desc", "VAL"));
 1524  
 1525          let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_owned();
 1526          let generated_usage = short_usage("fruits", optgroups.as_slice());
 1527  
 1528          debug!("expected: <<{}>>", expected);
 1529          debug!("generated: <<{}>>", generated_usage);
 1530          assert_eq!(generated_usage, expected);
 1531      }
 1532  }


libgetopts/lib.rs:199:22-199:22 -enum- definition:
pub enum FailType {
    ArgumentMissing_,
    UnrecognizedOption_,
references:- 3
197: /// The type of failure that occurred.


libgetopts/lib.rs:131:23-131:23 -struct- definition:
pub struct Opt {
    /// Name of the option
    pub name: Name,
references:- 20
253:             },
254:             (1,_) => Opt {
255:                 name: Long((long_name)),
--
258:                 aliases: vec!(
259:                     Opt {
260:                         name: Short(short_name.char_at(0)),
--
520: pub fn getopts(args: &[~str], optgrps: &[OptGroup]) -> Result {
521:     let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
522:     let n_opts = opts.len();


libgetopts/lib.rs:183:29-183:29 -enum- definition:
pub enum Fail_ {
    /// The option requires an argument but none was passed.
    ArgumentMissing(~str),
references:- 8
182: /// error as a string.
184: pub enum Fail_ {
--
208: /// The result of parsing a command line with a set of options.
209: pub type Result = result::Result<Matches, Fail_>;
--
492: impl Fail_ {
493:     /// Convert a `Fail_` enum into an error string.


libgetopts/lib.rs:170:23-170:23 -struct- definition:
pub struct Matches {
    /// Options that matched
    opts: Vec<Opt> ,
references:- 14
169: /// of matches and a vector of free strings.
171: pub struct Matches {
--
272: impl Matches {
273:     fn opt_vals(&self, nm: &str) -> Vec<Optval> {
--
647:     }
648:     Ok(Matches {
649:         opts: opts,


libgetopts/lib.rs:365:1-365:1 -fn- definition:
fn is_arg(arg: &str) -> bool {
    arg.len() > 1 && arg[0] == '-' as u8
}
references:- 2
532:         let curlen = cur.len();
533:         if !is_arg(cur) {
534:             free.push(cur);
--
609:                     } else if name_pos < names.len() ||
610:                                   i + 1 == l || is_arg(args[i + 1]) {
611:                         vals.get_mut(optid).push(Given);


libgetopts/lib.rs:145:23-145:23 -struct- definition:
pub struct OptGroup {
    /// Short Name of the `OptGroup`
    pub short_name: ~str,
references:- 32


libgetopts/lib.rs:109:23-109:23 -enum- definition:
pub enum HasArg {
    /// The option requires an argument.
    Yes,
references:- 8
135:     /// Whether it has an argument
136:     pub hasarg: HasArg,
137:     /// How often it can occur
--
477:            hint: &str,
478:            hasarg: HasArg,
479:            occur: Occur) -> OptGroup {


libgetopts/lib.rs:120:23-120:23 -enum- definition:
pub enum Occur {
    /// The option occurs once.
    Req,
references:- 8
119: /// Describes how often an option may occur.
121: pub enum Occur {
--
478:            hasarg: HasArg,
479:            occur: Occur) -> OptGroup {
480:     let len = short_name.len();


libgetopts/lib.rs:98:23-98:23 -enum- definition:
pub enum Name {
    /// A string representing the long name of an option.
    /// For example: "help"
references:- 9
97: /// Name of an option. Either a string or a single char.
99: pub enum Name {
--
211: impl Name {
212:     fn from_str(nm: &str) -> Name {
--
370: fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
371:     // Search main options.


libgetopts/lib.rs:162:23-162:23 -enum- definition:
enum Optval {
    Val(~str),
    Given,
references:- 9
161: /// Describes wether an option is given at all or has a value.
163: enum Optval {
--
174:     /// Values of the Options that matched
175:     vals: Vec<Vec<Optval> > ,
176:     /// Free string fragments
--
272: impl Matches {
273:     fn opt_vals(&self, nm: &str) -> Vec<Optval> {
274:         match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
--
524:     fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }


libgetopts/lib.rs:369:1-369:1 -fn- definition:
fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
    // Search main options.
    let pos = opts.iter().position(|opt| opt.name == nm);
references:- 4
567:                     match find_opt(opts.as_slice(), opt.clone()) {
568:                       Some(id) => last_valid_opt_id = Some(id),
--
592:                 name_pos += 1;
593:                 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
594:                   Some(id) => id,