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