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