]> git.lizzy.rs Git - rust.git/blob - src/libgetopts/lib.rs
libstd: Remove all uses of `~str` from `libstd`
[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<StrBuf>) {
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<StrBuf> = os::args().iter()
53 //!                                       .map(|x| x.to_strbuf())
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://static.rust-lang.org/doc/master")]
88 #![feature(globs, phase)]
89 #![deny(missing_doc)]
90 #![deny(deprecated_owned_vector)]
91
92 #[cfg(test)] #[phase(syntax, link)] extern crate log;
93
94 use std::cmp::Eq;
95 use std::result::{Err, Ok};
96 use std::result;
97 use std::strbuf::StrBuf;
98
99 /// Name of an option. Either a string or a single char.
100 #[deriving(Clone, Eq)]
101 pub enum Name {
102     /// A string representing the long name of an option.
103     /// For example: "help"
104     Long(StrBuf),
105     /// A char representing the short name of an option.
106     /// For example: 'h'
107     Short(char),
108 }
109
110 /// Describes whether an option has an argument.
111 #[deriving(Clone, Eq)]
112 pub enum HasArg {
113     /// The option requires an argument.
114     Yes,
115     /// The option is just a flag, therefore no argument.
116     No,
117     /// The option argument is optional and it could or not exist.
118     Maybe,
119 }
120
121 /// Describes how often an option may occur.
122 #[deriving(Clone, Eq)]
123 pub enum Occur {
124     /// The option occurs once.
125     Req,
126     /// The option could or not occur.
127     Optional,
128     /// The option occurs once or multiple times.
129     Multi,
130 }
131
132 /// A description of a possible option.
133 #[deriving(Clone, Eq)]
134 pub struct Opt {
135     /// Name of the option
136     pub name: Name,
137     /// Whether it has an argument
138     pub hasarg: HasArg,
139     /// How often it can occur
140     pub occur: Occur,
141     /// Which options it aliases
142     pub aliases: Vec<Opt> ,
143 }
144
145 /// One group of options, e.g., both -h and --help, along with
146 /// their shared description and properties.
147 #[deriving(Clone, Eq)]
148 pub struct OptGroup {
149     /// Short Name of the `OptGroup`
150     pub short_name: StrBuf,
151     /// Long Name of the `OptGroup`
152     pub long_name: StrBuf,
153     /// Hint
154     pub hint: StrBuf,
155     /// Description
156     pub desc: StrBuf,
157     /// Whether it has an argument
158     pub hasarg: HasArg,
159     /// How often it can occur
160     pub occur: Occur
161 }
162
163 /// Describes wether an option is given at all or has a value.
164 #[deriving(Clone, Eq)]
165 enum Optval {
166     Val(StrBuf),
167     Given,
168 }
169
170 /// The result of checking command line arguments. Contains a vector
171 /// of matches and a vector of free strings.
172 #[deriving(Clone, Eq)]
173 pub struct Matches {
174     /// Options that matched
175     opts: Vec<Opt> ,
176     /// Values of the Options that matched
177     vals: Vec<Vec<Optval> > ,
178     /// Free string fragments
179     pub free: Vec<StrBuf>,
180 }
181
182 /// The type returned when the command line does not conform to the
183 /// expected format. Call the `to_err_msg` method to retrieve the
184 /// error as a string.
185 #[deriving(Clone, Eq, Show)]
186 pub enum Fail_ {
187     /// The option requires an argument but none was passed.
188     ArgumentMissing(StrBuf),
189     /// The passed option is not declared among the possible options.
190     UnrecognizedOption(StrBuf),
191     /// A required option is not present.
192     OptionMissing(StrBuf),
193     /// A single occurence option is being used multiple times.
194     OptionDuplicated(StrBuf),
195     /// There's an argument being passed to a non-argument option.
196     UnexpectedArgument(StrBuf),
197 }
198
199 /// The type of failure that occurred.
200 #[deriving(Eq)]
201 #[allow(missing_doc)]
202 pub enum FailType {
203     ArgumentMissing_,
204     UnrecognizedOption_,
205     OptionMissing_,
206     OptionDuplicated_,
207     UnexpectedArgument_,
208 }
209
210 /// The result of parsing a command line with a set of options.
211 pub type Result = result::Result<Matches, Fail_>;
212
213 impl Name {
214     fn from_str(nm: &str) -> Name {
215         if nm.len() == 1u {
216             Short(nm.char_at(0u))
217         } else {
218             Long(nm.to_strbuf())
219         }
220     }
221
222     fn to_str(&self) -> StrBuf {
223         match *self {
224             Short(ch) => ch.to_str().to_strbuf(),
225             Long(ref s) => s.to_strbuf()
226         }
227     }
228 }
229
230 impl OptGroup {
231     /// Translate OptGroup into Opt.
232     /// (Both short and long names correspond to different Opts).
233     pub fn long_to_short(&self) -> Opt {
234         let OptGroup {
235             short_name: short_name,
236             long_name: long_name,
237             hasarg: hasarg,
238             occur: occur,
239             ..
240         } = (*self).clone();
241
242         match (short_name.len(), long_name.len()) {
243             (0,0) => fail!("this long-format option was given no name"),
244             (0,_) => Opt {
245                 name: Long((long_name)),
246                 hasarg: hasarg,
247                 occur: occur,
248                 aliases: Vec::new()
249             },
250             (1,0) => Opt {
251                 name: Short(short_name.as_slice().char_at(0)),
252                 hasarg: hasarg,
253                 occur: occur,
254                 aliases: Vec::new()
255             },
256             (1,_) => Opt {
257                 name: Long((long_name)),
258                 hasarg: hasarg,
259                 occur:  occur,
260                 aliases: vec!(
261                     Opt {
262                         name: Short(short_name.as_slice().char_at(0)),
263                         hasarg: hasarg,
264                         occur:  occur,
265                         aliases: Vec::new()
266                     }
267                 )
268             },
269             (_,_) => fail!("something is wrong with the long-form opt")
270         }
271     }
272 }
273
274 impl Matches {
275     fn opt_vals(&self, nm: &str) -> Vec<Optval> {
276         match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
277             Some(id) => (*self.vals.get(id)).clone(),
278             None => fail!("No option '{}' defined", nm)
279         }
280     }
281
282     fn opt_val(&self, nm: &str) -> Option<Optval> {
283         let vals = self.opt_vals(nm);
284         if vals.is_empty() {
285             None
286         } else {
287             Some((*vals.get(0)).clone())
288         }
289     }
290
291     /// Returns true if an option was matched.
292     pub fn opt_present(&self, nm: &str) -> bool {
293         !self.opt_vals(nm).is_empty()
294     }
295
296     /// Returns the number of times an option was matched.
297     pub fn opt_count(&self, nm: &str) -> uint {
298         self.opt_vals(nm).len()
299     }
300
301     /// Returns true if any of several options were matched.
302     pub fn opts_present(&self, names: &[StrBuf]) -> bool {
303         for nm in names.iter() {
304             match find_opt(self.opts.as_slice(),
305                            Name::from_str(nm.as_slice())) {
306                 Some(id) if !self.vals.get(id).is_empty() => return true,
307                 _ => (),
308             };
309         }
310         false
311     }
312
313     /// Returns the string argument supplied to one of several matching options or `None`.
314     pub fn opts_str(&self, names: &[StrBuf]) -> Option<StrBuf> {
315         for nm in names.iter() {
316             match self.opt_val(nm.as_slice()) {
317                 Some(Val(ref s)) => return Some(s.clone()),
318                 _ => ()
319             }
320         }
321         None
322     }
323
324     /// Returns a vector of the arguments provided to all matches of the given
325     /// option.
326     ///
327     /// Used when an option accepts multiple values.
328     pub fn opt_strs(&self, nm: &str) -> Vec<StrBuf> {
329         let mut acc: Vec<StrBuf> = Vec::new();
330         let r = self.opt_vals(nm);
331         for v in r.iter() {
332             match *v {
333                 Val(ref s) => acc.push((*s).clone()),
334                 _ => ()
335             }
336         }
337         acc
338     }
339
340     /// Returns the string argument supplied to a matching option or `None`.
341     pub fn opt_str(&self, nm: &str) -> Option<StrBuf> {
342         let vals = self.opt_vals(nm);
343         if vals.is_empty() {
344             return None::<StrBuf>;
345         }
346         match vals.get(0) {
347             &Val(ref s) => Some((*s).clone()),
348             _ => None
349         }
350     }
351
352
353     /// Returns the matching string, a default, or none.
354     ///
355     /// Returns none if the option was not present, `def` if the option was
356     /// present but no argument was provided, and the argument if the option was
357     /// present and an argument was provided.
358     pub fn opt_default(&self, nm: &str, def: &str) -> Option<StrBuf> {
359         let vals = self.opt_vals(nm);
360         if vals.is_empty() {
361             return None;
362         }
363         match vals.get(0) {
364             &Val(ref s) => Some((*s).clone()),
365             _ => Some(def.to_strbuf())
366         }
367     }
368
369 }
370
371 fn is_arg(arg: &str) -> bool {
372     arg.len() > 1 && arg[0] == '-' as u8
373 }
374
375 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
376     // Search main options.
377     let pos = opts.iter().position(|opt| opt.name == nm);
378     if pos.is_some() {
379         return pos
380     }
381
382     // Search in aliases.
383     for candidate in opts.iter() {
384         if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
385             return opts.iter().position(|opt| opt.name == candidate.name);
386         }
387     }
388
389     None
390 }
391
392 /// Create a long option that is required and takes an argument.
393 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
394     let len = short_name.len();
395     assert!(len == 1 || len == 0);
396     OptGroup {
397         short_name: short_name.to_strbuf(),
398         long_name: long_name.to_strbuf(),
399         hint: hint.to_strbuf(),
400         desc: desc.to_strbuf(),
401         hasarg: Yes,
402         occur: Req
403     }
404 }
405
406 /// Create a long option that is optional and takes an argument.
407 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
408     let len = short_name.len();
409     assert!(len == 1 || len == 0);
410     OptGroup {
411         short_name: short_name.to_strbuf(),
412         long_name: long_name.to_strbuf(),
413         hint: hint.to_strbuf(),
414         desc: desc.to_strbuf(),
415         hasarg: Yes,
416         occur: Optional
417     }
418 }
419
420 /// Create a long option that is optional and does not take an argument.
421 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
422     let len = short_name.len();
423     assert!(len == 1 || len == 0);
424     OptGroup {
425         short_name: short_name.to_strbuf(),
426         long_name: long_name.to_strbuf(),
427         hint: "".to_strbuf(),
428         desc: desc.to_strbuf(),
429         hasarg: No,
430         occur: Optional
431     }
432 }
433
434 /// Create a long option that can occur more than once and does not
435 /// take an argument.
436 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
437     let len = short_name.len();
438     assert!(len == 1 || len == 0);
439     OptGroup {
440         short_name: short_name.to_strbuf(),
441         long_name: long_name.to_strbuf(),
442         hint: "".to_strbuf(),
443         desc: desc.to_strbuf(),
444         hasarg: No,
445         occur: Multi
446     }
447 }
448
449 /// Create a long option that is optional and takes an optional argument.
450 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
451     let len = short_name.len();
452     assert!(len == 1 || len == 0);
453     OptGroup {
454         short_name: short_name.to_strbuf(),
455         long_name: long_name.to_strbuf(),
456         hint: hint.to_strbuf(),
457         desc: desc.to_strbuf(),
458         hasarg: Maybe,
459         occur: Optional
460     }
461 }
462
463 /// Create a long option that is optional, takes an argument, and may occur
464 /// multiple times.
465 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
466     let len = short_name.len();
467     assert!(len == 1 || len == 0);
468     OptGroup {
469         short_name: short_name.to_strbuf(),
470         long_name: long_name.to_strbuf(),
471         hint: hint.to_strbuf(),
472         desc: desc.to_strbuf(),
473         hasarg: Yes,
474         occur: Multi
475     }
476 }
477
478 /// Create a generic option group, stating all parameters explicitly
479 pub fn opt(short_name: &str,
480            long_name: &str,
481            desc: &str,
482            hint: &str,
483            hasarg: HasArg,
484            occur: Occur) -> OptGroup {
485     let len = short_name.len();
486     assert!(len == 1 || len == 0);
487     OptGroup {
488         short_name: short_name.to_strbuf(),
489         long_name: long_name.to_strbuf(),
490         hint: hint.to_strbuf(),
491         desc: desc.to_strbuf(),
492         hasarg: hasarg,
493         occur: occur
494     }
495 }
496
497 impl Fail_ {
498     /// Convert a `Fail_` enum into an error string.
499     pub fn to_err_msg(self) -> StrBuf {
500         match self {
501             ArgumentMissing(ref nm) => {
502                 format_strbuf!("Argument to option '{}' missing.", *nm)
503             }
504             UnrecognizedOption(ref nm) => {
505                 format_strbuf!("Unrecognized option: '{}'.", *nm)
506             }
507             OptionMissing(ref nm) => {
508                 format_strbuf!("Required option '{}' missing.", *nm)
509             }
510             OptionDuplicated(ref nm) => {
511                 format_strbuf!("Option '{}' given more than once.", *nm)
512             }
513             UnexpectedArgument(ref nm) => {
514                 format_strbuf!("Option '{}' does not take an argument.", *nm)
515             }
516         }
517     }
518 }
519
520 /// Parse command line arguments according to the provided options.
521 ///
522 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
523 /// `opt_str`, etc. to interrogate results.  Returns `Err(Fail_)` on failure.
524 /// Use `to_err_msg` to get an error message.
525 pub fn getopts(args: &[StrBuf], optgrps: &[OptGroup]) -> Result {
526     let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
527     let n_opts = opts.len();
528
529     fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
530
531     let mut vals = Vec::from_fn(n_opts, f);
532     let mut free: Vec<StrBuf> = Vec::new();
533     let l = args.len();
534     let mut i = 0;
535     while i < l {
536         let cur = args[i].clone();
537         let curlen = cur.len();
538         if !is_arg(cur.as_slice()) {
539             free.push(cur);
540         } else if cur.as_slice() == "--" {
541             let mut j = i + 1;
542             while j < l { free.push(args[j].clone()); j += 1; }
543             break;
544         } else {
545             let mut names;
546             let mut i_arg = None;
547             if cur.as_slice()[1] == '-' as u8 {
548                 let tail = cur.as_slice().slice(2, curlen);
549                 let tail_eq: Vec<&str> = tail.split('=').collect();
550                 if tail_eq.len() <= 1 {
551                     names = vec!(Long(tail.to_strbuf()));
552                 } else {
553                     names =
554                         vec!(Long((*tail_eq.get(0)).to_strbuf()));
555                     i_arg = Some((*tail_eq.get(1)).to_strbuf());
556                 }
557             } else {
558                 let mut j = 1;
559                 let mut last_valid_opt_id = None;
560                 names = Vec::new();
561                 while j < curlen {
562                     let range = cur.as_slice().char_range_at(j);
563                     let opt = Short(range.ch);
564
565                     /* In a series of potential options (eg. -aheJ), if we
566                        see one which takes an argument, we assume all
567                        subsequent characters make up the argument. This
568                        allows options such as -L/usr/local/lib/foo to be
569                        interpreted correctly
570                     */
571
572                     match find_opt(opts.as_slice(), opt.clone()) {
573                       Some(id) => last_valid_opt_id = Some(id),
574                       None => {
575                         let arg_follows =
576                             last_valid_opt_id.is_some() &&
577                             match opts.get(last_valid_opt_id.unwrap())
578                               .hasarg {
579
580                               Yes | Maybe => true,
581                               No => false
582                             };
583                         if arg_follows && j < curlen {
584                             i_arg = Some(cur.as_slice()
585                                             .slice(j, curlen).to_strbuf());
586                             break;
587                         } else {
588                             last_valid_opt_id = None;
589                         }
590                       }
591                     }
592                     names.push(opt);
593                     j = range.next;
594                 }
595             }
596             let mut name_pos = 0;
597             for nm in names.iter() {
598                 name_pos += 1;
599                 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
600                   Some(id) => id,
601                   None => return Err(UnrecognizedOption(nm.to_str()))
602                 };
603                 match opts.get(optid).hasarg {
604                   No => {
605                     if !i_arg.is_none() {
606                         return Err(UnexpectedArgument(nm.to_str()));
607                     }
608                     vals.get_mut(optid).push(Given);
609                   }
610                   Maybe => {
611                     if !i_arg.is_none() {
612                         vals.get_mut(optid)
613                             .push(Val((i_arg.clone())
614                             .unwrap()));
615                     } else if name_pos < names.len() || i + 1 == l ||
616                             is_arg(args[i + 1].as_slice()) {
617                         vals.get_mut(optid).push(Given);
618                     } else {
619                         i += 1;
620                         vals.get_mut(optid).push(Val(args[i].clone()));
621                     }
622                   }
623                   Yes => {
624                     if !i_arg.is_none() {
625                         vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
626                     } else if i + 1 == l {
627                         return Err(ArgumentMissing(nm.to_str()));
628                     } else {
629                         i += 1;
630                         vals.get_mut(optid).push(Val(args[i].clone()));
631                     }
632                   }
633                 }
634             }
635         }
636         i += 1;
637     }
638     i = 0u;
639     while i < n_opts {
640         let n = vals.get(i).len();
641         let occ = opts.get(i).occur;
642         if occ == Req {
643             if n == 0 {
644                 return Err(OptionMissing(opts.get(i).name.to_str()));
645             }
646         }
647         if occ != Multi {
648             if n > 1 {
649                 return Err(OptionDuplicated(opts.get(i).name.to_str()));
650             }
651         }
652         i += 1;
653     }
654     Ok(Matches {
655         opts: opts,
656         vals: vals,
657         free: free
658     })
659 }
660
661 /// Derive a usage message from a set of long options.
662 pub fn usage(brief: &str, opts: &[OptGroup]) -> StrBuf {
663
664     let desc_sep = format!("\n{}", " ".repeat(24));
665
666     let mut rows = opts.iter().map(|optref| {
667         let OptGroup{short_name: short_name,
668                      long_name: long_name,
669                      hint: hint,
670                      desc: desc,
671                      hasarg: hasarg,
672                      ..} = (*optref).clone();
673
674         let mut row = StrBuf::from_owned_str(" ".repeat(4));
675
676         // short option
677         match short_name.len() {
678             0 => {}
679             1 => {
680                 row.push_char('-');
681                 row.push_str(short_name.as_slice());
682                 row.push_char(' ');
683             }
684             _ => fail!("the short name should only be 1 ascii char long"),
685         }
686
687         // long option
688         match long_name.len() {
689             0 => {}
690             _ => {
691                 row.push_str("--");
692                 row.push_str(long_name.as_slice());
693                 row.push_char(' ');
694             }
695         }
696
697         // arg
698         match hasarg {
699             No => {}
700             Yes => row.push_str(hint.as_slice()),
701             Maybe => {
702                 row.push_char('[');
703                 row.push_str(hint.as_slice());
704                 row.push_char(']');
705             }
706         }
707
708         // FIXME: #5516 should be graphemes not codepoints
709         // here we just need to indent the start of the description
710         let rowlen = row.as_slice().char_len();
711         if rowlen < 24 {
712             for _ in range(0, 24 - rowlen) {
713                 row.push_char(' ');
714             }
715         } else {
716             row.push_str(desc_sep.as_slice())
717         }
718
719         // Normalize desc to contain words separated by one space character
720         let mut desc_normalized_whitespace = StrBuf::new();
721         for word in desc.as_slice().words() {
722             desc_normalized_whitespace.push_str(word);
723             desc_normalized_whitespace.push_char(' ');
724         }
725
726         // FIXME: #5516 should be graphemes not codepoints
727         let mut desc_rows = Vec::new();
728         each_split_within(desc_normalized_whitespace.as_slice(),
729                           54,
730                           |substr| {
731             desc_rows.push(substr.to_owned());
732             true
733         });
734
735         // FIXME: #5516 should be graphemes not codepoints
736         // wrapped description
737         row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
738
739         row
740     });
741
742     format_strbuf!("{}\n\nOptions:\n{}\n",
743                    brief,
744                    rows.collect::<Vec<StrBuf>>().connect("\n"))
745 }
746
747 fn format_option(opt: &OptGroup) -> StrBuf {
748     let mut line = StrBuf::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]) -> StrBuf {
786     let mut line = format_strbuf!("Usage: {} ", program_name);
787     line.push_str(opts.iter()
788                       .map(format_option)
789                       .collect::<Vec<StrBuf>>()
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: &[StrBuf]) {
902         let mut v = Vec::new();
903         each_split_within(s, i, |s| { v.push(s.to_strbuf()); 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_strbuf()]);
909     t("\nMary had a little lamb\nLittle lamb\n", 15, [
910         "Mary had a".to_strbuf(),
911         "little lamb".to_strbuf(),
912         "Little lamb".to_strbuf()
913     ]);
914     t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
915         ["Mary had a little lamb\nLittle lamb".to_strbuf()]);
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_strbuf());
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_strbuf());
945             assert!(m.opt_present("t"));
946             assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
947           }
948           _ => { fail!("test_reqopt failed (long arg)"); }
949         }
950         let short_args = vec!("-t".to_strbuf(), "20".to_strbuf());
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_strbuf());
955             assert!((m.opt_present("t")));
956             assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
957           }
958           _ => { fail!("test_reqopt failed (short arg)"); }
959         }
960     }
961
962     #[test]
963     fn test_reqopt_missing() {
964         let args = vec!("blah".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf(), "30".to_strbuf());
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_strbuf());
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_strbuf());
1010             assert!((m.opt_present("t")));
1011             assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1012           }
1013           _ => fail!()
1014         }
1015         let short_args = vec!("-t".to_strbuf(), "20".to_strbuf());
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_strbuf());
1020             assert!((m.opt_present("t")));
1021             assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1022           }
1023           _ => fail!()
1024         }
1025     }
1026
1027     #[test]
1028     fn test_optopt_missing() {
1029         let args = vec!("blah".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf(), "30".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf());
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_strbuf(), "20".to_strbuf());
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_strbuf());
1140           }
1141           _ => fail!()
1142         }
1143     }
1144
1145     // Tests for optflagmulti
1146     #[test]
1147     fn test_optflagmulti_short1() {
1148         let args = vec!("-v".to_strbuf());
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_strbuf(), "-v".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "--verbose".to_strbuf());
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_strbuf(), "-v".to_strbuf(),
1214                         "-vv".to_strbuf(), "verbose".to_strbuf());
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_strbuf());
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_strbuf());
1236             assert!((m.opt_present("t")));
1237             assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1238           }
1239           _ => fail!()
1240         }
1241         let short_args = vec!("-t".to_strbuf(), "20".to_strbuf());
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_strbuf());
1246             assert!((m.opt_present("t")));
1247             assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1248           }
1249           _ => fail!()
1250         }
1251     }
1252
1253     #[test]
1254     fn test_optmulti_missing() {
1255         let args = vec!("blah".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf(), "30".to_strbuf());
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_strbuf());
1292               assert!(m.opt_present("t"));
1293               assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1294               let pair = m.opt_strs("test");
1295               assert!(*pair.get(0) == "20".to_strbuf());
1296               assert!(*pair.get(1) == "30".to_strbuf());
1297           }
1298           _ => fail!()
1299         }
1300     }
1301
1302     #[test]
1303     fn test_unrecognized_option() {
1304         let long_args = vec!("--untest".to_strbuf());
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_strbuf());
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_strbuf(),
1322                  "free1".to_strbuf(),
1323                  "-s".to_strbuf(),
1324                  "20".to_strbuf(),
1325                  "free2".to_strbuf(),
1326                  "--flag".to_strbuf(),
1327                  "--long=30".to_strbuf(),
1328                  "-f".to_strbuf(),
1329                  "-m".to_strbuf(),
1330                  "40".to_strbuf(),
1331                  "-m".to_strbuf(),
1332                  "50".to_strbuf(),
1333                  "-n".to_strbuf(),
1334                  "-A B".to_strbuf(),
1335                  "-n".to_strbuf(),
1336                  "-60 70".to_strbuf());
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_strbuf());
1349             assert!(*m.free.get(1) == "free1".to_strbuf());
1350             assert_eq!(m.opt_str("s").unwrap(), "20".to_strbuf());
1351             assert!(*m.free.get(2) == "free2".to_strbuf());
1352             assert!((m.opt_present("flag")));
1353             assert_eq!(m.opt_str("long").unwrap(), "30".to_strbuf());
1354             assert!((m.opt_present("f")));
1355             let pair = m.opt_strs("m");
1356             assert!(*pair.get(0) == "40".to_strbuf());
1357             assert!(*pair.get(1) == "50".to_strbuf());
1358             let pair = m.opt_strs("n");
1359             assert!(*pair.get(0) == "-A B".to_strbuf());
1360             assert!(*pair.get(1) == "-60 70".to_strbuf());
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_strbuf(), "foo".to_strbuf());
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_strbuf()]));
1380         assert!(matches_single.opts_present(["encrypt".to_strbuf(), "e".to_strbuf()]));
1381         assert!(matches_single.opts_present(["e".to_strbuf(), "encrypt".to_strbuf()]));
1382         assert!(!matches_single.opts_present(["encrypt".to_strbuf()]));
1383         assert!(!matches_single.opts_present(["thing".to_strbuf()]));
1384         assert!(!matches_single.opts_present([]));
1385
1386         assert_eq!(matches_single.opts_str(["e".to_strbuf()]).unwrap(), "foo".to_strbuf());
1387         assert_eq!(matches_single.opts_str(["e".to_strbuf(), "encrypt".to_strbuf()]).unwrap(),
1388                    "foo".to_strbuf());
1389         assert_eq!(matches_single.opts_str(["encrypt".to_strbuf(), "e".to_strbuf()]).unwrap(),
1390                    "foo".to_strbuf());
1391
1392         let args_both = vec!("-e".to_strbuf(), "foo".to_strbuf(), "--encrypt".to_strbuf(),
1393                              "foo".to_strbuf());
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_strbuf()]));
1400         assert!(matches_both.opts_present(["encrypt".to_strbuf()]));
1401         assert!(matches_both.opts_present(["encrypt".to_strbuf(), "e".to_strbuf()]));
1402         assert!(matches_both.opts_present(["e".to_strbuf(), "encrypt".to_strbuf()]));
1403         assert!(!matches_both.opts_present(["f".to_strbuf()]));
1404         assert!(!matches_both.opts_present(["thing".to_strbuf()]));
1405         assert!(!matches_both.opts_present([]));
1406
1407         assert_eq!(matches_both.opts_str(["e".to_strbuf()]).unwrap(), "foo".to_strbuf());
1408         assert_eq!(matches_both.opts_str(["encrypt".to_strbuf()]).unwrap(), "foo".to_strbuf());
1409         assert_eq!(matches_both.opts_str(["e".to_strbuf(), "encrypt".to_strbuf()]).unwrap(),
1410                    "foo".to_strbuf());
1411         assert_eq!(matches_both.opts_str(["encrypt".to_strbuf(), "e".to_strbuf()]).unwrap(),
1412                    "foo".to_strbuf());
1413     }
1414
1415     #[test]
1416     fn test_nospace() {
1417         let args = vec!("-Lfoo".to_strbuf(), "-M.".to_strbuf());
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_strbuf()]));
1425         assert_eq!(matches.opts_str(["L".to_strbuf()]).unwrap(), "foo".to_strbuf());
1426         assert!(matches.opts_present(["M".to_strbuf()]));
1427         assert_eq!(matches.opts_str(["M".to_strbuf()]).unwrap(), ".".to_strbuf());
1428
1429     }
1430
1431     #[test]
1432     fn test_long_to_short() {
1433         let mut short = Opt {
1434             name: Long("banana".to_strbuf()),
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_strbuf(), "--apple".to_strbuf(), "-a".to_strbuf());
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_strbuf();
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_strbuf();
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_strbuf();
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_strbuf();
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 }