(index<- )        ./libextra/getopts.rs

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

libextra/getopts.rs:153:16-153:16 -enum- definition:
#[deriving(Eq)]
pub enum FailType {
references:-
153: #[deriving(Eq)]
153: #[deriving(Eq)]
153: #[deriving(Eq)]


libextra/getopts.rs:131:23-131:23 -struct- definition:
#[deriving(Clone, Eq)]
pub struct Matches {
references:-
131: #[deriving(Clone, Eq)]
131: #[deriving(Clone, Eq)]
510:     Ok(Matches {
131: #[deriving(Clone, Eq)]
131: #[deriving(Clone, Eq)]
131: #[deriving(Clone, Eq)]
131: #[deriving(Clone, Eq)]
131: #[deriving(Clone, Eq)]
131: #[deriving(Clone, Eq)]
163: pub type Result = result::Result<Matches, Fail_>;
131: #[deriving(Clone, Eq)]
182: impl Matches {
131: #[deriving(Clone, Eq)]
131: #[deriving(Clone, Eq)]


libextra/getopts.rs:672:4-672:4 -fn- definition:
    pub fn getopts(args: &[~str], opts: &[OptGroup]) -> ::getopts::Result {
        ::getopts::getopts(args, opts.map(|x| x.long_to_short()))
references:-
libextra/test.rs:
227:         match groups::getopts(args_, optgroups()) {


libextra/getopts.rs:143:30-143:30 -enum- definition:
#[deriving(Clone, Eq, ToStr)]
pub enum Fail_ {
references:-
163: pub type Result = result::Result<Matches, Fail_>;
143: #[deriving(Clone, Eq, ToStr)]
363: impl Fail_ {
143: #[deriving(Clone, Eq, ToStr)]
143: #[deriving(Clone, Eq, ToStr)]
143: #[deriving(Clone, Eq, ToStr)]
143: #[deriving(Clone, Eq, ToStr)]
143: #[deriving(Clone, Eq, ToStr)]


libextra/getopts.rs:283:1-283:1 -fn- definition:

fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
references:-
463:                 let optid = match find_opt(opts, (*nm).clone()) {
216:             match find_opt(self.opts, Name::from_str(*nm)) {
186:         match find_opt(self.opts, Name::from_str(nm)) {
437:                     match find_opt(opts, opt.clone()) {


libextra/getopts.rs:279:1-279:1 -fn- definition:

fn is_arg(arg: &str) -> bool {
references:-
478:                                   i + 1 == l || is_arg(args[i + 1]) {
403:         if !is_arg(cur) {


libextra/getopts.rs:102:23-102:23 -enum- definition:
#[deriving(Clone, Eq)]
pub enum Occur {
references:-
102: #[deriving(Clone, Eq)]
102: #[deriving(Clone, Eq)]
538:         occur: Occur
102: #[deriving(Clone, Eq)]
102: #[deriving(Clone, Eq)]
117:     occur: Occur,
102: #[deriving(Clone, Eq)]


libextra/getopts.rs:390:46-390:46 -fn- definition:
/// Use `to_err_msg` to get an error message.
pub fn getopts(args: &[~str], opts: &[Opt]) -> Result {
references:-
673:         ::getopts::getopts(args, opts.map(|x| x.long_to_short()))


libextra/getopts.rs:677:4-677:4 -fn- definition:
    pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {

references:-
libextra/test.rs:
196:     println(groups::usage(message, optgroups()));


libextra/getopts.rs:94:23-94:23 -enum- definition:
#[deriving(Clone, Eq)]
pub enum HasArg {
references:-
94: #[deriving(Clone, Eq)]
94: #[deriving(Clone, Eq)]
115:     hasarg: HasArg,
94: #[deriving(Clone, Eq)]
94: #[deriving(Clone, Eq)]
94: #[deriving(Clone, Eq)]
536:         hasarg: HasArg,


libextra/getopts.rs:394:4-394:4 -fn- definition:
    fn f(_x: uint) -> ~[Optval] { return ~[]; }

references:-
396:     let mut vals = vec::from_fn(n_opts, f);


libextra/getopts.rs:526:4-526:4 -struct- definition:
    pub struct OptGroup {
        /// Short Name of the `OptGroup`
references:-
646:         OptGroup {
682:             let OptGroup{short_name: short_name,
525:     #[deriving(Clone, Eq)]
658:     pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
661:         OptGroup {
525:     #[deriving(Clone, Eq)]
525:     #[deriving(Clone, Eq)]
614:     pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
545:             let OptGroup {
525:     #[deriving(Clone, Eq)]
525:     #[deriving(Clone, Eq)]
672:     pub fn getopts(args: &[~str], opts: &[OptGroup]) -> ::getopts::Result {
617:         OptGroup {
525:     #[deriving(Clone, Eq)]
525:     #[deriving(Clone, Eq)]
586:     pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
600:     pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
643:     pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
525:     #[deriving(Clone, Eq)]
525:     #[deriving(Clone, Eq)]
603:         OptGroup {
629:     pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
677:     pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {
541:     impl OptGroup {
589:         OptGroup {
525:     #[deriving(Clone, Eq)]
632:         OptGroup {
525:     #[deriving(Clone, Eq)]
libextra/test.rs:
172: fn optgroups() -> ~[getopts::groups::OptGroup] {


libextra/getopts.rs:162:64-162:64 -ty- definition:
/// The result of parsing a command line with a set of options.
pub type Result = result::Result<Matches, Fail_>;
references:-
391: pub fn getopts(args: &[~str], opts: &[Opt]) -> Result {
672:     pub fn getopts(args: &[~str], opts: &[OptGroup]) -> ::getopts::Result {


libextra/getopts.rs:110:23-110:23 -struct- definition:
#[deriving(Clone, Eq)]
pub struct Opt {
references:-
323:     Opt {
110: #[deriving(Clone, Eq)]
355:     Opt {
110: #[deriving(Clone, Eq)]
312: pub fn optopt(name: &str) -> Opt {
313:     Opt {
344:     Opt {
119:     aliases: ~[Opt],
110: #[deriving(Clone, Eq)]
561:                 (1,0) => Opt {
110: #[deriving(Clone, Eq)]
322: pub fn optflag(name: &str) -> Opt {
391: pub fn getopts(args: &[~str], opts: &[Opt]) -> Result {
110: #[deriving(Clone, Eq)]
343: pub fn optflagopt(name: &str) -> Opt {
354: pub fn optmulti(name: &str) -> Opt {
333: pub fn optflagmulti(name: &str) -> Opt {
110: #[deriving(Clone, Eq)]
134:     opts: ~[Opt],
110: #[deriving(Clone, Eq)]
544:         pub fn long_to_short(&self) -> Opt {
110: #[deriving(Clone, Eq)]
572:                         Opt {
302: pub fn reqopt(name: &str) -> Opt {
284: fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
334:     Opt {
110: #[deriving(Clone, Eq)]
555:                 (0,_) => Opt {
110: #[deriving(Clone, Eq)]
303:     Opt {
(567)(110)

libextra/getopts.rs:600:4-600:4 -fn- definition:
    pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
        let len = short_name.len();
references:-
libextra/test.rs:
183:       groups::optopt("", "ratchet-noise-percent",
188:       groups::optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite",
186:       groups::optopt("", "logfile", "Write logs to the specified file instead \
177:       groups::optopt("", "save-metrics", "Location to save bench metrics",
179:       groups::optopt("", "ratchet-metrics",


libextra/getopts.rs:614:4-614:4 -fn- definition:
    pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
        let len = short_name.len();
references:-
libextra/test.rs:
173:     ~[groups::optflag("", "ignored", "Run ignored tests"),
175:       groups::optflag("", "bench", "Run benchmarks instead of tests"),
174:       groups::optflag("", "test", "Run tests and not benchmarks"),
176:       groups::optflag("h", "help", "Display this message (longer with --help)"),


libextra/getopts.rs:769:4-769:4 -fn- definition:
    fn each_split_within<'a>(ss: &'a str,
                             lim: uint,
references:-
743:             do each_split_within(desc_normalized_whitespace, 54) |substr| {


libextra/getopts.rs:123:23-123:23 -enum- definition:
#[deriving(Clone, Eq)]
enum Optval {
references:-
123: #[deriving(Clone, Eq)]
136:     vals: ~[~[Optval]],
123: #[deriving(Clone, Eq)]
394:     fn f(_x: uint) -> ~[Optval] { return ~[]; }
123: #[deriving(Clone, Eq)]
185:     pub fn opt_vals(&self, nm: &str) -> ~[Optval] {
123: #[deriving(Clone, Eq)]
123: #[deriving(Clone, Eq)]
194:     pub fn opt_val(&self, nm: &str) -> Option<Optval> {


libextra/getopts.rs:87:23-87:23 -enum- definition:
#[deriving(Clone, Eq)]
pub enum Name {
references:-
113:     name: Name,
87: #[deriving(Clone, Eq)]
87: #[deriving(Clone, Eq)]
87: #[deriving(Clone, Eq)]
87: #[deriving(Clone, Eq)]
166:     fn from_str(nm: &str) -> Name {
165: impl Name {
87: #[deriving(Clone, Eq)]
284: fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {