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