]> git.lizzy.rs Git - rust.git/blob - src/libgetopts/lib.rs
240f5c007fa24290f372e87e451bac1796cb9bfe
[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();
53 //!
54 //!     let program = args.get(0).clone();
55 //!
56 //!     let opts = [
57 //!         optopt("o", "", "set output file name", "NAME"),
58 //!         optflag("h", "help", "print this help menu")
59 //!     ];
60 //!     let matches = match getopts(args.tail(), opts) {
61 //!         Ok(m) => { m }
62 //!         Err(f) => { fail!(f.to_str()) }
63 //!     };
64 //!     if matches.opt_present("h") {
65 //!         print_usage(program.as_slice(), opts);
66 //!         return;
67 //!     }
68 //!     let output = matches.opt_str("o");
69 //!     let input = if !matches.free.is_empty() {
70 //!         (*matches.free.get(0)).clone()
71 //!     } else {
72 //!         print_usage(program.as_slice(), opts);
73 //!         return;
74 //!     };
75 //!     do_work(input.as_slice(), output);
76 //! }
77 //! ~~~
78
79 #![crate_id = "getopts#0.11.0-pre"]
80 #![experimental]
81 #![crate_type = "rlib"]
82 #![crate_type = "dylib"]
83 #![license = "MIT/ASL2"]
84 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
85        html_favicon_url = "http://www.rust-lang.org/favicon.ico",
86        html_root_url = "http://doc.rust-lang.org/",
87        html_playground_url = "http://play.rust-lang.org/")]
88 #![feature(globs, phase)]
89 #![deny(missing_doc)]
90
91 #[cfg(test)] extern crate debug;
92 #[cfg(test)] #[phase(plugin, link)] extern crate log;
93
94 use std::cmp::PartialEq;
95 use std::fmt;
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, PartialEq, 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, PartialEq, 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, PartialEq, 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, PartialEq, 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, PartialEq, 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 whether an option is given at all or has a value.
165 #[deriving(Clone, PartialEq, 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, PartialEq, 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. Use the `Show` implementation to output detailed
185 /// information.
186 #[deriving(Clone, PartialEq, Eq)]
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(PartialEq, 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(),
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     #[deprecated="use `Show` (`{}` format specifier)"]
501     pub fn to_err_msg(self) -> String {
502         self.to_str()
503     }
504 }
505
506 impl fmt::Show for Fail_ {
507     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
508         match *self {
509             ArgumentMissing(ref nm) => {
510                 write!(f, "Argument to option '{}' missing.", *nm)
511             }
512             UnrecognizedOption(ref nm) => {
513                 write!(f, "Unrecognized option: '{}'.", *nm)
514             }
515             OptionMissing(ref nm) => {
516                 write!(f, "Required option '{}' missing.", *nm)
517             }
518             OptionDuplicated(ref nm) => {
519                 write!(f, "Option '{}' given more than once.", *nm)
520             }
521             UnexpectedArgument(ref nm) => {
522                 write!(f, "Option '{}' does not take an argument.", *nm)
523             }
524         }
525     }
526 }
527
528 /// Parse command line arguments according to the provided options.
529 ///
530 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
531 /// `opt_str`, etc. to interrogate results.  Returns `Err(Fail_)` on
532 /// failure: use the `Show` implementation of `Fail_` to display
533 /// information about it.
534 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
535     let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
536     let n_opts = opts.len();
537
538     fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
539
540     let mut vals = Vec::from_fn(n_opts, f);
541     let mut free: Vec<String> = Vec::new();
542     let l = args.len();
543     let mut i = 0;
544     while i < l {
545         let cur = args[i].clone();
546         let curlen = cur.len();
547         if !is_arg(cur.as_slice()) {
548             free.push(cur);
549         } else if cur.as_slice() == "--" {
550             let mut j = i + 1;
551             while j < l { free.push(args[j].clone()); j += 1; }
552             break;
553         } else {
554             let mut names;
555             let mut i_arg = None;
556             if cur.as_slice()[1] == '-' as u8 {
557                 let tail = cur.as_slice().slice(2, curlen);
558                 let tail_eq: Vec<&str> = tail.split('=').collect();
559                 if tail_eq.len() <= 1 {
560                     names = vec!(Long(tail.to_string()));
561                 } else {
562                     names =
563                         vec!(Long((*tail_eq.get(0)).to_string()));
564                     i_arg = Some((*tail_eq.get(1)).to_string());
565                 }
566             } else {
567                 let mut j = 1;
568                 let mut last_valid_opt_id = None;
569                 names = Vec::new();
570                 while j < curlen {
571                     let range = cur.as_slice().char_range_at(j);
572                     let opt = Short(range.ch);
573
574                     /* In a series of potential options (eg. -aheJ), if we
575                        see one which takes an argument, we assume all
576                        subsequent characters make up the argument. This
577                        allows options such as -L/usr/local/lib/foo to be
578                        interpreted correctly
579                     */
580
581                     match find_opt(opts.as_slice(), opt.clone()) {
582                       Some(id) => last_valid_opt_id = Some(id),
583                       None => {
584                         let arg_follows =
585                             last_valid_opt_id.is_some() &&
586                             match opts.get(last_valid_opt_id.unwrap())
587                               .hasarg {
588
589                               Yes | Maybe => true,
590                               No => false
591                             };
592                         if arg_follows && j < curlen {
593                             i_arg = Some(cur.as_slice()
594                                             .slice(j, curlen).to_string());
595                             break;
596                         } else {
597                             last_valid_opt_id = None;
598                         }
599                       }
600                     }
601                     names.push(opt);
602                     j = range.next;
603                 }
604             }
605             let mut name_pos = 0;
606             for nm in names.iter() {
607                 name_pos += 1;
608                 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
609                   Some(id) => id,
610                   None => return Err(UnrecognizedOption(nm.to_str()))
611                 };
612                 match opts.get(optid).hasarg {
613                   No => {
614                     if !i_arg.is_none() {
615                         return Err(UnexpectedArgument(nm.to_str()));
616                     }
617                     vals.get_mut(optid).push(Given);
618                   }
619                   Maybe => {
620                     if !i_arg.is_none() {
621                         vals.get_mut(optid)
622                             .push(Val((i_arg.clone())
623                             .unwrap()));
624                     } else if name_pos < names.len() || i + 1 == l ||
625                             is_arg(args[i + 1].as_slice()) {
626                         vals.get_mut(optid).push(Given);
627                     } else {
628                         i += 1;
629                         vals.get_mut(optid).push(Val(args[i].clone()));
630                     }
631                   }
632                   Yes => {
633                     if !i_arg.is_none() {
634                         vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
635                     } else if i + 1 == l {
636                         return Err(ArgumentMissing(nm.to_str()));
637                     } else {
638                         i += 1;
639                         vals.get_mut(optid).push(Val(args[i].clone()));
640                     }
641                   }
642                 }
643             }
644         }
645         i += 1;
646     }
647     i = 0u;
648     while i < n_opts {
649         let n = vals.get(i).len();
650         let occ = opts.get(i).occur;
651         if occ == Req {
652             if n == 0 {
653                 return Err(OptionMissing(opts.get(i).name.to_str()));
654             }
655         }
656         if occ != Multi {
657             if n > 1 {
658                 return Err(OptionDuplicated(opts.get(i).name.to_str()));
659             }
660         }
661         i += 1;
662     }
663     Ok(Matches {
664         opts: opts,
665         vals: vals,
666         free: free
667     })
668 }
669
670 /// Derive a usage message from a set of long options.
671 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
672
673     let desc_sep = format!("\n{}", " ".repeat(24));
674
675     let mut rows = opts.iter().map(|optref| {
676         let OptGroup{short_name: short_name,
677                      long_name: long_name,
678                      hint: hint,
679                      desc: desc,
680                      hasarg: hasarg,
681                      ..} = (*optref).clone();
682
683         let mut row = " ".repeat(4);
684
685         // short option
686         match short_name.len() {
687             0 => {}
688             1 => {
689                 row.push_char('-');
690                 row.push_str(short_name.as_slice());
691                 row.push_char(' ');
692             }
693             _ => fail!("the short name should only be 1 ascii char long"),
694         }
695
696         // long option
697         match long_name.len() {
698             0 => {}
699             _ => {
700                 row.push_str("--");
701                 row.push_str(long_name.as_slice());
702                 row.push_char(' ');
703             }
704         }
705
706         // arg
707         match hasarg {
708             No => {}
709             Yes => row.push_str(hint.as_slice()),
710             Maybe => {
711                 row.push_char('[');
712                 row.push_str(hint.as_slice());
713                 row.push_char(']');
714             }
715         }
716
717         // FIXME: #5516 should be graphemes not codepoints
718         // here we just need to indent the start of the description
719         let rowlen = row.as_slice().char_len();
720         if rowlen < 24 {
721             for _ in range(0, 24 - rowlen) {
722                 row.push_char(' ');
723             }
724         } else {
725             row.push_str(desc_sep.as_slice())
726         }
727
728         // Normalize desc to contain words separated by one space character
729         let mut desc_normalized_whitespace = String::new();
730         for word in desc.as_slice().words() {
731             desc_normalized_whitespace.push_str(word);
732             desc_normalized_whitespace.push_char(' ');
733         }
734
735         // FIXME: #5516 should be graphemes not codepoints
736         let mut desc_rows = Vec::new();
737         each_split_within(desc_normalized_whitespace.as_slice(),
738                           54,
739                           |substr| {
740             desc_rows.push(substr.to_string());
741             true
742         });
743
744         // FIXME: #5516 should be graphemes not codepoints
745         // wrapped description
746         row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
747
748         row
749     });
750
751     format!("{}\n\nOptions:\n{}\n", brief,
752             rows.collect::<Vec<String>>().connect("\n"))
753 }
754
755 fn format_option(opt: &OptGroup) -> String {
756     let mut line = String::new();
757
758     if opt.occur != Req {
759         line.push_char('[');
760     }
761
762     // Use short_name is possible, but fallback to long_name.
763     if opt.short_name.len() > 0 {
764         line.push_char('-');
765         line.push_str(opt.short_name.as_slice());
766     } else {
767         line.push_str("--");
768         line.push_str(opt.long_name.as_slice());
769     }
770
771     if opt.hasarg != No {
772         line.push_char(' ');
773         if opt.hasarg == Maybe {
774             line.push_char('[');
775         }
776         line.push_str(opt.hint.as_slice());
777         if opt.hasarg == Maybe {
778             line.push_char(']');
779         }
780     }
781
782     if opt.occur != Req {
783         line.push_char(']');
784     }
785     if opt.occur == Multi {
786         line.push_str("..");
787     }
788
789     line
790 }
791
792 /// Derive a short one-line usage summary from a set of long options.
793 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
794     let mut line = format!("Usage: {} ", program_name);
795     line.push_str(opts.iter()
796                       .map(format_option)
797                       .collect::<Vec<String>>()
798                       .connect(" ")
799                       .as_slice());
800     line
801 }
802
803
804 /// Splits a string into substrings with possibly internal whitespace,
805 /// each of them at most `lim` bytes long. The substrings have leading and trailing
806 /// whitespace removed, and are only cut at whitespace boundaries.
807 ///
808 /// Note: Function was moved here from `std::str` because this module is the only place that
809 /// uses it, and because it was to specific for a general string function.
810 ///
811 /// #Failure:
812 ///
813 /// Fails during iteration if the string contains a non-whitespace
814 /// sequence longer than the limit.
815 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
816                      -> bool {
817     // Just for fun, let's write this as a state machine:
818
819     enum SplitWithinState {
820         A,  // leading whitespace, initial state
821         B,  // words
822         C,  // internal and trailing whitespace
823     }
824     enum Whitespace {
825         Ws, // current char is whitespace
826         Cr  // current char is not whitespace
827     }
828     enum LengthLimit {
829         UnderLim, // current char makes current substring still fit in limit
830         OverLim   // current char makes current substring no longer fit in limit
831     }
832
833     let mut slice_start = 0;
834     let mut last_start = 0;
835     let mut last_end = 0;
836     let mut state = A;
837     let mut fake_i = ss.len();
838     let mut lim = lim;
839
840     let mut cont = true;
841
842     // if the limit is larger than the string, lower it to save cycles
843     if lim >= fake_i {
844         lim = fake_i;
845     }
846
847     let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
848         let whitespace = if ::std::char::is_whitespace(c) { Ws }       else { Cr };
849         let limit      = if (i - slice_start + 1) <= lim  { UnderLim } else { OverLim };
850
851         state = match (state, whitespace, limit) {
852             (A, Ws, _)        => { A }
853             (A, Cr, _)        => { slice_start = i; last_start = i; B }
854
855             (B, Cr, UnderLim) => { B }
856             (B, Cr, OverLim)  if (i - last_start + 1) > lim
857                             => fail!("word starting with {} longer than limit!",
858                                     ss.slice(last_start, i + 1)),
859             (B, Cr, OverLim)  => {
860                 *cont = it(ss.slice(slice_start, last_end));
861                 slice_start = last_start;
862                 B
863             }
864             (B, Ws, UnderLim) => {
865                 last_end = i;
866                 C
867             }
868             (B, Ws, OverLim)  => {
869                 last_end = i;
870                 *cont = it(ss.slice(slice_start, last_end));
871                 A
872             }
873
874             (C, Cr, UnderLim) => {
875                 last_start = i;
876                 B
877             }
878             (C, Cr, OverLim)  => {
879                 *cont = it(ss.slice(slice_start, last_end));
880                 slice_start = i;
881                 last_start = i;
882                 last_end = i;
883                 B
884             }
885             (C, Ws, OverLim)  => {
886                 *cont = it(ss.slice(slice_start, last_end));
887                 A
888             }
889             (C, Ws, UnderLim) => {
890                 C
891             }
892         };
893
894         *cont
895     };
896
897     ss.char_indices().advance(|x| machine(&mut cont, x));
898
899     // Let the automaton 'run out' by supplying trailing whitespace
900     while cont && match state { B | C => true, A => false } {
901         machine(&mut cont, (fake_i, ' '));
902         fake_i += 1;
903     }
904     return cont;
905 }
906
907 #[test]
908 fn test_split_within() {
909     fn t(s: &str, i: uint, u: &[String]) {
910         let mut v = Vec::new();
911         each_split_within(s, i, |s| { v.push(s.to_string()); true });
912         assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
913     }
914     t("", 0, []);
915     t("", 15, []);
916     t("hello", 15, ["hello".to_string()]);
917     t("\nMary had a little lamb\nLittle lamb\n", 15, [
918         "Mary had a".to_string(),
919         "little lamb".to_string(),
920         "Little lamb".to_string()
921     ]);
922     t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
923         ["Mary had a little lamb\nLittle lamb".to_string()]);
924 }
925
926 #[cfg(test)]
927 mod tests {
928     use super::*;
929
930     use std::result::{Err, Ok};
931     use std::result;
932
933     fn check_fail_type(f: Fail_, ft: FailType) {
934         match f {
935           ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
936           UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
937           OptionMissing(_) => assert!(ft == OptionMissing_),
938           OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
939           UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
940         }
941     }
942
943     // Tests for reqopt
944     #[test]
945     fn test_reqopt() {
946         let long_args = vec!("--test=20".to_string());
947         let opts = vec!(reqopt("t", "test", "testing", "TEST"));
948         let rs = getopts(long_args.as_slice(), opts.as_slice());
949         match rs {
950           Ok(ref m) => {
951             assert!(m.opt_present("test"));
952             assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
953             assert!(m.opt_present("t"));
954             assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
955           }
956           _ => { fail!("test_reqopt failed (long arg)"); }
957         }
958         let short_args = vec!("-t".to_string(), "20".to_string());
959         match getopts(short_args.as_slice(), opts.as_slice()) {
960           Ok(ref m) => {
961             assert!((m.opt_present("test")));
962             assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
963             assert!((m.opt_present("t")));
964             assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
965           }
966           _ => { fail!("test_reqopt failed (short arg)"); }
967         }
968     }
969
970     #[test]
971     fn test_reqopt_missing() {
972         let args = vec!("blah".to_string());
973         let opts = vec!(reqopt("t", "test", "testing", "TEST"));
974         let rs = getopts(args.as_slice(), opts.as_slice());
975         match rs {
976           Err(f) => check_fail_type(f, OptionMissing_),
977           _ => fail!()
978         }
979     }
980
981     #[test]
982     fn test_reqopt_no_arg() {
983         let long_args = vec!("--test".to_string());
984         let opts = vec!(reqopt("t", "test", "testing", "TEST"));
985         let rs = getopts(long_args.as_slice(), opts.as_slice());
986         match rs {
987           Err(f) => check_fail_type(f, ArgumentMissing_),
988           _ => fail!()
989         }
990         let short_args = vec!("-t".to_string());
991         match getopts(short_args.as_slice(), opts.as_slice()) {
992           Err(f) => check_fail_type(f, ArgumentMissing_),
993           _ => fail!()
994         }
995     }
996
997     #[test]
998     fn test_reqopt_multi() {
999         let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1000         let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1001         let rs = getopts(args.as_slice(), opts.as_slice());
1002         match rs {
1003           Err(f) => check_fail_type(f, OptionDuplicated_),
1004           _ => fail!()
1005         }
1006     }
1007
1008     // Tests for optopt
1009     #[test]
1010     fn test_optopt() {
1011         let long_args = vec!("--test=20".to_string());
1012         let opts = vec!(optopt("t", "test", "testing", "TEST"));
1013         let rs = getopts(long_args.as_slice(), opts.as_slice());
1014         match rs {
1015           Ok(ref m) => {
1016             assert!(m.opt_present("test"));
1017             assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1018             assert!((m.opt_present("t")));
1019             assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1020           }
1021           _ => fail!()
1022         }
1023         let short_args = vec!("-t".to_string(), "20".to_string());
1024         match getopts(short_args.as_slice(), opts.as_slice()) {
1025           Ok(ref m) => {
1026             assert!((m.opt_present("test")));
1027             assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1028             assert!((m.opt_present("t")));
1029             assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1030           }
1031           _ => fail!()
1032         }
1033     }
1034
1035     #[test]
1036     fn test_optopt_missing() {
1037         let args = vec!("blah".to_string());
1038         let opts = vec!(optopt("t", "test", "testing", "TEST"));
1039         let rs = getopts(args.as_slice(), opts.as_slice());
1040         match rs {
1041           Ok(ref m) => {
1042             assert!(!m.opt_present("test"));
1043             assert!(!m.opt_present("t"));
1044           }
1045           _ => fail!()
1046         }
1047     }
1048
1049     #[test]
1050     fn test_optopt_no_arg() {
1051         let long_args = vec!("--test".to_string());
1052         let opts = vec!(optopt("t", "test", "testing", "TEST"));
1053         let rs = getopts(long_args.as_slice(), opts.as_slice());
1054         match rs {
1055           Err(f) => check_fail_type(f, ArgumentMissing_),
1056           _ => fail!()
1057         }
1058         let short_args = vec!("-t".to_string());
1059         match getopts(short_args.as_slice(), opts.as_slice()) {
1060           Err(f) => check_fail_type(f, ArgumentMissing_),
1061           _ => fail!()
1062         }
1063     }
1064
1065     #[test]
1066     fn test_optopt_multi() {
1067         let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1068         let opts = vec!(optopt("t", "test", "testing", "TEST"));
1069         let rs = getopts(args.as_slice(), opts.as_slice());
1070         match rs {
1071           Err(f) => check_fail_type(f, OptionDuplicated_),
1072           _ => fail!()
1073         }
1074     }
1075
1076     // Tests for optflag
1077     #[test]
1078     fn test_optflag() {
1079         let long_args = vec!("--test".to_string());
1080         let opts = vec!(optflag("t", "test", "testing"));
1081         let rs = getopts(long_args.as_slice(), opts.as_slice());
1082         match rs {
1083           Ok(ref m) => {
1084             assert!(m.opt_present("test"));
1085             assert!(m.opt_present("t"));
1086           }
1087           _ => fail!()
1088         }
1089         let short_args = vec!("-t".to_string());
1090         match getopts(short_args.as_slice(), opts.as_slice()) {
1091           Ok(ref m) => {
1092             assert!(m.opt_present("test"));
1093             assert!(m.opt_present("t"));
1094           }
1095           _ => fail!()
1096         }
1097     }
1098
1099     #[test]
1100     fn test_optflag_missing() {
1101         let args = vec!("blah".to_string());
1102         let opts = vec!(optflag("t", "test", "testing"));
1103         let rs = getopts(args.as_slice(), opts.as_slice());
1104         match rs {
1105           Ok(ref m) => {
1106             assert!(!m.opt_present("test"));
1107             assert!(!m.opt_present("t"));
1108           }
1109           _ => fail!()
1110         }
1111     }
1112
1113     #[test]
1114     fn test_optflag_long_arg() {
1115         let args = vec!("--test=20".to_string());
1116         let opts = vec!(optflag("t", "test", "testing"));
1117         let rs = getopts(args.as_slice(), opts.as_slice());
1118         match rs {
1119           Err(f) => {
1120             check_fail_type(f, UnexpectedArgument_);
1121           }
1122           _ => fail!()
1123         }
1124     }
1125
1126     #[test]
1127     fn test_optflag_multi() {
1128         let args = vec!("--test".to_string(), "-t".to_string());
1129         let opts = vec!(optflag("t", "test", "testing"));
1130         let rs = getopts(args.as_slice(), opts.as_slice());
1131         match rs {
1132           Err(f) => check_fail_type(f, OptionDuplicated_),
1133           _ => fail!()
1134         }
1135     }
1136
1137     #[test]
1138     fn test_optflag_short_arg() {
1139         let args = vec!("-t".to_string(), "20".to_string());
1140         let opts = vec!(optflag("t", "test", "testing"));
1141         let rs = getopts(args.as_slice(), opts.as_slice());
1142         match rs {
1143           Ok(ref m) => {
1144             // The next variable after the flag is just a free argument
1145
1146             assert!(*m.free.get(0) == "20".to_string());
1147           }
1148           _ => fail!()
1149         }
1150     }
1151
1152     // Tests for optflagmulti
1153     #[test]
1154     fn test_optflagmulti_short1() {
1155         let args = vec!("-v".to_string());
1156         let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1157         let rs = getopts(args.as_slice(), opts.as_slice());
1158         match rs {
1159           Ok(ref m) => {
1160             assert_eq!(m.opt_count("v"), 1);
1161           }
1162           _ => fail!()
1163         }
1164     }
1165
1166     #[test]
1167     fn test_optflagmulti_short2a() {
1168         let args = vec!("-v".to_string(), "-v".to_string());
1169         let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1170         let rs = getopts(args.as_slice(), opts.as_slice());
1171         match rs {
1172           Ok(ref m) => {
1173             assert_eq!(m.opt_count("v"), 2);
1174           }
1175           _ => fail!()
1176         }
1177     }
1178
1179     #[test]
1180     fn test_optflagmulti_short2b() {
1181         let args = vec!("-vv".to_string());
1182         let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1183         let rs = getopts(args.as_slice(), opts.as_slice());
1184         match rs {
1185           Ok(ref m) => {
1186             assert_eq!(m.opt_count("v"), 2);
1187           }
1188           _ => fail!()
1189         }
1190     }
1191
1192     #[test]
1193     fn test_optflagmulti_long1() {
1194         let args = vec!("--verbose".to_string());
1195         let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1196         let rs = getopts(args.as_slice(), opts.as_slice());
1197         match rs {
1198           Ok(ref m) => {
1199             assert_eq!(m.opt_count("verbose"), 1);
1200           }
1201           _ => fail!()
1202         }
1203     }
1204
1205     #[test]
1206     fn test_optflagmulti_long2() {
1207         let args = vec!("--verbose".to_string(), "--verbose".to_string());
1208         let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1209         let rs = getopts(args.as_slice(), opts.as_slice());
1210         match rs {
1211           Ok(ref m) => {
1212             assert_eq!(m.opt_count("verbose"), 2);
1213           }
1214           _ => fail!()
1215         }
1216     }
1217
1218     #[test]
1219     fn test_optflagmulti_mix() {
1220         let args = vec!("--verbose".to_string(), "-v".to_string(),
1221                         "-vv".to_string(), "verbose".to_string());
1222         let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1223         let rs = getopts(args.as_slice(), opts.as_slice());
1224         match rs {
1225           Ok(ref m) => {
1226             assert_eq!(m.opt_count("verbose"), 4);
1227             assert_eq!(m.opt_count("v"), 4);
1228           }
1229           _ => fail!()
1230         }
1231     }
1232
1233     // Tests for optmulti
1234     #[test]
1235     fn test_optmulti() {
1236         let long_args = vec!("--test=20".to_string());
1237         let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1238         let rs = getopts(long_args.as_slice(), opts.as_slice());
1239         match rs {
1240           Ok(ref m) => {
1241             assert!((m.opt_present("test")));
1242             assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1243             assert!((m.opt_present("t")));
1244             assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1245           }
1246           _ => fail!()
1247         }
1248         let short_args = vec!("-t".to_string(), "20".to_string());
1249         match getopts(short_args.as_slice(), opts.as_slice()) {
1250           Ok(ref m) => {
1251             assert!((m.opt_present("test")));
1252             assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1253             assert!((m.opt_present("t")));
1254             assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1255           }
1256           _ => fail!()
1257         }
1258     }
1259
1260     #[test]
1261     fn test_optmulti_missing() {
1262         let args = vec!("blah".to_string());
1263         let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1264         let rs = getopts(args.as_slice(), opts.as_slice());
1265         match rs {
1266           Ok(ref m) => {
1267             assert!(!m.opt_present("test"));
1268             assert!(!m.opt_present("t"));
1269           }
1270           _ => fail!()
1271         }
1272     }
1273
1274     #[test]
1275     fn test_optmulti_no_arg() {
1276         let long_args = vec!("--test".to_string());
1277         let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1278         let rs = getopts(long_args.as_slice(), opts.as_slice());
1279         match rs {
1280           Err(f) => check_fail_type(f, ArgumentMissing_),
1281           _ => fail!()
1282         }
1283         let short_args = vec!("-t".to_string());
1284         match getopts(short_args.as_slice(), opts.as_slice()) {
1285           Err(f) => check_fail_type(f, ArgumentMissing_),
1286           _ => fail!()
1287         }
1288     }
1289
1290     #[test]
1291     fn test_optmulti_multi() {
1292         let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1293         let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1294         let rs = getopts(args.as_slice(), opts.as_slice());
1295         match rs {
1296           Ok(ref m) => {
1297               assert!(m.opt_present("test"));
1298               assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1299               assert!(m.opt_present("t"));
1300               assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1301               let pair = m.opt_strs("test");
1302               assert!(*pair.get(0) == "20".to_string());
1303               assert!(*pair.get(1) == "30".to_string());
1304           }
1305           _ => fail!()
1306         }
1307     }
1308
1309     #[test]
1310     fn test_unrecognized_option() {
1311         let long_args = vec!("--untest".to_string());
1312         let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1313         let rs = getopts(long_args.as_slice(), opts.as_slice());
1314         match rs {
1315           Err(f) => check_fail_type(f, UnrecognizedOption_),
1316           _ => fail!()
1317         }
1318         let short_args = vec!("-u".to_string());
1319         match getopts(short_args.as_slice(), opts.as_slice()) {
1320           Err(f) => check_fail_type(f, UnrecognizedOption_),
1321           _ => fail!()
1322         }
1323     }
1324
1325     #[test]
1326     fn test_combined() {
1327         let args =
1328             vec!("prog".to_string(),
1329                  "free1".to_string(),
1330                  "-s".to_string(),
1331                  "20".to_string(),
1332                  "free2".to_string(),
1333                  "--flag".to_string(),
1334                  "--long=30".to_string(),
1335                  "-f".to_string(),
1336                  "-m".to_string(),
1337                  "40".to_string(),
1338                  "-m".to_string(),
1339                  "50".to_string(),
1340                  "-n".to_string(),
1341                  "-A B".to_string(),
1342                  "-n".to_string(),
1343                  "-60 70".to_string());
1344         let opts =
1345             vec!(optopt("s", "something", "something", "SOMETHING"),
1346               optflag("", "flag", "a flag"),
1347               reqopt("", "long", "hi", "LONG"),
1348               optflag("f", "", "another flag"),
1349               optmulti("m", "", "mmmmmm", "YUM"),
1350               optmulti("n", "", "nothing", "NOTHING"),
1351               optopt("", "notpresent", "nothing to see here", "NOPE"));
1352         let rs = getopts(args.as_slice(), opts.as_slice());
1353         match rs {
1354           Ok(ref m) => {
1355             assert!(*m.free.get(0) == "prog".to_string());
1356             assert!(*m.free.get(1) == "free1".to_string());
1357             assert_eq!(m.opt_str("s").unwrap(), "20".to_string());
1358             assert!(*m.free.get(2) == "free2".to_string());
1359             assert!((m.opt_present("flag")));
1360             assert_eq!(m.opt_str("long").unwrap(), "30".to_string());
1361             assert!((m.opt_present("f")));
1362             let pair = m.opt_strs("m");
1363             assert!(*pair.get(0) == "40".to_string());
1364             assert!(*pair.get(1) == "50".to_string());
1365             let pair = m.opt_strs("n");
1366             assert!(*pair.get(0) == "-A B".to_string());
1367             assert!(*pair.get(1) == "-60 70".to_string());
1368             assert!((!m.opt_present("notpresent")));
1369           }
1370           _ => fail!()
1371         }
1372     }
1373
1374     #[test]
1375     fn test_multi() {
1376         let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1377                      optopt("", "encrypt", "encrypt", "ENCRYPT"),
1378                      optopt("f", "", "flag", "FLAG"));
1379
1380         let args_single = vec!("-e".to_string(), "foo".to_string());
1381         let matches_single = &match getopts(args_single.as_slice(),
1382                                             opts.as_slice()) {
1383           result::Ok(m) => m,
1384           result::Err(_) => fail!()
1385         };
1386         assert!(matches_single.opts_present(["e".to_string()]));
1387         assert!(matches_single.opts_present(["encrypt".to_string(), "e".to_string()]));
1388         assert!(matches_single.opts_present(["e".to_string(), "encrypt".to_string()]));
1389         assert!(!matches_single.opts_present(["encrypt".to_string()]));
1390         assert!(!matches_single.opts_present(["thing".to_string()]));
1391         assert!(!matches_single.opts_present([]));
1392
1393         assert_eq!(matches_single.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1394         assert_eq!(matches_single.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1395                    "foo".to_string());
1396         assert_eq!(matches_single.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1397                    "foo".to_string());
1398
1399         let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1400                              "foo".to_string());
1401         let matches_both = &match getopts(args_both.as_slice(),
1402                                           opts.as_slice()) {
1403           result::Ok(m) => m,
1404           result::Err(_) => fail!()
1405         };
1406         assert!(matches_both.opts_present(["e".to_string()]));
1407         assert!(matches_both.opts_present(["encrypt".to_string()]));
1408         assert!(matches_both.opts_present(["encrypt".to_string(), "e".to_string()]));
1409         assert!(matches_both.opts_present(["e".to_string(), "encrypt".to_string()]));
1410         assert!(!matches_both.opts_present(["f".to_string()]));
1411         assert!(!matches_both.opts_present(["thing".to_string()]));
1412         assert!(!matches_both.opts_present([]));
1413
1414         assert_eq!(matches_both.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1415         assert_eq!(matches_both.opts_str(["encrypt".to_string()]).unwrap(), "foo".to_string());
1416         assert_eq!(matches_both.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1417                    "foo".to_string());
1418         assert_eq!(matches_both.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1419                    "foo".to_string());
1420     }
1421
1422     #[test]
1423     fn test_nospace() {
1424         let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1425         let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1426                      optmulti("M", "", "something", "MMMM"));
1427         let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1428           result::Ok(m) => m,
1429           result::Err(_) => fail!()
1430         };
1431         assert!(matches.opts_present(["L".to_string()]));
1432         assert_eq!(matches.opts_str(["L".to_string()]).unwrap(), "foo".to_string());
1433         assert!(matches.opts_present(["M".to_string()]));
1434         assert_eq!(matches.opts_str(["M".to_string()]).unwrap(), ".".to_string());
1435
1436     }
1437
1438     #[test]
1439     fn test_long_to_short() {
1440         let mut short = Opt {
1441             name: Long("banana".to_string()),
1442             hasarg: Yes,
1443             occur: Req,
1444             aliases: Vec::new(),
1445         };
1446         short.aliases = vec!(Opt { name: Short('b'),
1447                                 hasarg: Yes,
1448                                 occur: Req,
1449                                 aliases: Vec::new() });
1450         let verbose = reqopt("b", "banana", "some bananas", "VAL");
1451
1452         assert!(verbose.long_to_short() == short);
1453     }
1454
1455     #[test]
1456     fn test_aliases_long_and_short() {
1457         let opts = vec!(
1458             optflagmulti("a", "apple", "Desc"));
1459
1460         let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1461
1462         let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1463         assert_eq!(3, matches.opt_count("a"));
1464         assert_eq!(3, matches.opt_count("apple"));
1465     }
1466
1467     #[test]
1468     fn test_usage() {
1469         let optgroups = vec!(
1470             reqopt("b", "banana", "Desc", "VAL"),
1471             optopt("a", "012345678901234567890123456789",
1472                              "Desc", "VAL"),
1473             optflag("k", "kiwi", "Desc"),
1474             optflagopt("p", "", "Desc", "VAL"),
1475             optmulti("l", "", "Desc", "VAL"));
1476
1477         let expected =
1478 "Usage: fruits
1479
1480 Options:
1481     -b --banana VAL     Desc
1482     -a --012345678901234567890123456789 VAL
1483                         Desc
1484     -k --kiwi           Desc
1485     -p [VAL]            Desc
1486     -l VAL              Desc
1487 ".to_string();
1488
1489         let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1490
1491         debug!("expected: <<{}>>", expected);
1492         debug!("generated: <<{}>>", generated_usage);
1493         assert_eq!(generated_usage, expected);
1494     }
1495
1496     #[test]
1497     fn test_usage_description_wrapping() {
1498         // indentation should be 24 spaces
1499         // lines wrap after 78: or rather descriptions wrap after 54
1500
1501         let optgroups = vec!(
1502             optflag("k", "kiwi",
1503                 "This is a long description which won't be wrapped..+.."), // 54
1504             optflag("a", "apple",
1505                 "This is a long description which _will_ be wrapped..+.."));
1506
1507         let expected =
1508 "Usage: fruits
1509
1510 Options:
1511     -k --kiwi           This is a long description which won't be wrapped..+..
1512     -a --apple          This is a long description which _will_ be
1513                         wrapped..+..
1514 ".to_string();
1515
1516         let usage = usage("Usage: fruits", optgroups.as_slice());
1517
1518         debug!("expected: <<{}>>", expected);
1519         debug!("generated: <<{}>>", usage);
1520         assert!(usage == expected)
1521     }
1522
1523     #[test]
1524     fn test_usage_description_multibyte_handling() {
1525         let optgroups = vec!(
1526             optflag("k", "k\u2013w\u2013",
1527                 "The word kiwi is normally spelled with two i's"),
1528             optflag("a", "apple",
1529                 "This \u201Cdescription\u201D has some characters that could \
1530 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1531
1532         let expected =
1533 "Usage: fruits
1534
1535 Options:
1536     -k --k–w–           The word kiwi is normally spelled with two i's
1537     -a --apple          This “description” has some characters that could
1538                         confuse the line wrapping; an apple costs 0.51€ in
1539                         some parts of Europe.
1540 ".to_string();
1541
1542         let usage = usage("Usage: fruits", optgroups.as_slice());
1543
1544         debug!("expected: <<{}>>", expected);
1545         debug!("generated: <<{}>>", usage);
1546         assert!(usage == expected)
1547     }
1548
1549     #[test]
1550     fn test_short_usage() {
1551         let optgroups = vec!(
1552             reqopt("b", "banana", "Desc", "VAL"),
1553             optopt("a", "012345678901234567890123456789",
1554                      "Desc", "VAL"),
1555             optflag("k", "kiwi", "Desc"),
1556             optflagopt("p", "", "Desc", "VAL"),
1557             optmulti("l", "", "Desc", "VAL"));
1558
1559         let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1560         let generated_usage = short_usage("fruits", optgroups.as_slice());
1561
1562         debug!("expected: <<{}>>", expected);
1563         debug!("generated: <<{}>>", generated_usage);
1564         assert_eq!(generated_usage, expected);
1565     }
1566 }