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