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