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.
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.
11 //! Simple getopt alternative.
13 //! Construct a vector of options, either by using `reqopt`, `optopt`, and `optflag`
14 //! or by building them from components yourself, and pass them to `getopts`,
15 //! along with a vector of actual arguments (not including `argv[0]`). You'll
16 //! either get a failure code back, or a match. You'll have to verify whether
17 //! the amount of 'free' arguments in the match is what you expect. Use `opt_*`
18 //! accessors to get argument values out of the matches object.
20 //! Single-character options are expected to appear on the command line with a
21 //! single preceding dash; multiple-character options are expected to be
22 //! proceeded by two dashes. Options that expect an argument accept their
23 //! argument following either a space or an equals sign. Single-character
24 //! options don't require the space.
28 //! The following example shows simple command line parsing for an application
29 //! that requires an input file to be specified, accepts an optional output
30 //! file name following `-o`, and accepts both `-h` and `--help` as optional flags.
33 //! #![feature(rustc_private)]
35 //! extern crate getopts;
36 //! use getopts::{optopt,optflag,getopts,OptGroup,usage};
39 //! fn do_work(inp: &str, out: Option<String>) {
40 //! println!("{}", inp);
42 //! Some(x) => println!("{}", x),
43 //! None => println!("No Output"),
47 //! fn print_usage(program: &str, opts: &[OptGroup]) {
48 //! let brief = format!("Usage: {} [options]", program);
49 //! print!("{}", usage(&brief, opts));
53 //! let args: Vec<String> = env::args().collect();
55 //! let program = args[0].clone();
58 //! optopt("o", "", "set output file name", "NAME"),
59 //! optflag("h", "help", "print this help menu")
61 //! let matches = match getopts(&args[1..], opts) {
63 //! Err(f) => { panic!(f.to_string()) }
65 //! if matches.opt_present("h") {
66 //! print_usage(&program, opts);
69 //! let output = matches.opt_str("o");
70 //! let input = if !matches.free.is_empty() {
71 //! matches.free[0].clone()
73 //! print_usage(&program, opts);
76 //! do_work(&input, output);
80 #![crate_name = "getopts"]
81 #![cfg_attr(stage0, unstable(feature = "rustc_private",
82 reason = "use the crates.io `getopts` library instead",
84 #![crate_type = "rlib"]
85 #![crate_type = "dylib"]
86 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
87 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
88 html_root_url = "https://doc.rust-lang.org/nightly/",
89 html_playground_url = "https://play.rust-lang.org/",
90 test(attr(deny(warnings))))]
92 #![deny(missing_docs)]
94 #![cfg_attr(stage0, feature(staged_api))]
101 use self::SplitWithinState::*;
102 use self::Whitespace::*;
103 use self::LengthLimit::*;
106 use std::iter::repeat;
109 /// Name of an option. Either a string or a single char.
110 #[derive(Clone, PartialEq, Eq, Debug)]
112 /// A string representing the long name of an option.
113 /// For example: "help"
115 /// A char representing the short name of an option.
120 /// Describes whether an option has an argument.
121 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
123 /// The option requires an argument.
125 /// The option takes no argument.
127 /// The option argument is optional.
131 /// Describes how often an option may occur.
132 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
134 /// The option occurs once.
136 /// The option occurs at most once.
138 /// The option occurs zero or more times.
142 /// A description of a possible option.
143 #[derive(Clone, PartialEq, Eq, Debug)]
145 /// Name of the option
147 /// Whether it has an argument
149 /// How often it can occur
151 /// Which options it aliases
152 pub aliases: Vec<Opt>,
155 /// One group of options, e.g., both `-h` and `--help`, along with
156 /// their shared description and properties.
157 #[derive(Clone, PartialEq, Eq, Debug)]
158 pub struct OptGroup {
159 /// Short name of the option, e.g. `h` for a `-h` option
160 pub short_name: String,
161 /// Long name of the option, e.g. `help` for a `--help` option
162 pub long_name: String,
163 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
165 /// Description for usage help text
167 /// Whether option has an argument
169 /// How often it can occur
173 /// Describes whether an option is given at all or has a value.
174 #[derive(Clone, PartialEq, Eq, Debug)]
180 /// The result of checking command line arguments. Contains a vector
181 /// of matches and a vector of free strings.
182 #[derive(Clone, PartialEq, Eq, Debug)]
184 /// Options that matched
186 /// Values of the Options that matched
187 vals: Vec<Vec<Optval>>,
188 /// Free string fragments
189 pub free: Vec<String>,
192 /// The type returned when the command line does not conform to the
193 /// expected format. Use the `Debug` implementation to output detailed
195 #[derive(Clone, PartialEq, Eq, Debug)]
197 /// The option requires an argument but none was passed.
198 ArgumentMissing(String),
199 /// The passed option is not declared among the possible options.
200 UnrecognizedOption(String),
201 /// A required option is not present.
202 OptionMissing(String),
203 /// A single occurrence option is being used multiple times.
204 OptionDuplicated(String),
205 /// There's an argument being passed to a non-argument option.
206 UnexpectedArgument(String),
209 /// The type of failure that occurred.
210 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
211 #[allow(missing_docs)]
220 /// The result of parsing a command line with a set of options.
221 pub type Result = result::Result<Matches, Fail>;
224 fn from_str(nm: &str) -> Name {
226 Short(nm.chars().next().unwrap())
232 fn to_string(&self) -> String {
234 Short(ch) => ch.to_string(),
235 Long(ref s) => s.to_owned(),
241 /// Translate OptGroup into Opt.
242 /// (Both short and long names correspond to different Opts).
243 pub fn long_to_short(&self) -> Opt {
252 match (short_name.len(), long_name.len()) {
253 (0, 0) => panic!("this long-format option was given no name"),
256 name: Long((long_name)),
264 name: Short(short_name.chars().next().unwrap()),
272 name: Long((long_name)),
276 name: Short(short_name.chars().next().unwrap()),
283 _ => panic!("something is wrong with the long-form opt"),
289 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
290 match find_opt(&self.opts[..], Name::from_str(nm)) {
291 Some(id) => self.vals[id].clone(),
292 None => panic!("No option '{}' defined", nm),
296 fn opt_val(&self, nm: &str) -> Option<Optval> {
297 let vals = self.opt_vals(nm);
301 Some(vals[0].clone())
305 /// Returns true if an option was matched.
306 pub fn opt_present(&self, nm: &str) -> bool {
307 !self.opt_vals(nm).is_empty()
310 /// Returns the number of times an option was matched.
311 pub fn opt_count(&self, nm: &str) -> usize {
312 self.opt_vals(nm).len()
315 /// Returns true if any of several options were matched.
316 pub fn opts_present(&self, names: &[String]) -> bool {
318 match find_opt(&self.opts, Name::from_str(&**nm)) {
319 Some(id) if !self.vals[id].is_empty() => return true,
326 /// Returns the string argument supplied to one of several matching options or `None`.
327 pub fn opts_str(&self, names: &[String]) -> Option<String> {
329 if let Some(Val(ref s)) = self.opt_val(&nm[..]) {
330 return Some(s.clone())
336 /// Returns a vector of the arguments provided to all matches of the given
339 /// Used when an option accepts multiple values.
340 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
341 let mut acc: Vec<String> = Vec::new();
342 let r = self.opt_vals(nm);
345 Val(ref s) => acc.push((*s).clone()),
352 /// Returns the string argument supplied to a matching option or `None`.
353 pub fn opt_str(&self, nm: &str) -> Option<String> {
354 let vals = self.opt_vals(nm);
356 return None::<String>;
359 Val(ref s) => Some((*s).clone()),
365 /// Returns the matching string, a default, or none.
367 /// Returns none if the option was not present, `def` if the option was
368 /// present but no argument was provided, and the argument if the option was
369 /// present and an argument was provided.
370 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
371 let vals = self.opt_vals(nm);
376 Val(ref s) => Some((*s).clone()),
377 _ => Some(def.to_owned()),
383 fn is_arg(arg: &str) -> bool {
384 arg.len() > 1 && arg.as_bytes()[0] == b'-'
387 fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
388 // Search main options.
389 let pos = opts.iter().position(|opt| opt.name == nm);
394 // Search in aliases.
395 for candidate in opts {
396 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
397 return opts.iter().position(|opt| opt.name == candidate.name);
404 /// Create a long option that is required and takes an argument.
406 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
407 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
408 /// * `desc` - Description for usage help
409 /// * `hint` - Hint that is used in place of the argument in the usage help,
410 /// e.g. `"FILE"` for a `-o FILE` option
411 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
412 let len = short_name.len();
413 assert!(len == 1 || len == 0);
415 short_name: short_name.to_owned(),
416 long_name: long_name.to_owned(),
417 hint: hint.to_owned(),
418 desc: desc.to_owned(),
424 /// Create a long option that is optional and takes an argument.
426 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
427 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
428 /// * `desc` - Description for usage help
429 /// * `hint` - Hint that is used in place of the argument in the usage help,
430 /// e.g. `"FILE"` for a `-o FILE` option
431 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
432 let len = short_name.len();
433 assert!(len == 1 || len == 0);
435 short_name: short_name.to_owned(),
436 long_name: long_name.to_owned(),
437 hint: hint.to_owned(),
438 desc: desc.to_owned(),
444 /// Create a long option that is optional and does not take an argument.
446 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
447 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
448 /// * `desc` - Description for usage help
449 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
450 let len = short_name.len();
451 assert!(len == 1 || len == 0);
453 short_name: short_name.to_owned(),
454 long_name: long_name.to_owned(),
456 desc: desc.to_owned(),
462 /// Create a long option that can occur more than once and does not
463 /// take an argument.
465 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
466 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
467 /// * `desc` - Description for usage help
468 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
469 let len = short_name.len();
470 assert!(len == 1 || len == 0);
472 short_name: short_name.to_owned(),
473 long_name: long_name.to_owned(),
475 desc: desc.to_owned(),
481 /// Create a long option that is optional and takes an optional argument.
483 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
484 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
485 /// * `desc` - Description for usage help
486 /// * `hint` - Hint that is used in place of the argument in the usage help,
487 /// e.g. `"FILE"` for a `-o FILE` option
488 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
489 let len = short_name.len();
490 assert!(len == 1 || len == 0);
492 short_name: short_name.to_owned(),
493 long_name: long_name.to_owned(),
494 hint: hint.to_owned(),
495 desc: desc.to_owned(),
501 /// Create a long option that is optional, takes an argument, and may occur
504 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
505 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
506 /// * `desc` - Description for usage help
507 /// * `hint` - Hint that is used in place of the argument in the usage help,
508 /// e.g. `"FILE"` for a `-o FILE` option
509 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
510 let len = short_name.len();
511 assert!(len == 1 || len == 0);
513 short_name: short_name.to_owned(),
514 long_name: long_name.to_owned(),
515 hint: hint.to_owned(),
516 desc: desc.to_owned(),
522 /// Create a generic option group, stating all parameters explicitly
523 pub fn opt(short_name: &str,
530 let len = short_name.len();
531 assert!(len == 1 || len == 0);
533 short_name: short_name.to_owned(),
534 long_name: long_name.to_owned(),
535 hint: hint.to_owned(),
536 desc: desc.to_owned(),
542 impl fmt::Display for Fail {
543 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
545 ArgumentMissing(ref nm) => write!(f, "Argument to option '{}' missing.", *nm),
546 UnrecognizedOption(ref nm) => write!(f, "Unrecognized option: '{}'.", *nm),
547 OptionMissing(ref nm) => write!(f, "Required option '{}' missing.", *nm),
548 OptionDuplicated(ref nm) => write!(f, "Option '{}' given more than once.", *nm),
549 UnexpectedArgument(ref nm) => write!(f, "Option '{}' does not take an argument.", *nm),
554 /// Parse command line arguments according to the provided options.
556 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
557 /// `opt_str`, etc. to interrogate results.
560 /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` to display
561 /// information about it.
562 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
563 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
564 let n_opts = opts.len();
566 fn f(_x: usize) -> Vec<Optval> {
570 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
571 let mut free: Vec<String> = Vec::new();
575 let cur = args[i].clone();
576 let curlen = cur.len();
577 if !is_arg(&cur[..]) {
579 } else if cur == "--" {
582 free.push(args[j].clone());
588 let mut i_arg = None;
589 if cur.as_bytes()[1] == b'-' {
590 let tail = &cur[2..curlen];
591 let tail_eq: Vec<&str> = tail.splitn(2, '=').collect();
592 if tail_eq.len() <= 1 {
593 names = vec![Long(tail.to_owned())];
595 names = vec![Long(tail_eq[0].to_owned())];
596 i_arg = Some(tail_eq[1].to_owned());
602 let ch = cur[j..].chars().next().unwrap();
605 // In a series of potential options (eg. -aheJ), if we
606 // see one which takes an argument, we assume all
607 // subsequent characters make up the argument. This
608 // allows options such as -L/usr/local/lib/foo to be
609 // interpreted correctly
611 let opt_id = match find_opt(&opts, opt.clone()) {
613 None => return Err(UnrecognizedOption(opt.to_string())),
618 let arg_follows = match opts[opt_id].hasarg {
623 let next = j + ch.len_utf8();
624 if arg_follows && next < curlen {
625 i_arg = Some((&cur[next..curlen]).to_owned());
632 let mut name_pos = 0;
635 let optid = match find_opt(&opts, (*nm).clone()) {
637 None => return Err(UnrecognizedOption(nm.to_string())),
639 match opts[optid].hasarg {
641 if name_pos == names.len() && !i_arg.is_none() {
642 return Err(UnexpectedArgument(nm.to_string()));
644 let v = &mut vals[optid];
648 if !i_arg.is_none() {
649 let v = &mut vals[optid];
650 v.push(Val((i_arg.clone()).unwrap()));
651 } else if name_pos < names.len() || i + 1 == l || is_arg(&args[i + 1][..]) {
652 let v = &mut vals[optid];
656 let v = &mut vals[optid];
657 v.push(Val(args[i].clone()));
661 if !i_arg.is_none() {
662 let v = &mut vals[optid];
663 v.push(Val(i_arg.clone().unwrap()));
664 } else if i + 1 == l {
665 return Err(ArgumentMissing(nm.to_string()));
668 let v = &mut vals[optid];
669 v.push(Val(args[i].clone()));
678 let n = vals[i].len();
679 let occ = opts[i].occur;
680 if occ == Req && n == 0 {
681 return Err(OptionMissing(opts[i].name.to_string()));
683 if occ != Multi && n > 1 {
684 return Err(OptionDuplicated(opts[i].name.to_string()));
694 /// Derive a usage message from a set of long options.
695 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
697 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
699 let rows = opts.iter().map(|optref| {
700 let OptGroup{short_name,
705 ..} = (*optref).clone();
707 let mut row = repeat(" ").take(4).collect::<String>();
710 match short_name.len() {
714 row.push_str(&short_name[..]);
717 _ => panic!("the short name should only be 1 ascii char long"),
721 match long_name.len() {
725 row.push_str(&long_name[..]);
733 Yes => row.push_str(&hint[..]),
736 row.push_str(&hint[..]);
741 // FIXME: #5516 should be graphemes not codepoints
742 // here we just need to indent the start of the description
743 let rowlen = row.chars().count();
745 for _ in 0..24 - rowlen {
749 row.push_str(&desc_sep[..]);
752 // Normalize desc to contain words separated by one space character
753 let mut desc_normalized_whitespace = String::new();
754 for word in desc.split_whitespace() {
755 desc_normalized_whitespace.push_str(word);
756 desc_normalized_whitespace.push(' ');
759 // FIXME: #5516 should be graphemes not codepoints
760 let mut desc_rows = Vec::new();
761 each_split_within(&desc_normalized_whitespace[..], 54, |substr| {
762 desc_rows.push(substr.to_owned());
766 // FIXME: #5516 should be graphemes not codepoints
767 // wrapped description
768 row.push_str(&desc_rows.join(&desc_sep[..]));
773 format!("{}\n\nOptions:\n{}\n",
775 rows.collect::<Vec<String>>().join("\n"))
778 fn format_option(opt: &OptGroup) -> String {
779 let mut line = String::new();
781 if opt.occur != Req {
785 // Use short_name is possible, but fallback to long_name.
786 if !opt.short_name.is_empty() {
788 line.push_str(&opt.short_name[..]);
791 line.push_str(&opt.long_name[..]);
794 if opt.hasarg != No {
796 if opt.hasarg == Maybe {
799 line.push_str(&opt.hint[..]);
800 if opt.hasarg == Maybe {
805 if opt.occur != Req {
808 if opt.occur == Multi {
815 /// Derive a short one-line usage summary from a set of long options.
816 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
817 let mut line = format!("Usage: {} ", program_name);
818 line.push_str(&opts.iter()
820 .collect::<Vec<String>>()
825 #[derive(Copy, Clone)]
826 enum SplitWithinState {
827 A, // leading whitespace, initial state
829 C, // internal and trailing whitespace
831 #[derive(Copy, Clone)]
833 Ws, // current char is whitespace
834 Cr, // current char is not whitespace
836 #[derive(Copy, Clone)]
838 UnderLim, // current char makes current substring still fit in limit
839 OverLim, // current char makes current substring no longer fit in limit
843 /// Splits a string into substrings with possibly internal whitespace,
844 /// each of them at most `lim` bytes long. The substrings have leading and trailing
845 /// whitespace removed, and are only cut at whitespace boundaries.
847 /// Note: Function was moved here from `std::str` because this module is the only place that
848 /// uses it, and because it was too specific for a general string function.
852 /// Panics during iteration if the string contains a non-whitespace
853 /// sequence longer than the limit.
854 fn each_split_within<F>(ss: &str, lim: usize, mut it: F) -> bool
855 where F: FnMut(&str) -> bool
857 // Just for fun, let's write this as a state machine:
859 let mut slice_start = 0;
860 let mut last_start = 0;
861 let mut last_end = 0;
863 let mut fake_i = ss.len();
868 // if the limit is larger than the string, lower it to save cycles
873 let mut machine = |cont: &mut bool, (i, c): (usize, char)| -> bool {
874 let whitespace = if c.is_whitespace() {
879 let limit = if (i - slice_start + 1) <= lim {
885 state = match (state, whitespace, limit) {
893 (B, Cr, UnderLim) => B,
894 (B, Cr, OverLim) if (i - last_start + 1) > lim => {
895 panic!("word starting with {} longer than limit!",
896 &ss[last_start..i + 1])
898 (B, Cr, OverLim) => {
899 *cont = it(&ss[slice_start..last_end]);
900 slice_start = last_start;
903 (B, Ws, UnderLim) => {
907 (B, Ws, OverLim) => {
909 *cont = it(&ss[slice_start..last_end]);
913 (C, Cr, UnderLim) => {
917 (C, Cr, OverLim) => {
918 *cont = it(&ss[slice_start..last_end]);
924 (C, Ws, OverLim) => {
925 *cont = it(&ss[slice_start..last_end]);
928 (C, Ws, UnderLim) => C,
934 ss.char_indices().all(|x| machine(&mut cont, x));
936 // Let the automaton 'run out' by supplying trailing whitespace
942 machine(&mut cont, (fake_i, ' '));
949 fn test_split_within() {
950 fn t(s: &str, i: usize, u: &[String]) {
951 let mut v = Vec::new();
952 each_split_within(s, i, |s| {
953 v.push(s.to_string());
956 assert!(v.iter().zip(u).all(|(a, b)| a == b));
960 t("hello", 15, &["hello".to_string()]);
961 t("\nMary had a little lamb\nLittle lamb\n",
963 &["Mary had a".to_string(), "little lamb".to_string(), "Little lamb".to_string()]);
964 t("\nMary had a little lamb\nLittle lamb\n",
966 &["Mary had a little lamb\nLittle lamb".to_string()]);
973 use std::result::Result::{Err, Ok};
979 let long_args = vec!["--test=20".to_string()];
980 let opts = vec![reqopt("t", "test", "testing", "TEST")];
981 let rs = getopts(&long_args, &opts);
984 assert!(m.opt_present("test"));
985 assert_eq!(m.opt_str("test").unwrap(), "20");
986 assert!(m.opt_present("t"));
987 assert_eq!(m.opt_str("t").unwrap(), "20");
990 panic!("test_reqopt failed (long arg)");
993 let short_args = vec!["-t".to_string(), "20".to_string()];
994 match getopts(&short_args, &opts) {
996 assert!((m.opt_present("test")));
997 assert_eq!(m.opt_str("test").unwrap(), "20");
998 assert!((m.opt_present("t")));
999 assert_eq!(m.opt_str("t").unwrap(), "20");
1002 panic!("test_reqopt failed (short arg)");
1008 fn test_reqopt_missing() {
1009 let args = vec!["blah".to_string()];
1010 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1011 let rs = getopts(&args, &opts);
1013 Err(OptionMissing(_)) => {}
1019 fn test_reqopt_no_arg() {
1020 let long_args = vec!["--test".to_string()];
1021 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1022 let rs = getopts(&long_args, &opts);
1024 Err(ArgumentMissing(_)) => {}
1027 let short_args = vec!["-t".to_string()];
1028 match getopts(&short_args, &opts) {
1029 Err(ArgumentMissing(_)) => {}
1035 fn test_reqopt_multi() {
1036 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1037 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1038 let rs = getopts(&args, &opts);
1040 Err(OptionDuplicated(_)) => {}
1048 let long_args = vec!["--test=20".to_string()];
1049 let opts = vec![optopt("t", "test", "testing", "TEST")];
1050 let rs = getopts(&long_args, &opts);
1053 assert!(m.opt_present("test"));
1054 assert_eq!(m.opt_str("test").unwrap(), "20");
1055 assert!((m.opt_present("t")));
1056 assert_eq!(m.opt_str("t").unwrap(), "20");
1060 let short_args = vec!["-t".to_string(), "20".to_string()];
1061 match getopts(&short_args, &opts) {
1063 assert!((m.opt_present("test")));
1064 assert_eq!(m.opt_str("test").unwrap(), "20");
1065 assert!((m.opt_present("t")));
1066 assert_eq!(m.opt_str("t").unwrap(), "20");
1073 fn test_optopt_missing() {
1074 let args = vec!["blah".to_string()];
1075 let opts = vec![optopt("t", "test", "testing", "TEST")];
1076 let rs = getopts(&args, &opts);
1079 assert!(!m.opt_present("test"));
1080 assert!(!m.opt_present("t"));
1087 fn test_optopt_no_arg() {
1088 let long_args = vec!["--test".to_string()];
1089 let opts = vec![optopt("t", "test", "testing", "TEST")];
1090 let rs = getopts(&long_args, &opts);
1092 Err(ArgumentMissing(_)) => {}
1095 let short_args = vec!["-t".to_string()];
1096 match getopts(&short_args, &opts) {
1097 Err(ArgumentMissing(_)) => {}
1103 fn test_optopt_multi() {
1104 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1105 let opts = vec![optopt("t", "test", "testing", "TEST")];
1106 let rs = getopts(&args, &opts);
1108 Err(OptionDuplicated(_)) => {}
1113 // Tests for optflag
1116 let long_args = vec!["--test".to_string()];
1117 let opts = vec![optflag("t", "test", "testing")];
1118 let rs = getopts(&long_args, &opts);
1121 assert!(m.opt_present("test"));
1122 assert!(m.opt_present("t"));
1126 let short_args = vec!["-t".to_string()];
1127 match getopts(&short_args, &opts) {
1129 assert!(m.opt_present("test"));
1130 assert!(m.opt_present("t"));
1137 fn test_optflag_missing() {
1138 let args = vec!["blah".to_string()];
1139 let opts = vec![optflag("t", "test", "testing")];
1140 let rs = getopts(&args, &opts);
1143 assert!(!m.opt_present("test"));
1144 assert!(!m.opt_present("t"));
1151 fn test_optflag_long_arg() {
1152 let args = vec!["--test=20".to_string()];
1153 let opts = vec![optflag("t", "test", "testing")];
1154 let rs = getopts(&args, &opts);
1156 Err(UnexpectedArgument(_)) => {}
1162 fn test_optflag_multi() {
1163 let args = vec!["--test".to_string(), "-t".to_string()];
1164 let opts = vec![optflag("t", "test", "testing")];
1165 let rs = getopts(&args, &opts);
1167 Err(OptionDuplicated(_)) => {}
1173 fn test_optflag_short_arg() {
1174 let args = vec!["-t".to_string(), "20".to_string()];
1175 let opts = vec![optflag("t", "test", "testing")];
1176 let rs = getopts(&args, &opts);
1179 // The next variable after the flag is just a free argument
1181 assert!(m.free[0] == "20");
1187 // Tests for optflagmulti
1189 fn test_optflagmulti_short1() {
1190 let args = vec!["-v".to_string()];
1191 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1192 let rs = getopts(&args, &opts);
1195 assert_eq!(m.opt_count("v"), 1);
1202 fn test_optflagmulti_short2a() {
1203 let args = vec!["-v".to_string(), "-v".to_string()];
1204 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1205 let rs = getopts(&args, &opts);
1208 assert_eq!(m.opt_count("v"), 2);
1215 fn test_optflagmulti_short2b() {
1216 let args = vec!["-vv".to_string()];
1217 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1218 let rs = getopts(&args, &opts);
1221 assert_eq!(m.opt_count("v"), 2);
1228 fn test_optflagmulti_long1() {
1229 let args = vec!["--verbose".to_string()];
1230 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1231 let rs = getopts(&args, &opts);
1234 assert_eq!(m.opt_count("verbose"), 1);
1241 fn test_optflagmulti_long2() {
1242 let args = vec!["--verbose".to_string(), "--verbose".to_string()];
1243 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1244 let rs = getopts(&args, &opts);
1247 assert_eq!(m.opt_count("verbose"), 2);
1254 fn test_optflagmulti_mix() {
1255 let args = vec!["--verbose".to_string(),
1258 "verbose".to_string()];
1259 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1260 let rs = getopts(&args, &opts);
1263 assert_eq!(m.opt_count("verbose"), 4);
1264 assert_eq!(m.opt_count("v"), 4);
1270 // Tests for optmulti
1272 fn test_optmulti() {
1273 let long_args = vec!["--test=20".to_string()];
1274 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1275 let rs = getopts(&long_args, &opts);
1278 assert!((m.opt_present("test")));
1279 assert_eq!(m.opt_str("test").unwrap(), "20");
1280 assert!((m.opt_present("t")));
1281 assert_eq!(m.opt_str("t").unwrap(), "20");
1285 let short_args = vec!["-t".to_string(), "20".to_string()];
1286 match getopts(&short_args, &opts) {
1288 assert!((m.opt_present("test")));
1289 assert_eq!(m.opt_str("test").unwrap(), "20");
1290 assert!((m.opt_present("t")));
1291 assert_eq!(m.opt_str("t").unwrap(), "20");
1298 fn test_optmulti_missing() {
1299 let args = vec!["blah".to_string()];
1300 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1301 let rs = getopts(&args, &opts);
1304 assert!(!m.opt_present("test"));
1305 assert!(!m.opt_present("t"));
1312 fn test_optmulti_no_arg() {
1313 let long_args = vec!["--test".to_string()];
1314 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1315 let rs = getopts(&long_args, &opts);
1317 Err(ArgumentMissing(_)) => {}
1320 let short_args = vec!["-t".to_string()];
1321 match getopts(&short_args, &opts) {
1322 Err(ArgumentMissing(_)) => {}
1328 fn test_optmulti_multi() {
1329 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1330 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1331 let rs = getopts(&args, &opts);
1334 assert!(m.opt_present("test"));
1335 assert_eq!(m.opt_str("test").unwrap(), "20");
1336 assert!(m.opt_present("t"));
1337 assert_eq!(m.opt_str("t").unwrap(), "20");
1338 let pair = m.opt_strs("test");
1339 assert!(pair[0] == "20");
1340 assert!(pair[1] == "30");
1347 fn test_unrecognized_option() {
1348 let long_args = vec!["--untest".to_string()];
1349 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1350 let rs = getopts(&long_args, &opts);
1352 Err(UnrecognizedOption(_)) => {}
1355 let short_args = vec!["-u".to_string()];
1356 match getopts(&short_args, &opts) {
1357 Err(UnrecognizedOption(_)) => {}
1363 fn test_combined() {
1364 let args = vec!["prog".to_string(),
1365 "free1".to_string(),
1368 "free2".to_string(),
1369 "--flag".to_string(),
1370 "--long=30".to_string(),
1379 "-60 70".to_string()];
1380 let opts = vec![optopt("s", "something", "something", "SOMETHING"),
1381 optflag("", "flag", "a flag"),
1382 reqopt("", "long", "hi", "LONG"),
1383 optflag("f", "", "another flag"),
1384 optmulti("m", "", "mmmmmm", "YUM"),
1385 optmulti("n", "", "nothing", "NOTHING"),
1386 optopt("", "notpresent", "nothing to see here", "NOPE")];
1387 let rs = getopts(&args, &opts);
1390 assert!(m.free[0] == "prog");
1391 assert!(m.free[1] == "free1");
1392 assert_eq!(m.opt_str("s").unwrap(), "20");
1393 assert!(m.free[2] == "free2");
1394 assert!((m.opt_present("flag")));
1395 assert_eq!(m.opt_str("long").unwrap(), "30");
1396 assert!((m.opt_present("f")));
1397 let pair = m.opt_strs("m");
1398 assert!(pair[0] == "40");
1399 assert!(pair[1] == "50");
1400 let pair = m.opt_strs("n");
1401 assert!(pair[0] == "-A B");
1402 assert!(pair[1] == "-60 70");
1403 assert!((!m.opt_present("notpresent")));
1411 let opts = vec![optopt("e", "", "encrypt", "ENCRYPT"),
1412 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1413 optopt("f", "", "flag", "FLAG")];
1415 let args_single = vec!["-e".to_string(), "foo".to_string()];
1416 let matches_single = &match getopts(&args_single, &opts) {
1417 result::Result::Ok(m) => m,
1418 result::Result::Err(_) => panic!(),
1420 assert!(matches_single.opts_present(&["e".to_string()]));
1421 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1422 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1423 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1424 assert!(!matches_single.opts_present(&["thing".to_string()]));
1425 assert!(!matches_single.opts_present(&[]));
1427 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1428 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1430 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1433 let args_both = vec!["-e".to_string(),
1435 "--encrypt".to_string(),
1437 let matches_both = &match getopts(&args_both, &opts) {
1438 result::Result::Ok(m) => m,
1439 result::Result::Err(_) => panic!(),
1441 assert!(matches_both.opts_present(&["e".to_string()]));
1442 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1443 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1444 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1445 assert!(!matches_both.opts_present(&["f".to_string()]));
1446 assert!(!matches_both.opts_present(&["thing".to_string()]));
1447 assert!(!matches_both.opts_present(&[]));
1449 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1450 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(),
1452 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1454 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1460 let args = vec!["-Lfoo".to_string(), "-M.".to_string()];
1461 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1462 optmulti("M", "", "something", "MMMM")];
1463 let matches = &match getopts(&args, &opts) {
1464 result::Result::Ok(m) => m,
1465 result::Result::Err(_) => panic!(),
1467 assert!(matches.opts_present(&["L".to_string()]));
1468 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1469 assert!(matches.opts_present(&["M".to_string()]));
1470 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1475 fn test_nospace_conflict() {
1476 let args = vec!["-vvLverbose".to_string(), "-v".to_string()];
1477 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1478 optflagmulti("v", "verbose", "Verbose")];
1479 let matches = &match getopts(&args, &opts) {
1480 result::Result::Ok(m) => m,
1481 result::Result::Err(e) => panic!("{}", e),
1483 assert!(matches.opts_present(&["L".to_string()]));
1484 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1485 assert!(matches.opts_present(&["v".to_string()]));
1486 assert_eq!(3, matches.opt_count("v"));
1490 fn test_long_to_short() {
1491 let mut short = Opt {
1492 name: Name::Long("banana".to_string()),
1493 hasarg: HasArg::Yes,
1495 aliases: Vec::new(),
1497 short.aliases = vec![Opt {
1498 name: Name::Short('b'),
1499 hasarg: HasArg::Yes,
1501 aliases: Vec::new(),
1503 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1505 assert!(verbose.long_to_short() == short);
1509 fn test_aliases_long_and_short() {
1510 let opts = vec![optflagmulti("a", "apple", "Desc")];
1512 let args = vec!["-a".to_string(), "--apple".to_string(), "-a".to_string()];
1514 let matches = getopts(&args, &opts).unwrap();
1515 assert_eq!(3, matches.opt_count("a"));
1516 assert_eq!(3, matches.opt_count("apple"));
1521 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1522 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1523 optflag("k", "kiwi", "Desc"),
1524 optflagopt("p", "", "Desc", "VAL"),
1525 optmulti("l", "", "Desc", "VAL")];
1531 -b --banana VAL Desc
1532 -a --012345678901234567890123456789 VAL
1539 let generated_usage = usage("Usage: fruits", &optgroups);
1541 assert_eq!(generated_usage, expected);
1545 fn test_usage_description_wrapping() {
1546 // indentation should be 24 spaces
1547 // lines wrap after 78: or rather descriptions wrap after 54
1549 let optgroups = vec![optflag("k",
1552 "This is a long description which won't be wrapped..+.."),
1555 "This is a long description which _will_ be wrapped..+..")];
1561 -k --kiwi This is a long description which won't be wrapped..+..
1562 -a --apple This is a long description which _will_ be
1566 let usage = usage("Usage: fruits", &optgroups);
1568 assert!(usage == expected)
1572 fn test_usage_description_multibyte_handling() {
1573 let optgroups = vec![optflag("k",
1574 "k\u{2013}w\u{2013}",
1575 "The word kiwi is normally spelled with two i's"),
1578 "This \u{201C}description\u{201D} has some characters that \
1579 could confuse the line wrapping; an apple costs 0.51€ in \
1580 some parts of Europe.")];
1586 -k --k–w– The word kiwi is normally spelled with two i's
1587 -a --apple This “description” has some characters that could
1588 confuse the line wrapping; an apple costs 0.51€ in
1589 some parts of Europe.
1592 let usage = usage("Usage: fruits", &optgroups);
1594 assert!(usage == expected)
1598 fn test_short_usage() {
1599 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1600 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1601 optflag("k", "kiwi", "Desc"),
1602 optflagopt("p", "", "Desc", "VAL"),
1603 optmulti("l", "", "Desc", "VAL")];
1605 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1606 let generated_usage = short_usage("fruits", &optgroups);
1608 assert_eq!(generated_usage, expected);
1612 fn test_args_with_equals() {
1613 let args = vec!["--one".to_string(), "A=B".to_string(),
1614 "--two=C=D".to_string()];
1615 let opts = vec![optopt("o", "one", "One", "INFO"),
1616 optopt("t", "two", "Two", "INFO")];
1617 let matches = &match getopts(&args, &opts) {
1618 result::Result::Ok(m) => m,
1619 result::Result::Err(e) => panic!("{}", e)
1621 assert_eq!(matches.opts_str(&["o".to_string()]).unwrap(), "A=B");
1622 assert_eq!(matches.opts_str(&["t".to_string()]).unwrap(), "C=D");