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