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