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