getopts/
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
16//! `optflag` or by building them from components yourself, and pass them to
17//! `getopts`, along with a vector of actual arguments (not including
18//! `argv[0]`). You'll either get a failure code back, or a match. You'll have
19//! to verify whether the amount of 'free' arguments in the match is what you
20//! expect. Use `opt_*` accessors to get argument values out of the matches
21//! object.
22//!
23//! Single-character options are expected to appear on the command line with a
24//! single preceding dash; multiple-character options are expected to be
25//! proceeded by two dashes. Options that expect an argument accept their
26//! argument following either a space or an equals sign. Single-character
27//! options don't require the space.
28//!
29//! # Usage
30//!
31//! This crate is [on crates.io](https://crates.io/crates/getopts) and can be
32//! used by adding `getopts` to the dependencies in your project's `Cargo.toml`.
33//!
34//! ```toml
35//! [dependencies]
36//! getopts = "0.2"
37//! ```
38//!
39//! and this to your crate root:
40//!
41//! ```rust
42//! extern crate getopts;
43//! ```
44//!
45//! # Example
46//!
47//! The following example shows simple command line parsing for an application
48//! that requires an input file to be specified, accepts an optional output file
49//! name following `-o`, and accepts both `-h` and `--help` as optional flags.
50//!
51//! ```{.rust}
52//! extern crate getopts;
53//! use getopts::Options;
54//! use std::env;
55//!
56//! fn do_work(inp: &str, out: Option<String>) {
57//!     println!("{}", inp);
58//!     match out {
59//!         Some(x) => println!("{}", x),
60//!         None => println!("No Output"),
61//!     }
62//! }
63//!
64//! fn print_usage(program: &str, opts: Options) {
65//!     let brief = format!("Usage: {} FILE [options]", program);
66//!     print!("{}", opts.usage(&brief));
67//! }
68//!
69//! fn main() {
70//!     let args: Vec<String> = env::args().collect();
71//!     let program = args[0].clone();
72//!
73//!     let mut opts = Options::new();
74//!     opts.optopt("o", "", "set output file name", "NAME");
75//!     opts.optflag("h", "help", "print this help menu");
76//!     let matches = match opts.parse(&args[1..]) {
77//!         Ok(m) => { m }
78//!         Err(f) => { panic!(f.to_string()) }
79//!     };
80//!     if matches.opt_present("h") {
81//!         print_usage(&program, opts);
82//!         return;
83//!     }
84//!     let output = matches.opt_str("o");
85//!     let input = if !matches.free.is_empty() {
86//!         matches.free[0].clone()
87//!     } else {
88//!         print_usage(&program, opts);
89//!         return;
90//!     };
91//!     do_work(&input, output);
92//! }
93//! ```
94
95#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
96       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
97       html_root_url = "https://docs.rs/getopts/0.2.18")]
98#![deny(missing_docs)]
99#![cfg_attr(test, deny(warnings))]
100#![cfg_attr(rust_build, feature(staged_api))]
101#![cfg_attr(rust_build, staged_api)]
102#![cfg_attr(rust_build,
103            unstable(feature = "rustc_private",
104                     reason = "use the crates.io `getopts` library instead"))]
105
106#[cfg(test)] #[macro_use] extern crate log;
107extern crate unicode_width;
108
109
110use self::Name::*;
111use self::HasArg::*;
112use self::Occur::*;
113use self::Fail::*;
114use self::Optval::*;
115
116use std::error::Error;
117use std::ffi::OsStr;
118use std::fmt;
119use std::iter::{repeat, IntoIterator};
120use std::result;
121use std::str::FromStr;
122
123use unicode_width::UnicodeWidthStr;
124
125/// A description of the options that a program can handle.
126pub struct Options {
127    grps: Vec<OptGroup>,
128    parsing_style : ParsingStyle,
129    long_only: bool
130}
131
132impl Options {
133    /// Create a blank set of options.
134    pub fn new() -> Options {
135        Options {
136            grps: Vec::new(),
137            parsing_style: ParsingStyle::FloatingFrees,
138            long_only: false
139        }
140    }
141
142    /// Set the parsing style.
143    pub fn parsing_style(&mut self, style: ParsingStyle) -> &mut Options {
144        self.parsing_style = style;
145        self
146    }
147
148    /// Set or clear "long options only" mode.
149    ///
150    /// In "long options only" mode, short options cannot be clustered
151    /// together, and long options can be given with either a single
152    /// "-" or the customary "--".  This mode also changes the meaning
153    /// of "-a=b"; in the ordinary mode this will parse a short option
154    /// "-a" with argument "=b"; whereas in long-options-only mode the
155    /// argument will be simply "b".
156    pub fn long_only(&mut self, long_only: bool) -> &mut Options {
157        self.long_only = long_only;
158        self
159    }
160
161    /// Create a generic option group, stating all parameters explicitly.
162    pub fn opt(&mut self, short_name: &str, long_name: &str, desc: &str,
163                       hint: &str, hasarg: HasArg, occur: Occur) -> &mut Options {
164        validate_names(short_name, long_name);
165        self.grps.push(OptGroup {
166            short_name: short_name.to_string(),
167            long_name: long_name.to_string(),
168            hint: hint.to_string(),
169            desc: desc.to_string(),
170            hasarg: hasarg,
171            occur: occur
172        });
173        self
174    }
175
176    /// Create a long option that is optional and does not take an argument.
177    ///
178    /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
179    /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
180    /// * `desc` - Description for usage help
181    pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str)
182                           -> &mut Options {
183        validate_names(short_name, long_name);
184        self.grps.push(OptGroup {
185            short_name: short_name.to_string(),
186            long_name: long_name.to_string(),
187            hint: "".to_string(),
188            desc: desc.to_string(),
189            hasarg: No,
190            occur: Optional
191        });
192        self
193    }
194
195    /// Create a long option that can occur more than once and does not
196    /// take an argument.
197    ///
198    /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
199    /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
200    /// * `desc` - Description for usage help
201    pub fn optflagmulti(&mut self, short_name: &str, long_name: &str, desc: &str)
202                                -> &mut Options {
203        validate_names(short_name, long_name);
204        self.grps.push(OptGroup {
205            short_name: short_name.to_string(),
206            long_name: long_name.to_string(),
207            hint: "".to_string(),
208            desc: desc.to_string(),
209            hasarg: No,
210            occur: Multi
211        });
212        self
213    }
214
215    /// Create a long option that is optional and takes an optional argument.
216    ///
217    /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
218    /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
219    /// * `desc` - Description for usage help
220    /// * `hint` - Hint that is used in place of the argument in the usage help,
221    ///   e.g. `"FILE"` for a `-o FILE` option
222    pub fn optflagopt(&mut self, short_name: &str, long_name: &str, desc: &str,
223                              hint: &str) -> &mut Options {
224        validate_names(short_name, long_name);
225        self.grps.push(OptGroup {
226            short_name: short_name.to_string(),
227            long_name: long_name.to_string(),
228            hint: hint.to_string(),
229            desc: desc.to_string(),
230            hasarg: Maybe,
231            occur: Optional
232        });
233        self
234    }
235
236    /// Create a long option that is optional, takes an argument, and may occur
237    /// multiple times.
238    ///
239    /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
240    /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
241    /// * `desc` - Description for usage help
242    /// * `hint` - Hint that is used in place of the argument in the usage help,
243    ///   e.g. `"FILE"` for a `-o FILE` option
244    pub fn optmulti(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str)
245                            -> &mut Options {
246        validate_names(short_name, long_name);
247        self.grps.push(OptGroup {
248            short_name: short_name.to_string(),
249            long_name: long_name.to_string(),
250            hint: hint.to_string(),
251            desc: desc.to_string(),
252            hasarg: Yes,
253            occur: Multi
254        });
255        self
256    }
257
258    /// Create a long option that is optional and takes an argument.
259    ///
260    /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
261    /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
262    /// * `desc` - Description for usage help
263    /// * `hint` - Hint that is used in place of the argument in the usage help,
264    ///   e.g. `"FILE"` for a `-o FILE` option
265    pub fn optopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str)
266                          -> &mut Options {
267        validate_names(short_name, long_name);
268        self.grps.push(OptGroup {
269            short_name: short_name.to_string(),
270            long_name: long_name.to_string(),
271            hint: hint.to_string(),
272            desc: desc.to_string(),
273            hasarg: Yes,
274            occur: Optional
275        });
276        self
277    }
278
279    /// Create a long option that is required and takes an argument.
280    ///
281    /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
282    /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
283    /// * `desc` - Description for usage help
284    /// * `hint` - Hint that is used in place of the argument in the usage help,
285    ///   e.g. `"FILE"` for a `-o FILE` option
286    pub fn reqopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str)
287                          -> &mut Options {
288        validate_names(short_name, long_name);
289        self.grps.push(OptGroup {
290            short_name: short_name.to_string(),
291            long_name: long_name.to_string(),
292            hint: hint.to_string(),
293            desc: desc.to_string(),
294            hasarg: Yes,
295            occur: Req
296        });
297        self
298    }
299
300    /// Parse command line arguments according to the provided options.
301    ///
302    /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
303    /// `opt_str`, etc. to interrogate results.
304    /// # Panics
305    ///
306    /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail`
307    /// to display information about it.
308    pub fn parse<C: IntoIterator>(&self, args: C) -> Result
309        where C::Item: AsRef<OsStr>
310    {
311        let opts: Vec<Opt> = self.grps.iter().map(|x| x.long_to_short()).collect();
312
313        let mut vals = (0 .. opts.len()).map(|_| Vec::new()).collect::<Vec<Vec<Optval>>>();
314        let mut free: Vec<String> = Vec::new();
315        let args = args.into_iter().map(|i| {
316            i.as_ref().to_str().ok_or_else(|| {
317                Fail::UnrecognizedOption(format!("{:?}", i.as_ref()))
318            }).map(|s| s.to_owned())
319        }).collect::<::std::result::Result<Vec<_>,_>>()?;
320        let mut args = args.into_iter().peekable();
321        while let Some(cur) = args.next() {
322            if !is_arg(&cur) {
323                free.push(cur);
324                match self.parsing_style {
325                    ParsingStyle::FloatingFrees => {},
326                    ParsingStyle::StopAtFirstFree => {
327                        free.extend(args);
328                        break;
329                    }
330                }
331            } else if cur == "--" {
332                free.extend(args);
333                break;
334            } else {
335                let mut names;
336                let mut i_arg = None;
337                let mut was_long = true;
338                if cur.as_bytes()[1] == b'-' || self.long_only {
339                    let tail = if cur.as_bytes()[1] == b'-' {
340                        &cur[2..]
341                    } else {
342                        assert!(self.long_only);
343                        &cur[1..]
344                    };
345                    let mut parts = tail.splitn(2, '=');
346                    names = vec![Name::from_str(parts.next().unwrap())];
347                    if let Some(rest) = parts.next() {
348                        i_arg = Some(rest.to_string());
349                    }
350                } else {
351                    was_long = false;
352                    names = Vec::new();
353                    for (j, ch) in cur.char_indices().skip(1) {
354                        let opt = Short(ch);
355
356                        /* In a series of potential options (eg. -aheJ), if we
357                           see one which takes an argument, we assume all
358                           subsequent characters make up the argument. This
359                           allows options such as -L/usr/local/lib/foo to be
360                           interpreted correctly
361                        */
362
363                        let opt_id = match find_opt(&opts, &opt) {
364                          Some(id) => id,
365                          None => return Err(UnrecognizedOption(opt.to_string()))
366                        };
367
368                        names.push(opt);
369
370                        let arg_follows = match opts[opt_id].hasarg {
371                            Yes | Maybe => true,
372                            No => false
373                        };
374
375                        if arg_follows {
376                            let next = j + ch.len_utf8();
377                            if next < cur.len() {
378                                i_arg = Some(cur[next..].to_string());
379                                break;
380                            }
381                        }
382                    }
383                }
384                let mut name_pos = 0;
385                for nm in names.iter() {
386                    name_pos += 1;
387                    let optid = match find_opt(&opts, &nm) {
388                      Some(id) => id,
389                      None => return Err(UnrecognizedOption(nm.to_string()))
390                    };
391                    match opts[optid].hasarg {
392                      No => {
393                        if name_pos == names.len() && !i_arg.is_none() {
394                            return Err(UnexpectedArgument(nm.to_string()));
395                        }
396                        vals[optid].push(Given);
397                      }
398                      Maybe => {
399                        // Note that here we do not handle `--arg value`.
400                        // This matches GNU getopt behavior; but also
401                        // makes sense, because if this were accepted,
402                        // then users could only write a "Maybe" long
403                        // option at the end of the arguments when
404                        // FloatingFrees is in use.
405                        if let Some(i_arg) = i_arg.take() {
406                            vals[optid].push(Val(i_arg));
407                        } else if was_long || name_pos < names.len() || args.peek().map_or(true, |n| is_arg(&n)) {
408                            vals[optid].push(Given);
409                        } else {
410                            vals[optid].push(Val(args.next().unwrap()));
411                        }
412                      }
413                      Yes => {
414                        if let Some(i_arg) = i_arg.take() {
415                            vals[optid].push(Val(i_arg));
416                        } else if let Some(n) = args.next() {
417                            vals[optid].push(Val(n));
418                        } else {
419                            return Err(ArgumentMissing(nm.to_string()));
420                        }
421                      }
422                    }
423                }
424            }
425        }
426        debug_assert_eq!(vals.len(), opts.len());
427        for (vals, opt) in vals.iter().zip(opts.iter()) {
428            if opt.occur == Req && vals.is_empty() {
429                return Err(OptionMissing(opt.name.to_string()));
430            }
431            if opt.occur != Multi && vals.len() > 1 {
432                return Err(OptionDuplicated(opt.name.to_string()));
433            }
434        }
435        Ok(Matches {
436            opts: opts,
437            vals: vals,
438            free: free
439        })
440    }
441
442    /// Derive a short one-line usage summary from a set of long options.
443    pub fn short_usage(&self, program_name: &str) -> String {
444        let mut line = format!("Usage: {} ", program_name);
445        line.push_str(&self.grps.iter()
446                           .map(format_option)
447                           .collect::<Vec<String>>()
448                           .join(" "));
449        line
450    }
451
452
453    /// Derive a formatted message from a set of options.
454    pub fn usage(&self, brief: &str) -> String {
455        self.usage_with_format(|opts|
456            format!("{}\n\nOptions:\n{}\n", brief, opts.collect::<Vec<String>>().join("\n")))
457    }
458
459    /// Derive a custom formatted message from a set of options. The formatted options provided to
460    /// a closure as an iterator.
461    pub fn usage_with_format<F: FnMut(&mut Iterator<Item=String>) -> String>(&self, mut formatter: F) -> String {
462        formatter(&mut self.usage_items())
463    }
464
465    /// Derive usage items from a set of options.
466    fn usage_items<'a>(&'a self) -> Box<Iterator<Item=String> + 'a> {
467        let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
468
469        let any_short = self.grps.iter().any(|optref| {
470            optref.short_name.len() > 0
471        });
472
473        let rows = self.grps.iter().map(move |optref| {
474            let OptGroup{short_name,
475                         long_name,
476                         hint,
477                         desc,
478                         hasarg,
479                         ..} = (*optref).clone();
480
481            let mut row = "    ".to_string();
482
483            // short option
484            match short_name.width() {
485                0 => {
486                    if any_short {
487                        row.push_str("    ");
488                    }
489                }
490                1 => {
491                    row.push('-');
492                    row.push_str(&short_name);
493                    if long_name.width() > 0 {
494                        row.push_str(", ");
495                    } else {
496                        // Only a single space here, so that any
497                        // argument is printed in the correct spot.
498                        row.push(' ');
499                    }
500                }
501                // FIXME: refer issue #7.
502                _ => panic!("the short name should only be 1 ascii char long"),
503            }
504
505            // long option
506            match long_name.width() {
507                0 => {}
508                _ => {
509                    row.push_str(if self.long_only { "-" } else { "--" });
510                    row.push_str(&long_name);
511                    row.push(' ');
512                }
513            }
514
515            // arg
516            match hasarg {
517                No => {}
518                Yes => row.push_str(&hint),
519                Maybe => {
520                    row.push('[');
521                    row.push_str(&hint);
522                    row.push(']');
523                }
524            }
525
526            let rowlen = row.width();
527            if rowlen < 24 {
528                for _ in 0 .. 24 - rowlen {
529                    row.push(' ');
530                }
531            } else {
532                row.push_str(&desc_sep)
533            }
534
535            let desc_rows = each_split_within(&desc, 54);
536            row.push_str(&desc_rows.join(&desc_sep));
537
538            row
539        });
540
541       Box::new(rows)
542    }
543}
544
545fn validate_names(short_name: &str, long_name: &str) {
546    let len = short_name.len();
547    assert!(len == 1 || len == 0,
548            "the short_name (first argument) should be a single character, \
549             or an empty string for none");
550    let len = long_name.len();
551    assert!(len == 0 || len > 1,
552            "the long_name (second argument) should be longer than a single \
553             character, or an empty string for none");
554}
555
556/// What parsing style to use when parsing arguments.
557#[derive(Clone, Copy, PartialEq, Eq)]
558pub enum ParsingStyle {
559    /// Flags and "free" arguments can be freely inter-mixed.
560    FloatingFrees,
561    /// As soon as a "free" argument (i.e. non-flag) is encountered, stop
562    /// considering any remaining arguments as flags.
563    StopAtFirstFree
564}
565
566/// Name of an option. Either a string or a single char.
567#[derive(Clone, PartialEq, Eq)]
568enum Name {
569    /// A string representing the long name of an option.
570    /// For example: "help"
571    Long(String),
572    /// A char representing the short name of an option.
573    /// For example: 'h'
574    Short(char),
575}
576
577/// Describes whether an option has an argument.
578#[derive(Clone, Copy, PartialEq, Eq)]
579pub enum HasArg {
580    /// The option requires an argument.
581    Yes,
582    /// The option takes no argument.
583    No,
584    /// The option argument is optional.
585    Maybe,
586}
587
588/// Describes how often an option may occur.
589#[derive(Clone, Copy, PartialEq, Eq)]
590pub enum Occur {
591    /// The option occurs once.
592    Req,
593    /// The option occurs at most once.
594    Optional,
595    /// The option occurs zero or more times.
596    Multi,
597}
598
599/// A description of a possible option.
600#[derive(Clone, PartialEq, Eq)]
601struct Opt {
602    /// Name of the option
603    name: Name,
604    /// Whether it has an argument
605    hasarg: HasArg,
606    /// How often it can occur
607    occur: Occur,
608    /// Which options it aliases
609    aliases: Vec<Opt>,
610}
611
612/// One group of options, e.g., both `-h` and `--help`, along with
613/// their shared description and properties.
614#[derive(Clone, PartialEq, Eq)]
615struct OptGroup {
616    /// Short name of the option, e.g. `h` for a `-h` option
617    short_name: String,
618    /// Long name of the option, e.g. `help` for a `--help` option
619    long_name: String,
620    /// Hint for argument, e.g. `FILE` for a `-o FILE` option
621    hint: String,
622    /// Description for usage help text
623    desc: String,
624    /// Whether option has an argument
625    hasarg: HasArg,
626    /// How often it can occur
627    occur: Occur
628}
629
630/// Describes whether an option is given at all or has a value.
631#[derive(Clone, PartialEq, Eq)]
632enum Optval {
633    Val(String),
634    Given,
635}
636
637/// The result of checking command line arguments. Contains a vector
638/// of matches and a vector of free strings.
639#[derive(Clone, PartialEq, Eq)]
640pub struct Matches {
641    /// Options that matched
642    opts: Vec<Opt>,
643    /// Values of the Options that matched
644    vals: Vec<Vec<Optval>>,
645    /// Free string fragments
646    pub free: Vec<String>,
647}
648
649/// The type returned when the command line does not conform to the
650/// expected format. Use the `Debug` implementation to output detailed
651/// information.
652#[derive(Clone, Debug, PartialEq, Eq)]
653pub enum Fail {
654    /// The option requires an argument but none was passed.
655    ArgumentMissing(String),
656    /// The passed option is not declared among the possible options.
657    UnrecognizedOption(String),
658    /// A required option is not present.
659    OptionMissing(String),
660    /// A single occurrence option is being used multiple times.
661    OptionDuplicated(String),
662    /// There's an argument being passed to a non-argument option.
663    UnexpectedArgument(String),
664}
665
666impl Error for Fail {
667    fn description(&self) -> &str {
668        match *self {
669            ArgumentMissing(_) => "missing argument",
670            UnrecognizedOption(_) => "unrecognized option",
671            OptionMissing(_) => "missing option",
672            OptionDuplicated(_) => "duplicated option",
673            UnexpectedArgument(_) => "unexpected argument",
674        }
675    }
676}
677
678/// The result of parsing a command line with a set of options.
679pub type Result = result::Result<Matches, Fail>;
680
681impl Name {
682    fn from_str(nm: &str) -> Name {
683        if nm.len() == 1 {
684            Short(nm.as_bytes()[0] as char)
685        } else {
686            Long(nm.to_string())
687        }
688    }
689
690    fn to_string(&self) -> String {
691        match *self {
692            Short(ch) => ch.to_string(),
693            Long(ref s) => s.to_string()
694        }
695    }
696}
697
698impl OptGroup {
699    /// Translate OptGroup into Opt.
700    /// (Both short and long names correspond to different Opts).
701    fn long_to_short(&self) -> Opt {
702        let OptGroup {
703            short_name,
704            long_name,
705            hasarg,
706            occur,
707            ..
708        } = (*self).clone();
709
710        match (short_name.len(), long_name.len()) {
711            (0,0) => panic!("this long-format option was given no name"),
712            (0,_) => Opt {
713                name: Long(long_name),
714                hasarg: hasarg,
715                occur: occur,
716                aliases: Vec::new()
717            },
718            (1,0) => Opt {
719                name: Short(short_name.as_bytes()[0] as char),
720                hasarg: hasarg,
721                occur: occur,
722                aliases: Vec::new()
723            },
724            (1,_) => Opt {
725                name: Long(long_name),
726                hasarg: hasarg,
727                occur: occur,
728                aliases: vec!(
729                    Opt {
730                        name: Short(short_name.as_bytes()[0] as char),
731                        hasarg: hasarg,
732                        occur:  occur,
733                        aliases: Vec::new()
734                    }
735                )
736            },
737            (_,_) => panic!("something is wrong with the long-form opt")
738        }
739    }
740}
741
742impl Matches {
743    fn opt_vals(&self, nm: &str) -> Vec<Optval> {
744        match find_opt(&self.opts, &Name::from_str(nm)) {
745            Some(id) => self.vals[id].clone(),
746            None => panic!("No option '{}' defined", nm)
747        }
748    }
749
750    fn opt_val(&self, nm: &str) -> Option<Optval> {
751        self.opt_vals(nm).into_iter().next()
752    }
753    /// Returns true if an option was defined
754    pub fn opt_defined(&self, nm: &str) -> bool {
755        find_opt(&self.opts, &Name::from_str(nm)).is_some()
756    }
757
758    /// Returns true if an option was matched.
759    pub fn opt_present(&self, nm: &str) -> bool {
760        !self.opt_vals(nm).is_empty()
761    }
762
763    /// Returns the number of times an option was matched.
764    pub fn opt_count(&self, nm: &str) -> usize {
765        self.opt_vals(nm).len()
766    }
767
768    /// Returns true if any of several options were matched.
769    pub fn opts_present(&self, names: &[String]) -> bool {
770        names.iter().any(|nm| {
771            match find_opt(&self.opts, &Name::from_str(&nm)) {
772                Some(id) if !self.vals[id].is_empty() => true,
773                _ => false,
774            }
775        })
776    }
777
778    /// Returns the string argument supplied to one of several matching options or `None`.
779    pub fn opts_str(&self, names: &[String]) -> Option<String> {
780        names.iter().filter_map(|nm| {
781            match self.opt_val(&nm) {
782                Some(Val(s)) => Some(s),
783                _ => None,
784            }
785        }).next()
786    }
787
788    /// Returns a vector of the arguments provided to all matches of the given
789    /// option.
790    ///
791    /// Used when an option accepts multiple values.
792    pub fn opt_strs(&self, nm: &str) -> Vec<String> {
793        self.opt_vals(nm).into_iter().filter_map(|v| {
794            match v {
795                Val(s) => Some(s),
796                _ => None,
797            }
798        }).collect()
799    }
800
801    /// Returns the string argument supplied to a matching option or `None`.
802    pub fn opt_str(&self, nm: &str) -> Option<String> {
803        match self.opt_val(nm) {
804            Some(Val(s)) => Some(s),
805            _ => None,
806        }
807    }
808
809
810    /// Returns the matching string, a default, or `None`.
811    ///
812    /// Returns `None` if the option was not present, `def` if the option was
813    /// present but no argument was provided, and the argument if the option was
814    /// present and an argument was provided.
815    pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
816        match self.opt_val(nm) {
817            Some(Val(s)) => Some(s),
818            Some(_) => Some(def.to_string()),
819            None => None,
820        }
821    }
822
823    /// Returns some matching value or `None`.
824    ///
825    /// Similar to opt_str, also converts matching argument using FromStr.
826    pub fn opt_get<T>(&self, nm: &str) -> result::Result<Option<T>, T::Err>
827        where T: FromStr
828    {
829        match self.opt_val(nm) {
830            Some(Val(s)) => Ok(Some(s.parse()?)),
831            Some(Given) => Ok(None),
832            None => Ok(None),
833        }
834    }
835
836    /// Returns a matching value or default.
837    ///
838    /// Similar to opt_default, except the two differences.
839    /// Instead of returning None when argument was not present, return `def`.
840    /// Instead of returning &str return type T, parsed using str::parse().
841    pub fn opt_get_default<T>(&self, nm: &str, def: T)
842        -> result::Result<T, T::Err> where T: FromStr
843    {
844        match self.opt_val(nm) {
845            Some(Val(s)) => s.parse(),
846            Some(Given) => Ok(def),
847            None => Ok(def),
848        }
849    }
850}
851
852fn is_arg(arg: &str) -> bool {
853    arg.as_bytes().get(0) == Some(&b'-') && arg.len() > 1
854}
855
856fn find_opt(opts: &[Opt], nm: &Name) -> Option<usize> {
857    // Search main options.
858    let pos = opts.iter().position(|opt| &opt.name == nm);
859    if pos.is_some() {
860        return pos
861    }
862
863    // Search in aliases.
864    for candidate in opts.iter() {
865        if candidate.aliases.iter().position(|opt| &opt.name == nm).is_some() {
866            return opts.iter().position(|opt| opt.name == candidate.name);
867        }
868    }
869
870    None
871}
872
873impl fmt::Display for Fail {
874    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
875        match *self {
876            ArgumentMissing(ref nm) => {
877                write!(f, "Argument to option '{}' missing", *nm)
878            }
879            UnrecognizedOption(ref nm) => {
880                write!(f, "Unrecognized option: '{}'", *nm)
881            }
882            OptionMissing(ref nm) => {
883                write!(f, "Required option '{}' missing", *nm)
884            }
885            OptionDuplicated(ref nm) => {
886                write!(f, "Option '{}' given more than once", *nm)
887            }
888            UnexpectedArgument(ref nm) => {
889                write!(f, "Option '{}' does not take an argument", *nm)
890            }
891        }
892    }
893}
894
895fn format_option(opt: &OptGroup) -> String {
896    let mut line = String::new();
897
898    if opt.occur != Req {
899        line.push('[');
900    }
901
902    // Use short_name if possible, but fall back to long_name.
903    if opt.short_name.len() > 0 {
904        line.push('-');
905        line.push_str(&opt.short_name);
906    } else {
907        line.push_str("--");
908        line.push_str(&opt.long_name);
909    }
910
911    if opt.hasarg != No {
912        line.push(' ');
913        if opt.hasarg == Maybe {
914            line.push('[');
915        }
916        line.push_str(&opt.hint);
917        if opt.hasarg == Maybe {
918            line.push(']');
919        }
920    }
921
922    if opt.occur != Req {
923        line.push(']');
924    }
925    if opt.occur == Multi {
926        line.push_str("..");
927    }
928
929    line
930}
931
932/// Splits a string into substrings with possibly internal whitespace,
933/// each of them at most `lim` bytes long, if possible. The substrings
934/// have leading and trailing whitespace removed, and are only cut at
935/// whitespace boundaries.
936fn each_split_within(desc: &str, lim: usize) -> Vec<String> {
937    let mut rows = Vec::new();
938    for line in desc.trim().lines() {
939        let line_chars = line.chars().chain(Some(' '));
940        let words = line_chars.fold( (Vec::new(), 0, 0), |(mut words, a, z), c | {
941            let idx = z + c.len_utf8(); // Get the current byte offset
942
943            // If the char is whitespace, advance the word start and maybe push a word
944            if c.is_whitespace() {
945                if a != z {
946                    words.push(&line[a..z]);
947                }
948                (words, idx, idx)
949            }
950            // If the char is not whitespace, continue, retaining the current
951            else {
952                (words, a, idx)
953            }
954        }).0;
955
956        let mut row = String::new();
957        for word in words.iter() {
958            let sep = if row.len() > 0 { Some(" ") } else { None };
959            let width = row.width()
960                + word.width()
961                + sep.map(UnicodeWidthStr::width).unwrap_or(0);
962
963            if width <= lim {
964                if let Some(sep) = sep { row.push_str(sep) }
965                row.push_str(word);
966                continue
967            }
968            if row.len() > 0 {
969                rows.push(row.clone());
970                row.clear();
971            }
972            row.push_str(word);
973        }
974        if row.len() > 0 {
975            rows.push(row);
976        }
977    }
978    rows
979}
980
981#[test]
982fn test_split_within() {
983    fn t(s: &str, i: usize, u: &[String]) {
984        let v = each_split_within(&(s.to_string()), i);
985        assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
986    }
987    t("", 0, &[]);
988    t("", 15, &[]);
989    t("hello", 15, &["hello".to_string()]);
990    t("\nMary had a little lamb\nLittle lamb\n", 15, &[
991        "Mary had a".to_string(),
992        "little lamb".to_string(),
993        "Little lamb".to_string()
994    ]);
995    t("\nMary had a little lamb\nLittle lamb\n", ::std::usize::MAX,
996        &["Mary had a little lamb".to_string(),
997        "Little lamb".to_string()
998    ]);
999}
1000
1001#[cfg(test)]
1002mod tests {
1003    use super::{HasArg, Name, Occur, Opt, Options, ParsingStyle};
1004    use super::Fail::*;
1005
1006    // Tests for reqopt
1007    #[test]
1008    fn test_reqopt() {
1009        let long_args = vec!("--test=20".to_string());
1010        let mut opts = Options::new();
1011        opts.reqopt("t", "test", "testing", "TEST");
1012        match opts.parse(&long_args) {
1013          Ok(ref m) => {
1014            assert!(m.opt_present("test"));
1015            assert_eq!(m.opt_str("test").unwrap(), "20");
1016            assert!(m.opt_present("t"));
1017            assert_eq!(m.opt_str("t").unwrap(), "20");
1018          }
1019          _ => { panic!("test_reqopt failed (long arg)"); }
1020        }
1021        let short_args = vec!("-t".to_string(), "20".to_string());
1022        match opts.parse(&short_args) {
1023          Ok(ref m) => {
1024            assert!((m.opt_present("test")));
1025            assert_eq!(m.opt_str("test").unwrap(), "20");
1026            assert!((m.opt_present("t")));
1027            assert_eq!(m.opt_str("t").unwrap(), "20");
1028          }
1029          _ => { panic!("test_reqopt failed (short arg)"); }
1030        }
1031    }
1032
1033    #[test]
1034    fn test_reqopt_missing() {
1035        let args = vec!("blah".to_string());
1036        match Options::new()
1037                      .reqopt("t", "test", "testing", "TEST")
1038                      .parse(&args) {
1039          Err(OptionMissing(_)) => {},
1040          _ => panic!()
1041        }
1042    }
1043
1044    #[test]
1045    fn test_reqopt_no_arg() {
1046        let long_args = vec!("--test".to_string());
1047        let mut opts = Options::new();
1048        opts.reqopt("t", "test", "testing", "TEST");
1049        match opts.parse(&long_args) {
1050          Err(ArgumentMissing(_)) => {},
1051          _ => panic!()
1052        }
1053        let short_args = vec!("-t".to_string());
1054        match opts.parse(&short_args) {
1055          Err(ArgumentMissing(_)) => {},
1056          _ => panic!()
1057        }
1058    }
1059
1060    #[test]
1061    fn test_reqopt_multi() {
1062        let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1063        match Options::new()
1064                      .reqopt("t", "test", "testing", "TEST")
1065                      .parse(&args) {
1066          Err(OptionDuplicated(_)) => {},
1067          _ => panic!()
1068        }
1069    }
1070
1071    // Tests for optopt
1072    #[test]
1073    fn test_optopt() {
1074        let long_args = vec!("--test=20".to_string());
1075        let mut opts = Options::new();
1076        opts.optopt("t", "test", "testing", "TEST");
1077        match opts.parse(&long_args) {
1078          Ok(ref m) => {
1079            assert!(m.opt_present("test"));
1080            assert_eq!(m.opt_str("test").unwrap(), "20");
1081            assert!((m.opt_present("t")));
1082            assert_eq!(m.opt_str("t").unwrap(), "20");
1083          }
1084          _ => panic!()
1085        }
1086        let short_args = vec!("-t".to_string(), "20".to_string());
1087        match opts.parse(&short_args) {
1088          Ok(ref m) => {
1089            assert!((m.opt_present("test")));
1090            assert_eq!(m.opt_str("test").unwrap(), "20");
1091            assert!((m.opt_present("t")));
1092            assert_eq!(m.opt_str("t").unwrap(), "20");
1093          }
1094          _ => panic!()
1095        }
1096    }
1097
1098    #[test]
1099    fn test_optopt_missing() {
1100        let args = vec!("blah".to_string());
1101        match Options::new()
1102                      .optopt("t", "test", "testing", "TEST")
1103                      .parse(&args) {
1104          Ok(ref m) => {
1105            assert!(!m.opt_present("test"));
1106            assert!(!m.opt_present("t"));
1107          }
1108          _ => panic!()
1109        }
1110    }
1111
1112    #[test]
1113    fn test_optopt_no_arg() {
1114        let long_args = vec!("--test".to_string());
1115        let mut opts = Options::new();
1116        opts.optopt("t", "test", "testing", "TEST");
1117        match opts.parse(&long_args) {
1118          Err(ArgumentMissing(_)) => {},
1119          _ => panic!()
1120        }
1121        let short_args = vec!("-t".to_string());
1122        match opts.parse(&short_args) {
1123          Err(ArgumentMissing(_)) => {},
1124          _ => panic!()
1125        }
1126    }
1127
1128    #[test]
1129    fn test_optopt_multi() {
1130        let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1131        match Options::new()
1132                      .optopt("t", "test", "testing", "TEST")
1133                      .parse(&args) {
1134          Err(OptionDuplicated(_)) => {},
1135          _ => panic!()
1136        }
1137    }
1138
1139    // Tests for optflag
1140    #[test]
1141    fn test_optflag() {
1142        let long_args = vec!("--test".to_string());
1143        let mut opts = Options::new();
1144        opts.optflag("t", "test", "testing");
1145        match opts.parse(&long_args) {
1146          Ok(ref m) => {
1147            assert!(m.opt_present("test"));
1148            assert!(m.opt_present("t"));
1149          }
1150          _ => panic!()
1151        }
1152        let short_args = vec!("-t".to_string());
1153        match opts.parse(&short_args) {
1154          Ok(ref m) => {
1155            assert!(m.opt_present("test"));
1156            assert!(m.opt_present("t"));
1157          }
1158          _ => panic!()
1159        }
1160    }
1161
1162    #[test]
1163    fn test_optflag_missing() {
1164        let args = vec!("blah".to_string());
1165        match Options::new()
1166                      .optflag("t", "test", "testing")
1167                      .parse(&args) {
1168          Ok(ref m) => {
1169            assert!(!m.opt_present("test"));
1170            assert!(!m.opt_present("t"));
1171          }
1172          _ => panic!()
1173        }
1174    }
1175
1176    #[test]
1177    fn test_opt_end() {
1178        let args = vec!["--".to_owned(), "-t".to_owned()];
1179        match Options::new()
1180                      .optflag("t", "test", "testing")
1181                      .parse(&args) {
1182          Ok(ref m) => {
1183            assert!(!m.opt_present("test"));
1184            assert!(!m.opt_present("t"));
1185            assert_eq!(m.free.len(), 1);
1186            assert_eq!(m.free[0], "-t");
1187          }
1188          _ => panic!()
1189        }
1190    }
1191
1192    #[test]
1193    fn test_opt_only_end() {
1194        let args = vec!["--".to_owned()];
1195        match Options::new()
1196                      .optflag("t", "test", "testing")
1197                      .parse(&args) {
1198          Ok(ref m) => {
1199            assert!(!m.opt_present("test"));
1200            assert!(!m.opt_present("t"));
1201            assert_eq!(m.free.len(), 0);
1202          }
1203          _ => panic!()
1204        }
1205    }
1206
1207    #[test]
1208    fn test_optflag_long_arg() {
1209        let args = vec!("--test=20".to_string());
1210        match Options::new()
1211                      .optflag("t", "test", "testing")
1212                      .parse(&args) {
1213          Err(UnexpectedArgument(_)) => {},
1214          _ => panic!()
1215        }
1216    }
1217
1218    #[test]
1219    fn test_optflag_multi() {
1220        let args = vec!("--test".to_string(), "-t".to_string());
1221        match Options::new()
1222                      .optflag("t", "test", "testing")
1223                      .parse(&args) {
1224          Err(OptionDuplicated(_)) => {},
1225          _ => panic!()
1226        }
1227    }
1228
1229    #[test]
1230    fn test_optflag_short_arg() {
1231        let args = vec!("-t".to_string(), "20".to_string());
1232        match Options::new()
1233                      .optflag("t", "test", "testing")
1234                      .parse(&args) {
1235          Ok(ref m) => {
1236            // The next variable after the flag is just a free argument
1237
1238            assert!(m.free[0] == "20");
1239          }
1240          _ => panic!()
1241        }
1242    }
1243
1244    // Tests for optflagmulti
1245    #[test]
1246    fn test_optflagmulti_short1() {
1247        let args = vec!("-v".to_string());
1248        match Options::new()
1249                      .optflagmulti("v", "verbose", "verbosity")
1250                      .parse(&args) {
1251          Ok(ref m) => {
1252            assert_eq!(m.opt_count("v"), 1);
1253          }
1254          _ => panic!()
1255        }
1256    }
1257
1258    #[test]
1259    fn test_optflagmulti_short2a() {
1260        let args = vec!("-v".to_string(), "-v".to_string());
1261        match Options::new()
1262                      .optflagmulti("v", "verbose", "verbosity")
1263                      .parse(&args) {
1264          Ok(ref m) => {
1265            assert_eq!(m.opt_count("v"), 2);
1266          }
1267          _ => panic!()
1268        }
1269    }
1270
1271    #[test]
1272    fn test_optflagmulti_short2b() {
1273        let args = vec!("-vv".to_string());
1274        match Options::new()
1275                      .optflagmulti("v", "verbose", "verbosity")
1276                      .parse(&args) {
1277          Ok(ref m) => {
1278            assert_eq!(m.opt_count("v"), 2);
1279          }
1280          _ => panic!()
1281        }
1282    }
1283
1284    #[test]
1285    fn test_optflagmulti_long1() {
1286        let args = vec!("--verbose".to_string());
1287        match Options::new()
1288                      .optflagmulti("v", "verbose", "verbosity")
1289                      .parse(&args) {
1290          Ok(ref m) => {
1291            assert_eq!(m.opt_count("verbose"), 1);
1292          }
1293          _ => panic!()
1294        }
1295    }
1296
1297    #[test]
1298    fn test_optflagmulti_long2() {
1299        let args = vec!("--verbose".to_string(), "--verbose".to_string());
1300        match Options::new()
1301                      .optflagmulti("v", "verbose", "verbosity")
1302                      .parse(&args) {
1303          Ok(ref m) => {
1304            assert_eq!(m.opt_count("verbose"), 2);
1305          }
1306          _ => panic!()
1307        }
1308    }
1309
1310    #[test]
1311    fn test_optflagmulti_mix() {
1312        let args = vec!("--verbose".to_string(), "-v".to_string(),
1313                        "-vv".to_string(), "verbose".to_string());
1314        match Options::new()
1315                      .optflagmulti("v", "verbose", "verbosity")
1316                      .parse(&args) {
1317          Ok(ref m) => {
1318            assert_eq!(m.opt_count("verbose"), 4);
1319            assert_eq!(m.opt_count("v"), 4);
1320          }
1321          _ => panic!()
1322        }
1323    }
1324
1325    // Tests for optflagopt
1326    #[test]
1327    fn test_optflagopt() {
1328        let long_args = vec!("--test".to_string());
1329        let mut opts = Options::new();
1330        opts.optflagopt("t", "test", "testing", "ARG");
1331        match opts.parse(&long_args) {
1332          Ok(ref m) => {
1333            assert!(m.opt_present("test"));
1334            assert!(m.opt_present("t"));
1335          }
1336          _ => panic!()
1337        }
1338        let short_args = vec!("-t".to_string());
1339        match opts.parse(&short_args) {
1340          Ok(ref m) => {
1341            assert!(m.opt_present("test"));
1342            assert!(m.opt_present("t"));
1343          }
1344          _ => panic!()
1345        }
1346        let short_args = vec!("-t".to_string(), "x".to_string());
1347        match opts.parse(&short_args) {
1348            Ok(ref m) => {
1349                assert_eq!(m.opt_str("t").unwrap(), "x");
1350                assert_eq!(m.opt_str("test").unwrap(), "x");
1351            }
1352            _ => panic!()
1353        }
1354        let long_args = vec!("--test=x".to_string());
1355        match opts.parse(&long_args) {
1356            Ok(ref m) => {
1357                assert_eq!(m.opt_str("t").unwrap(), "x");
1358                assert_eq!(m.opt_str("test").unwrap(), "x");
1359            }
1360            _ => panic!()
1361        }
1362        let long_args = vec!("--test".to_string(), "x".to_string());
1363        match opts.parse(&long_args) {
1364            Ok(ref m) => {
1365                assert_eq!(m.opt_str("t"), None);
1366                assert_eq!(m.opt_str("test"), None);
1367            }
1368            _ => panic!()
1369        }
1370        let no_args: Vec<String> = vec!();
1371        match opts.parse(&no_args) {
1372          Ok(ref m) => {
1373            assert!(!m.opt_present("test"));
1374            assert!(!m.opt_present("t"));
1375          }
1376          _ => panic!()
1377        }
1378    }
1379
1380    // Tests for optmulti
1381    #[test]
1382    fn test_optmulti() {
1383        let long_args = vec!("--test=20".to_string());
1384        let mut opts = Options::new();
1385        opts.optmulti("t", "test", "testing", "TEST");
1386        match opts.parse(&long_args) {
1387          Ok(ref m) => {
1388            assert!((m.opt_present("test")));
1389            assert_eq!(m.opt_str("test").unwrap(), "20");
1390            assert!((m.opt_present("t")));
1391            assert_eq!(m.opt_str("t").unwrap(), "20");
1392          }
1393          _ => panic!()
1394        }
1395        let short_args = vec!("-t".to_string(), "20".to_string());
1396        match opts.parse(&short_args) {
1397          Ok(ref m) => {
1398            assert!((m.opt_present("test")));
1399            assert_eq!(m.opt_str("test").unwrap(), "20");
1400            assert!((m.opt_present("t")));
1401            assert_eq!(m.opt_str("t").unwrap(), "20");
1402          }
1403          _ => panic!()
1404        }
1405    }
1406
1407    #[test]
1408    fn test_optmulti_missing() {
1409        let args = vec!("blah".to_string());
1410        match Options::new()
1411                      .optmulti("t", "test", "testing", "TEST")
1412                      .parse(&args) {
1413          Ok(ref m) => {
1414            assert!(!m.opt_present("test"));
1415            assert!(!m.opt_present("t"));
1416          }
1417          _ => panic!()
1418        }
1419    }
1420
1421    #[test]
1422    fn test_optmulti_no_arg() {
1423        let long_args = vec!("--test".to_string());
1424        let mut opts = Options::new();
1425        opts.optmulti("t", "test", "testing", "TEST");
1426        match opts.parse(&long_args) {
1427          Err(ArgumentMissing(_)) => {},
1428          _ => panic!()
1429        }
1430        let short_args = vec!("-t".to_string());
1431        match opts.parse(&short_args) {
1432          Err(ArgumentMissing(_)) => {},
1433          _ => panic!()
1434        }
1435    }
1436
1437    #[test]
1438    fn test_optmulti_multi() {
1439        let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1440        match Options::new()
1441                      .optmulti("t", "test", "testing", "TEST")
1442                      .parse(&args) {
1443          Ok(ref m) => {
1444              assert!(m.opt_present("test"));
1445              assert_eq!(m.opt_str("test").unwrap(), "20");
1446              assert!(m.opt_present("t"));
1447              assert_eq!(m.opt_str("t").unwrap(), "20");
1448              let pair = m.opt_strs("test");
1449              assert!(pair[0] == "20");
1450              assert!(pair[1] == "30");
1451          }
1452          _ => panic!()
1453        }
1454    }
1455
1456    #[test]
1457    fn test_free_argument_is_hyphen() {
1458        let args = vec!("-".to_string());
1459        match Options::new().parse(&args) {
1460            Ok(ref m) => {
1461                assert_eq!(m.free.len(), 1);
1462                assert_eq!(m.free[0], "-");
1463            }
1464            _ => panic!()
1465        }
1466    }
1467
1468    #[test]
1469    fn test_unrecognized_option() {
1470        let long_args = vec!("--untest".to_string());
1471        let mut opts = Options::new();
1472        opts.optmulti("t", "test", "testing", "TEST");
1473        match opts.parse(&long_args) {
1474          Err(UnrecognizedOption(_)) => {},
1475          _ => panic!()
1476        }
1477        let short_args = vec!("-u".to_string());
1478        match opts.parse(&short_args) {
1479          Err(UnrecognizedOption(_)) => {},
1480          _ => panic!()
1481        }
1482    }
1483
1484    #[test]
1485    fn test_combined() {
1486        let args =
1487            vec!("prog".to_string(),
1488                 "free1".to_string(),
1489                 "-s".to_string(),
1490                 "20".to_string(),
1491                 "free2".to_string(),
1492                 "--flag".to_string(),
1493                 "--long=30".to_string(),
1494                 "-f".to_string(),
1495                 "-m".to_string(),
1496                 "40".to_string(),
1497                 "-m".to_string(),
1498                 "50".to_string(),
1499                 "-n".to_string(),
1500                 "-A B".to_string(),
1501                 "-n".to_string(),
1502                 "-60 70".to_string());
1503        match Options::new()
1504                      .optopt("s", "something", "something", "SOMETHING")
1505                      .optflag("", "flag", "a flag")
1506                      .reqopt("", "long", "hi", "LONG")
1507                      .optflag("f", "", "another flag")
1508                      .optmulti("m", "", "mmmmmm", "YUM")
1509                      .optmulti("n", "", "nothing", "NOTHING")
1510                      .optopt("", "notpresent", "nothing to see here", "NOPE")
1511                      .parse(&args) {
1512          Ok(ref m) => {
1513            assert!(m.free[0] == "prog");
1514            assert!(m.free[1] == "free1");
1515            assert_eq!(m.opt_str("s").unwrap(), "20");
1516            assert!(m.free[2] == "free2");
1517            assert!((m.opt_present("flag")));
1518            assert_eq!(m.opt_str("long").unwrap(), "30");
1519            assert!((m.opt_present("f")));
1520            let pair = m.opt_strs("m");
1521            assert!(pair[0] == "40");
1522            assert!(pair[1] == "50");
1523            let pair = m.opt_strs("n");
1524            assert!(pair[0] == "-A B");
1525            assert!(pair[1] == "-60 70");
1526            assert!((!m.opt_present("notpresent")));
1527          }
1528          _ => panic!()
1529        }
1530    }
1531
1532    #[test]
1533    fn test_mixed_stop() {
1534        let args =
1535            vec!("-a".to_string(),
1536                 "b".to_string(),
1537                 "-c".to_string(),
1538                 "d".to_string());
1539        match Options::new()
1540              .parsing_style(ParsingStyle::StopAtFirstFree)
1541              .optflag("a", "", "")
1542              .optopt("c", "", "", "")
1543              .parse(&args) {
1544          Ok(ref m) => {
1545            println!("{}", m.opt_present("c"));
1546            assert!(m.opt_present("a"));
1547            assert!(!m.opt_present("c"));
1548            assert_eq!(m.free.len(), 3);
1549            assert_eq!(m.free[0], "b");
1550            assert_eq!(m.free[1], "-c");
1551            assert_eq!(m.free[2], "d");
1552          }
1553          _ => panic!()
1554        }
1555    }
1556
1557    #[test]
1558    fn test_mixed_stop_hyphen() {
1559        let args =
1560            vec!("-a".to_string(),
1561                 "-".to_string(),
1562                 "-c".to_string(),
1563                 "d".to_string());
1564        match Options::new()
1565              .parsing_style(ParsingStyle::StopAtFirstFree)
1566              .optflag("a", "", "")
1567              .optopt("c", "", "", "")
1568              .parse(&args) {
1569          Ok(ref m) => {
1570            println!("{}", m.opt_present("c"));
1571            assert!(m.opt_present("a"));
1572            assert!(!m.opt_present("c"));
1573            assert_eq!(m.free.len(), 3);
1574            assert_eq!(m.free[0], "-");
1575            assert_eq!(m.free[1], "-c");
1576            assert_eq!(m.free[2], "d");
1577          }
1578          _ => panic!()
1579        }
1580    }
1581
1582    #[test]
1583    fn test_multi() {
1584        let mut opts = Options::new();
1585        opts.optopt("e", "", "encrypt", "ENCRYPT");
1586        opts.optopt("", "encrypt", "encrypt", "ENCRYPT");
1587        opts.optopt("f", "", "flag", "FLAG");
1588
1589        let args_single = vec!("-e".to_string(), "foo".to_string());
1590        let matches_single = &match opts.parse(&args_single) {
1591          Ok(m) => m,
1592          Err(_) => panic!()
1593        };
1594        assert!(matches_single.opts_present(&["e".to_string()]));
1595        assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1596        assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1597        assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1598        assert!(!matches_single.opts_present(&["thing".to_string()]));
1599        assert!(!matches_single.opts_present(&[]));
1600
1601        assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1602        assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1603                   "foo");
1604        assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1605                   "foo");
1606
1607        let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1608                             "foo".to_string());
1609        let matches_both = &match opts.parse(&args_both) {
1610          Ok(m) => m,
1611          Err(_) => panic!()
1612        };
1613        assert!(matches_both.opts_present(&["e".to_string()]));
1614        assert!(matches_both.opts_present(&["encrypt".to_string()]));
1615        assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1616        assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1617        assert!(!matches_both.opts_present(&["f".to_string()]));
1618        assert!(!matches_both.opts_present(&["thing".to_string()]));
1619        assert!(!matches_both.opts_present(&[]));
1620
1621        assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1622        assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1623        assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1624                   "foo");
1625        assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1626                   "foo");
1627    }
1628
1629    #[test]
1630    fn test_nospace() {
1631        let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1632        let matches = &match Options::new()
1633                                     .optmulti("L", "", "library directory", "LIB")
1634                                     .optmulti("M", "", "something", "MMMM")
1635                                     .parse(&args) {
1636          Ok(m) => m,
1637          Err(_) => panic!()
1638        };
1639        assert!(matches.opts_present(&["L".to_string()]));
1640        assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1641        assert!(matches.opts_present(&["M".to_string()]));
1642        assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1643
1644    }
1645
1646    #[test]
1647    fn test_nospace_conflict() {
1648        let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1649        let matches = &match Options::new()
1650                                     .optmulti("L", "", "library directory", "LIB")
1651                                     .optflagmulti("v", "verbose", "Verbose")
1652                                     .parse(&args) {
1653          Ok(m) => m,
1654          Err(e) => panic!( "{}", e )
1655        };
1656        assert!(matches.opts_present(&["L".to_string()]));
1657        assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1658        assert!(matches.opts_present(&["v".to_string()]));
1659        assert_eq!(3, matches.opt_count("v"));
1660    }
1661
1662    #[test]
1663    fn test_long_to_short() {
1664        let mut short = Opt {
1665            name: Name::Long("banana".to_string()),
1666            hasarg: HasArg::Yes,
1667            occur: Occur::Req,
1668            aliases: Vec::new(),
1669        };
1670        short.aliases = vec!(Opt { name: Name::Short('b'),
1671                                hasarg: HasArg::Yes,
1672                                occur: Occur::Req,
1673                                aliases: Vec::new() });
1674        let mut opts = Options::new();
1675        opts.reqopt("b", "banana", "some bananas", "VAL");
1676        let ref verbose = opts.grps[0];
1677        assert!(verbose.long_to_short() == short);
1678    }
1679
1680    #[test]
1681    fn test_aliases_long_and_short() {
1682        let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1683
1684        let matches = Options::new()
1685                              .optflagmulti("a", "apple", "Desc")
1686                              .parse(&args)
1687                              .unwrap();
1688        assert_eq!(3, matches.opt_count("a"));
1689        assert_eq!(3, matches.opt_count("apple"));
1690    }
1691
1692    #[test]
1693    fn test_usage() {
1694        let mut opts = Options::new();
1695        opts.reqopt("b", "banana", "Desc", "VAL");
1696        opts.optopt("a", "012345678901234567890123456789",
1697                             "Desc", "VAL");
1698        opts.optflag("k", "kiwi", "Desc");
1699        opts.optflagopt("p", "", "Desc", "VAL");
1700        opts.optmulti("l", "", "Desc", "VAL");
1701        opts.optflag("", "starfruit", "Starfruit");
1702
1703        let expected =
1704"Usage: fruits
1705
1706Options:
1707    -b, --banana VAL    Desc
1708    -a, --012345678901234567890123456789 VAL
1709                        Desc
1710    -k, --kiwi          Desc
1711    -p [VAL]            Desc
1712    -l VAL              Desc
1713        --starfruit     Starfruit
1714";
1715
1716        let generated_usage = opts.usage("Usage: fruits");
1717
1718        debug!("expected: <<{}>>", expected);
1719        debug!("generated: <<{}>>", generated_usage);
1720        assert_eq!(generated_usage, expected);
1721    }
1722
1723    #[test]
1724    fn test_usage_description_wrapping() {
1725        // indentation should be 24 spaces
1726        // lines wrap after 78: or rather descriptions wrap after 54
1727
1728        let mut opts = Options::new();
1729        opts.optflag("k", "kiwi",
1730            "This is a long description which won't be wrapped..+.."); // 54
1731        opts.optflag("a", "apple",
1732            "This is a long description which _will_ be wrapped..+..");
1733        opts.optflag("b", "banana",
1734            "HereWeNeedOneSingleWordThatIsLongerThanTheWrappingLengthAndThisIsIt");
1735
1736        let expected =
1737"Usage: fruits
1738
1739Options:
1740    -k, --kiwi          This is a long description which won't be wrapped..+..
1741    -a, --apple         This is a long description which _will_ be
1742                        wrapped..+..
1743    -b, --banana        HereWeNeedOneSingleWordThatIsLongerThanTheWrappingLengthAndThisIsIt
1744";
1745
1746        let usage = opts.usage("Usage: fruits");
1747
1748        debug!("expected: <<{}>>", expected);
1749        debug!("generated: <<{}>>", usage);
1750        assert!(usage == expected)
1751    }
1752
1753    #[test]
1754    fn test_usage_description_multibyte_handling() {
1755        let mut opts = Options::new();
1756        opts.optflag("k", "k\u{2013}w\u{2013}",
1757            "The word kiwi is normally spelled with two i's");
1758        opts.optflag("a", "apple",
1759            "This \u{201C}description\u{201D} has some characters that could \
1760confuse the line wrapping; an apple costs 0.51€ in some parts of Europe.");
1761
1762        let expected =
1763"Usage: fruits
1764
1765Options:
1766    -k, --k–w–          The word kiwi is normally spelled with two i's
1767    -a, --apple         This “description” has some characters that could
1768                        confuse the line wrapping; an apple costs 0.51€ in
1769                        some parts of Europe.
1770";
1771
1772        let usage = opts.usage("Usage: fruits");
1773
1774        debug!("expected: <<{}>>", expected);
1775        debug!("generated: <<{}>>", usage);
1776        assert!(usage == expected)
1777    }
1778
1779    #[test]
1780    fn test_usage_description_newline_handling() {
1781        let mut opts = Options::new();
1782        opts.optflag("k", "k\u{2013}w\u{2013}",
1783            "The word kiwi is normally spelled with two i's");
1784        opts.optflag("a", "apple",
1785            "This description forces a new line.\n Here is a premature\n\
1786            newline");
1787
1788        let expected =
1789"Usage: fruits
1790
1791Options:
1792    -k, --k–w–          The word kiwi is normally spelled with two i's
1793    -a, --apple         This description forces a new line.
1794                        Here is a premature
1795                        newline
1796";
1797
1798        let usage = opts.usage("Usage: fruits");
1799
1800        debug!("expected: <<{}>>", expected);
1801        debug!("generated: <<{}>>", usage);
1802        assert!(usage == expected)
1803    }
1804
1805    #[test]
1806    fn test_usage_multiwidth() {
1807        let mut opts = Options::new();
1808        opts.optflag("a", "apple",        "apple description");
1809        opts.optflag("b", "banana\u{00AB}", "banana description");
1810        opts.optflag("c", "brûlée", "brûlée quite long description");
1811        opts.optflag("k", "kiwi\u{20AC}",   "kiwi description");
1812        opts.optflag("o", "orange\u{2039}", "orange description");
1813        opts.optflag("r", "raspberry-but-making-this-option-way-too-long",
1814            "raspberry description is also quite long indeed longer than \
1815            every other piece of text we might encounter here and thus will \
1816            be automatically broken up"
1817        );
1818
1819        let expected =
1820"Usage: fruits
1821
1822Options:
1823    -a, --apple         apple description
1824    -b, --banana«       banana description
1825    -c, --brûlée        brûlée quite long description
1826    -k, --kiwi€         kiwi description
1827    -o, --orange‹       orange description
1828    -r, --raspberry-but-making-this-option-way-too-long 
1829                        raspberry description is also quite long indeed longer
1830                        than every other piece of text we might encounter here
1831                        and thus will be automatically broken up
1832";
1833
1834        let usage = opts.usage("Usage: fruits");
1835
1836        debug!("expected: <<{}>>", expected);
1837        debug!("generated: <<{}>>", usage);
1838        assert!(usage == expected)
1839    }
1840
1841
1842    #[test]
1843    fn test_usage_short_only() {
1844        let mut opts = Options::new();
1845        opts.optopt("k", "", "Kiwi", "VAL");
1846        opts.optflag("s", "", "Starfruit");
1847        opts.optflagopt("a", "", "Apple", "TYPE");
1848
1849        let expected =
1850"Usage: fruits
1851
1852Options:
1853    -k VAL              Kiwi
1854    -s                  Starfruit
1855    -a [TYPE]           Apple
1856";
1857
1858        let usage = opts.usage("Usage: fruits");
1859        debug!("expected: <<{}>>", expected);
1860        debug!("generated: <<{}>>", usage);
1861        assert!(usage == expected)
1862    }
1863
1864    #[test]
1865    fn test_usage_long_only() {
1866        let mut opts = Options::new();
1867        opts.optopt("", "kiwi", "Kiwi", "VAL");
1868        opts.optflag("", "starfruit", "Starfruit");
1869        opts.optflagopt("", "apple", "Apple", "TYPE");
1870
1871        let expected =
1872"Usage: fruits
1873
1874Options:
1875    --kiwi VAL          Kiwi
1876    --starfruit         Starfruit
1877    --apple [TYPE]      Apple
1878";
1879
1880        let usage = opts.usage("Usage: fruits");
1881        debug!("expected: <<{}>>", expected);
1882        debug!("generated: <<{}>>", usage);
1883        assert!(usage == expected)
1884    }
1885
1886    #[test]
1887    fn test_short_usage() {
1888        let mut opts = Options::new();
1889        opts.reqopt("b", "banana", "Desc", "VAL");
1890        opts.optopt("a", "012345678901234567890123456789",
1891                     "Desc", "VAL");
1892        opts.optflag("k", "kiwi", "Desc");
1893        opts.optflagopt("p", "", "Desc", "VAL");
1894        opts.optmulti("l", "", "Desc", "VAL");
1895
1896        let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1897        let generated_usage = opts.short_usage("fruits");
1898
1899        debug!("expected: <<{}>>", expected);
1900        debug!("generated: <<{}>>", generated_usage);
1901        assert_eq!(generated_usage, expected);
1902    }
1903    #[test]
1904    fn test_nonexistant_opt() {
1905        let mut opts = Options::new();
1906        opts.optflag("b", "bar", "Desc");
1907        let args: Vec<String> = Vec::new();
1908        let matches = opts.parse(&args).unwrap();
1909        assert_eq!(matches.opt_defined("foo"), false);
1910        assert_eq!(matches.opt_defined("bar"), true);
1911    }
1912    #[test]
1913    fn test_args_with_equals() {
1914        let mut opts = Options::new();
1915        opts.optopt("o", "one", "One", "INFO");
1916        opts.optopt("t", "two", "Two", "INFO");
1917
1918        let args = vec!("--one".to_string(), "A=B".to_string(),
1919                        "--two=C=D".to_string());
1920        let matches = &match opts.parse(&args) {
1921            Ok(m) => m,
1922            Err(e) => panic!("{}", e)
1923        };
1924        assert_eq!(matches.opts_str(&["o".to_string()]).unwrap(), "A=B");
1925        assert_eq!(matches.opts_str(&["t".to_string()]).unwrap(), "C=D");
1926    }
1927
1928    #[test]
1929    fn test_long_only_usage() {
1930        let mut opts = Options::new();
1931        opts.long_only(true);
1932        opts.optflag("k", "kiwi", "Description");
1933        opts.optflag("a", "apple", "Description");
1934
1935        let expected =
1936"Usage: fruits
1937
1938Options:
1939    -k, -kiwi           Description
1940    -a, -apple          Description
1941";
1942
1943        let usage = opts.usage("Usage: fruits");
1944
1945        debug!("expected: <<{}>>", expected);
1946        debug!("generated: <<{}>>", usage);
1947        assert!(usage == expected)
1948    }
1949
1950    #[test]
1951    fn test_long_only_mode() {
1952        let mut opts = Options::new();
1953        opts.long_only(true);
1954        opts.optopt("a", "apple", "Description", "X");
1955        opts.optopt("b", "banana", "Description", "X");
1956        opts.optopt("c", "currant", "Description", "X");
1957        opts.optopt("", "durian", "Description", "X");
1958        opts.optopt("e", "", "Description", "X");
1959        opts.optopt("", "fruit", "Description", "X");
1960
1961        let args = vec!("-a", "A", "-b=B", "--c=C", "-durian", "D", "--e", "E",
1962                        "-fruit=any");
1963        let matches = &match opts.parse(&args) {
1964            Ok(m) => m,
1965            Err(e) => panic!("{}", e)
1966        };
1967        assert_eq!(matches.opts_str(&["a".to_string()]).unwrap(), "A");
1968        assert_eq!(matches.opts_str(&["b".to_string()]).unwrap(), "B");
1969        assert_eq!(matches.opts_str(&["c".to_string()]).unwrap(), "C");
1970        assert_eq!(matches.opts_str(&["durian".to_string()]).unwrap(), "D");
1971        assert_eq!(matches.opts_str(&["e".to_string()]).unwrap(), "E");
1972        assert_eq!(matches.opts_str(&["fruit".to_string()]).unwrap(), "any");
1973    }
1974
1975    #[test]
1976    fn test_long_only_mode_no_short_parse() {
1977        let mut opts = Options::new();
1978        opts.long_only(true);
1979        opts.optflag("h", "help", "Description");
1980        opts.optflag("i", "ignore", "Description");
1981        opts.optflag("", "hi", "Description");
1982
1983        let args = vec!("-hi");
1984        let matches = &match opts.parse(&args) {
1985            Ok(m) => m,
1986            Err(e) => panic!("{}", e)
1987        };
1988        assert!(matches.opt_present("hi"));
1989        assert!(!matches.opt_present("h"));
1990        assert!(!matches.opt_present("i"));
1991    }
1992
1993    #[test]
1994    fn test_normal_mode_no_long_parse() {
1995        // Like test_long_only_mode_no_short_parse, but we make sure
1996        // that long_only can be disabled, and the right thing
1997        // happens.
1998        let mut opts = Options::new();
1999        opts.long_only(true);
2000        opts.optflag("h", "help", "Description");
2001        opts.optflag("i", "ignore", "Description");
2002        opts.optflag("", "hi", "Description");
2003        opts.long_only(false);
2004
2005        let args = vec!("-hi");
2006        let matches = &match opts.parse(&args) {
2007            Ok(m) => m,
2008            Err(e) => panic!("{}", e)
2009        };
2010        assert!(!matches.opt_present("hi"));
2011        assert!(matches.opt_present("h"));
2012        assert!(matches.opt_present("i"));
2013    }
2014
2015    #[test]
2016    #[should_panic]
2017    fn test_long_name_too_short() {
2018        let mut opts = Options::new();
2019        opts.optflag("", "a", "Oops, long option too short");
2020    }
2021
2022    #[test]
2023    #[should_panic]
2024    fn test_undefined_opt_present() {
2025        let mut opts = Options::new();
2026        opts.optflag("h", "help", "Description");
2027        let args = vec!["-h"];
2028        match opts.parse(args) {
2029            Ok(matches) => assert!(!matches.opt_present("undefined")),
2030            Err(e) => panic!("{}", e)
2031        }
2032    }
2033
2034    #[test]
2035    fn test_opt_default() {
2036        let mut opts = Options::new();
2037        opts.optflag("h", "help", "Description");
2038        opts.optflag("i", "ignore", "Description");
2039        opts.optflag("r", "run", "Description");
2040        opts.long_only(false);
2041
2042        let args: Vec<String> = ["-i", "-r", "10"]
2043            .iter().map(|x| x.to_string()).collect();
2044        let matches = &match opts.parse(&args) {
2045            Ok(m) => m,
2046            Err(e) => panic!("{}", e)
2047        };
2048        assert_eq!(matches.opt_default("help", ""), None);
2049        assert_eq!(matches.opt_default("i", "def"), Some("def".to_string()));
2050    }
2051
2052    #[test]
2053    fn test_opt_get() {
2054        let mut opts = Options::new();
2055        opts.optflag("h", "help", "Description");
2056        opts.optflagopt("i", "ignore", "Description", "true | false");
2057        opts.optflagopt("r", "run", "Description", "0 .. 10");
2058        opts.optflagopt("p", "percent", "Description", "0.0 .. 10.0");
2059        opts.long_only(false);
2060
2061        let args: Vec<String> = [
2062            "-i", "true", "-p", "1.1"
2063        ].iter().map(|x| x.to_string()).collect();
2064        let matches = &match opts.parse(&args) {
2065            Ok(m) => m,
2066            Err(e) => panic!("{}", e)
2067        };
2068        let h_arg = matches.opt_get::<i32>("help");
2069        assert_eq!(h_arg, Ok(None));
2070        let i_arg = matches.opt_get("i");
2071        assert_eq!(i_arg, Ok(Some(true)));
2072        let p_arg = matches.opt_get("p");
2073        assert_eq!(p_arg, Ok(Some(1.1)));
2074    }
2075
2076    #[test]
2077    fn test_opt_get_default() {
2078        let mut opts = Options::new();
2079        opts.optflag("h", "help", "Description");
2080        opts.optflagopt("i", "ignore", "Description", "true | false");
2081        opts.optflagopt("r", "run", "Description", "0 .. 10");
2082        opts.optflagopt("p", "percent", "Description", "0.0 .. 10.0");
2083        opts.long_only(false);
2084
2085        let args: Vec<String> = [
2086            "-i", "true", "-p", "1.1"
2087        ].iter().map(|x| x.to_string()).collect();
2088        let matches = &match opts.parse(&args) {
2089            Ok(m) => m,
2090            Err(e) => panic!("{}", e)
2091        };
2092        let h_arg =matches.opt_get_default("help", 10);
2093        assert_eq!(h_arg, Ok(10));
2094        let i_arg = matches.opt_get_default("i", false);
2095        assert_eq!(i_arg, Ok(true));
2096        let p_arg = matches.opt_get_default("p", 10.2);
2097        assert_eq!(p_arg, Ok(1.1));
2098    }
2099}