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