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 #![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)]
93 #![feature(staged_api)]
95 #![cfg_attr(test, feature(rustc_private))]
106 use self::SplitWithinState::*;
107 use self::Whitespace::*;
108 use self::LengthLimit::*;
111 use std::iter::repeat;
114 /// Name of an option. Either a string or a single char.
115 #[derive(Clone, PartialEq, Eq, Debug)]
117 /// A string representing the long name of an option.
118 /// For example: "help"
120 /// A char representing the short name of an option.
125 /// Describes whether an option has an argument.
126 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
128 /// The option requires an argument.
130 /// The option takes no argument.
132 /// The option argument is optional.
136 /// Describes how often an option may occur.
137 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
139 /// The option occurs once.
141 /// The option occurs at most once.
143 /// The option occurs zero or more times.
147 /// A description of a possible option.
148 #[derive(Clone, PartialEq, Eq, Debug)]
150 /// Name of the option
152 /// Whether it has an argument
154 /// How often it can occur
156 /// Which options it aliases
157 pub aliases: Vec<Opt>,
160 /// One group of options, e.g., both `-h` and `--help`, along with
161 /// their shared description and properties.
162 #[derive(Clone, PartialEq, Eq, Debug)]
163 pub struct OptGroup {
164 /// Short name of the option, e.g. `h` for a `-h` option
165 pub short_name: String,
166 /// Long name of the option, e.g. `help` for a `--help` option
167 pub long_name: String,
168 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
170 /// Description for usage help text
172 /// Whether option has an argument
174 /// How often it can occur
178 /// Describes whether an option is given at all or has a value.
179 #[derive(Clone, PartialEq, Eq, Debug)]
185 /// The result of checking command line arguments. Contains a vector
186 /// of matches and a vector of free strings.
187 #[derive(Clone, PartialEq, Eq, Debug)]
189 /// Options that matched
191 /// Values of the Options that matched
192 vals: Vec<Vec<Optval>>,
193 /// Free string fragments
194 pub free: Vec<String>,
197 /// The type returned when the command line does not conform to the
198 /// expected format. Use the `Debug` implementation to output detailed
200 #[derive(Clone, PartialEq, Eq, Debug)]
202 /// The option requires an argument but none was passed.
203 ArgumentMissing(String),
204 /// The passed option is not declared among the possible options.
205 UnrecognizedOption(String),
206 /// A required option is not present.
207 OptionMissing(String),
208 /// A single occurrence option is being used multiple times.
209 OptionDuplicated(String),
210 /// There's an argument being passed to a non-argument option.
211 UnexpectedArgument(String),
214 /// The type of failure that occurred.
215 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
216 #[allow(missing_docs)]
225 /// The result of parsing a command line with a set of options.
226 pub type Result = result::Result<Matches, Fail>;
229 fn from_str(nm: &str) -> Name {
237 fn to_string(&self) -> String {
239 Short(ch) => ch.to_string(),
240 Long(ref s) => s.to_owned(),
246 /// Translate OptGroup into Opt.
247 /// (Both short and long names correspond to different Opts).
248 pub fn long_to_short(&self) -> Opt {
257 match (short_name.len(), long_name.len()) {
258 (0, 0) => panic!("this long-format option was given no name"),
261 name: Long((long_name)),
269 name: Short(short_name.char_at(0)),
277 name: Long((long_name)),
281 name: Short(short_name.char_at(0)),
288 (_, _) => panic!("something is wrong with the long-form opt"),
294 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
295 match find_opt(&self.opts[..], Name::from_str(nm)) {
296 Some(id) => self.vals[id].clone(),
297 None => panic!("No option '{}' defined", nm),
301 fn opt_val(&self, nm: &str) -> Option<Optval> {
302 let vals = self.opt_vals(nm);
306 Some(vals[0].clone())
310 /// Returns true if an option was matched.
311 pub fn opt_present(&self, nm: &str) -> bool {
312 !self.opt_vals(nm).is_empty()
315 /// Returns the number of times an option was matched.
316 pub fn opt_count(&self, nm: &str) -> usize {
317 self.opt_vals(nm).len()
320 /// Returns true if any of several options were matched.
321 pub fn opts_present(&self, names: &[String]) -> bool {
323 match find_opt(&self.opts, Name::from_str(&**nm)) {
324 Some(id) if !self.vals[id].is_empty() => return true,
331 /// Returns the string argument supplied to one of several matching options or `None`.
332 pub fn opts_str(&self, names: &[String]) -> Option<String> {
334 match self.opt_val(&nm[..]) {
335 Some(Val(ref s)) => return Some(s.clone()),
342 /// Returns a vector of the arguments provided to all matches of the given
345 /// Used when an option accepts multiple values.
346 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
347 let mut acc: Vec<String> = Vec::new();
348 let r = self.opt_vals(nm);
351 Val(ref s) => acc.push((*s).clone()),
358 /// Returns the string argument supplied to a matching option or `None`.
359 pub fn opt_str(&self, nm: &str) -> Option<String> {
360 let vals = self.opt_vals(nm);
362 return None::<String>;
365 Val(ref s) => Some((*s).clone()),
371 /// Returns the matching string, a default, or none.
373 /// Returns none if the option was not present, `def` if the option was
374 /// present but no argument was provided, and the argument if the option was
375 /// present and an argument was provided.
376 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
377 let vals = self.opt_vals(nm);
382 Val(ref s) => Some((*s).clone()),
383 _ => Some(def.to_owned()),
389 fn is_arg(arg: &str) -> bool {
390 arg.len() > 1 && arg.as_bytes()[0] == b'-'
393 fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
394 // Search main options.
395 let pos = opts.iter().position(|opt| opt.name == nm);
400 // Search in aliases.
401 for candidate in opts {
402 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
403 return opts.iter().position(|opt| opt.name == candidate.name);
410 /// Create a long option that is required and takes an argument.
412 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
413 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
414 /// * `desc` - Description for usage help
415 /// * `hint` - Hint that is used in place of the argument in the usage help,
416 /// e.g. `"FILE"` for a `-o FILE` option
417 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
418 let len = short_name.len();
419 assert!(len == 1 || len == 0);
421 short_name: short_name.to_owned(),
422 long_name: long_name.to_owned(),
423 hint: hint.to_owned(),
424 desc: desc.to_owned(),
430 /// Create a long option that is optional and takes an argument.
432 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
433 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
434 /// * `desc` - Description for usage help
435 /// * `hint` - Hint that is used in place of the argument in the usage help,
436 /// e.g. `"FILE"` for a `-o FILE` option
437 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
438 let len = short_name.len();
439 assert!(len == 1 || len == 0);
441 short_name: short_name.to_owned(),
442 long_name: long_name.to_owned(),
443 hint: hint.to_owned(),
444 desc: desc.to_owned(),
450 /// Create a long option that is optional and does not take an argument.
452 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
453 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
454 /// * `desc` - Description for usage help
455 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
456 let len = short_name.len();
457 assert!(len == 1 || len == 0);
459 short_name: short_name.to_owned(),
460 long_name: long_name.to_owned(),
462 desc: desc.to_owned(),
468 /// Create a long option that can occur more than once and does not
469 /// take an argument.
471 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
472 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
473 /// * `desc` - Description for usage help
474 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
475 let len = short_name.len();
476 assert!(len == 1 || len == 0);
478 short_name: short_name.to_owned(),
479 long_name: long_name.to_owned(),
481 desc: desc.to_owned(),
487 /// Create a long option that is optional and takes an optional argument.
489 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
490 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
491 /// * `desc` - Description for usage help
492 /// * `hint` - Hint that is used in place of the argument in the usage help,
493 /// e.g. `"FILE"` for a `-o FILE` option
494 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
495 let len = short_name.len();
496 assert!(len == 1 || len == 0);
498 short_name: short_name.to_owned(),
499 long_name: long_name.to_owned(),
500 hint: hint.to_owned(),
501 desc: desc.to_owned(),
507 /// Create a long option that is optional, takes an argument, and may occur
510 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
511 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
512 /// * `desc` - Description for usage help
513 /// * `hint` - Hint that is used in place of the argument in the usage help,
514 /// e.g. `"FILE"` for a `-o FILE` option
515 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
516 let len = short_name.len();
517 assert!(len == 1 || len == 0);
519 short_name: short_name.to_owned(),
520 long_name: long_name.to_owned(),
521 hint: hint.to_owned(),
522 desc: desc.to_owned(),
528 /// Create a generic option group, stating all parameters explicitly
529 pub fn opt(short_name: &str,
536 let len = short_name.len();
537 assert!(len == 1 || len == 0);
539 short_name: short_name.to_owned(),
540 long_name: long_name.to_owned(),
541 hint: hint.to_owned(),
542 desc: desc.to_owned(),
548 impl fmt::Display for Fail {
549 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
551 ArgumentMissing(ref nm) => write!(f, "Argument to option '{}' missing.", *nm),
552 UnrecognizedOption(ref nm) => write!(f, "Unrecognized option: '{}'.", *nm),
553 OptionMissing(ref nm) => write!(f, "Required option '{}' missing.", *nm),
554 OptionDuplicated(ref nm) => write!(f, "Option '{}' given more than once.", *nm),
555 UnexpectedArgument(ref nm) => write!(f, "Option '{}' does not take an argument.", *nm),
560 /// Parse command line arguments according to the provided options.
562 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
563 /// `opt_str`, etc. to interrogate results.
566 /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` to display
567 /// information about it.
568 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
569 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
570 let n_opts = opts.len();
572 fn f(_x: usize) -> Vec<Optval> {
576 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
577 let mut free: Vec<String> = Vec::new();
581 let cur = args[i].clone();
582 let curlen = cur.len();
583 if !is_arg(&cur[..]) {
585 } else if cur == "--" {
588 free.push(args[j].clone());
594 let mut i_arg = None;
595 if cur.as_bytes()[1] == b'-' {
596 let tail = &cur[2..curlen];
597 let tail_eq: Vec<&str> = tail.splitn(2, '=').collect();
598 if tail_eq.len() <= 1 {
599 names = vec![Long(tail.to_owned())];
601 names = vec![Long(tail_eq[0].to_owned())];
602 i_arg = Some(tail_eq[1].to_owned());
608 let ch = cur.char_at(j);
611 // In a series of potential options (eg. -aheJ), if we
612 // see one which takes an argument, we assume all
613 // subsequent characters make up the argument. This
614 // allows options such as -L/usr/local/lib/foo to be
615 // interpreted correctly
617 let opt_id = match find_opt(&opts, opt.clone()) {
619 None => return Err(UnrecognizedOption(opt.to_string())),
624 let arg_follows = match opts[opt_id].hasarg {
629 let next = j + ch.len_utf8();
630 if arg_follows && next < curlen {
631 i_arg = Some((&cur[next..curlen]).to_owned());
638 let mut name_pos = 0;
641 let optid = match find_opt(&opts, (*nm).clone()) {
643 None => return Err(UnrecognizedOption(nm.to_string())),
645 match opts[optid].hasarg {
647 if name_pos == names.len() && !i_arg.is_none() {
648 return Err(UnexpectedArgument(nm.to_string()));
650 let v = &mut vals[optid];
654 if !i_arg.is_none() {
655 let v = &mut vals[optid];
656 v.push(Val((i_arg.clone()).unwrap()));
657 } else if name_pos < names.len() || i + 1 == l || is_arg(&args[i + 1][..]) {
658 let v = &mut vals[optid];
662 let v = &mut vals[optid];
663 v.push(Val(args[i].clone()));
667 if !i_arg.is_none() {
668 let v = &mut vals[optid];
669 v.push(Val(i_arg.clone().unwrap()));
670 } else if i + 1 == l {
671 return Err(ArgumentMissing(nm.to_string()));
674 let v = &mut vals[optid];
675 v.push(Val(args[i].clone()));
684 let n = vals[i].len();
685 let occ = opts[i].occur;
686 if occ == Req && n == 0 {
687 return Err(OptionMissing(opts[i].name.to_string()));
689 if occ != Multi && n > 1 {
690 return Err(OptionDuplicated(opts[i].name.to_string()));
700 /// Derive a usage message from a set of long options.
701 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
703 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
705 let rows = opts.iter().map(|optref| {
706 let OptGroup{short_name,
711 ..} = (*optref).clone();
713 let mut row = repeat(" ").take(4).collect::<String>();
716 match short_name.len() {
720 row.push_str(&short_name[..]);
723 _ => panic!("the short name should only be 1 ascii char long"),
727 match long_name.len() {
731 row.push_str(&long_name[..]);
739 Yes => row.push_str(&hint[..]),
742 row.push_str(&hint[..]);
747 // FIXME: #5516 should be graphemes not codepoints
748 // here we just need to indent the start of the description
749 let rowlen = row.chars().count();
751 for _ in 0..24 - rowlen {
755 row.push_str(&desc_sep[..]);
758 // Normalize desc to contain words separated by one space character
759 let mut desc_normalized_whitespace = String::new();
760 for word in desc.split_whitespace() {
761 desc_normalized_whitespace.push_str(word);
762 desc_normalized_whitespace.push(' ');
765 // FIXME: #5516 should be graphemes not codepoints
766 let mut desc_rows = Vec::new();
767 each_split_within(&desc_normalized_whitespace[..], 54, |substr| {
768 desc_rows.push(substr.to_owned());
772 // FIXME: #5516 should be graphemes not codepoints
773 // wrapped description
774 row.push_str(&desc_rows.join(&desc_sep[..]));
779 format!("{}\n\nOptions:\n{}\n",
781 rows.collect::<Vec<String>>().join("\n"))
784 fn format_option(opt: &OptGroup) -> String {
785 let mut line = String::new();
787 if opt.occur != Req {
791 // Use short_name is possible, but fallback to long_name.
792 if !opt.short_name.is_empty() {
794 line.push_str(&opt.short_name[..]);
797 line.push_str(&opt.long_name[..]);
800 if opt.hasarg != No {
802 if opt.hasarg == Maybe {
805 line.push_str(&opt.hint[..]);
806 if opt.hasarg == Maybe {
811 if opt.occur != Req {
814 if opt.occur == Multi {
821 /// Derive a short one-line usage summary from a set of long options.
822 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
823 let mut line = format!("Usage: {} ", program_name);
824 line.push_str(&opts.iter()
826 .collect::<Vec<String>>()
831 #[derive(Copy, Clone)]
832 enum SplitWithinState {
833 A, // leading whitespace, initial state
835 C, // internal and trailing whitespace
837 #[derive(Copy, Clone)]
839 Ws, // current char is whitespace
840 Cr, // current char is not whitespace
842 #[derive(Copy, Clone)]
844 UnderLim, // current char makes current substring still fit in limit
845 OverLim, // current char makes current substring no longer fit in limit
849 /// Splits a string into substrings with possibly internal whitespace,
850 /// each of them at most `lim` bytes long. The substrings have leading and trailing
851 /// whitespace removed, and are only cut at whitespace boundaries.
853 /// Note: Function was moved here from `std::str` because this module is the only place that
854 /// uses it, and because it was too specific for a general string function.
858 /// Panics during iteration if the string contains a non-whitespace
859 /// sequence longer than the limit.
860 fn each_split_within<F>(ss: &str, lim: usize, mut it: F) -> bool
861 where F: FnMut(&str) -> bool
863 // Just for fun, let's write this as a state machine:
865 let mut slice_start = 0;
866 let mut last_start = 0;
867 let mut last_end = 0;
869 let mut fake_i = ss.len();
874 // if the limit is larger than the string, lower it to save cycles
879 let mut machine = |cont: &mut bool, (i, c): (usize, char)| -> bool {
880 let whitespace = if c.is_whitespace() {
885 let limit = if (i - slice_start + 1) <= lim {
891 state = match (state, whitespace, limit) {
899 (B, Cr, UnderLim) => B,
900 (B, Cr, OverLim) if (i - last_start + 1) > lim => {
901 panic!("word starting with {} longer than limit!",
902 &ss[last_start..i + 1])
904 (B, Cr, OverLim) => {
905 *cont = it(&ss[slice_start..last_end]);
906 slice_start = last_start;
909 (B, Ws, UnderLim) => {
913 (B, Ws, OverLim) => {
915 *cont = it(&ss[slice_start..last_end]);
919 (C, Cr, UnderLim) => {
923 (C, Cr, OverLim) => {
924 *cont = it(&ss[slice_start..last_end]);
930 (C, Ws, OverLim) => {
931 *cont = it(&ss[slice_start..last_end]);
934 (C, Ws, UnderLim) => C,
940 ss.char_indices().all(|x| machine(&mut cont, x));
942 // Let the automaton 'run out' by supplying trailing whitespace
948 machine(&mut cont, (fake_i, ' '));
955 fn test_split_within() {
956 fn t(s: &str, i: usize, u: &[String]) {
957 let mut v = Vec::new();
958 each_split_within(s, i, |s| {
959 v.push(s.to_string());
962 assert!(v.iter().zip(u).all(|(a, b)| a == b));
966 t("hello", 15, &["hello".to_string()]);
967 t("\nMary had a little lamb\nLittle lamb\n",
969 &["Mary had a".to_string(), "little lamb".to_string(), "Little lamb".to_string()]);
970 t("\nMary had a little lamb\nLittle lamb\n",
972 &["Mary had a little lamb\nLittle lamb".to_string()]);
980 use std::result::Result::{Err, Ok};
986 let long_args = vec!["--test=20".to_string()];
987 let opts = vec![reqopt("t", "test", "testing", "TEST")];
988 let rs = getopts(&long_args, &opts);
991 assert!(m.opt_present("test"));
992 assert_eq!(m.opt_str("test").unwrap(), "20");
993 assert!(m.opt_present("t"));
994 assert_eq!(m.opt_str("t").unwrap(), "20");
997 panic!("test_reqopt failed (long arg)");
1000 let short_args = vec!["-t".to_string(), "20".to_string()];
1001 match getopts(&short_args, &opts) {
1003 assert!((m.opt_present("test")));
1004 assert_eq!(m.opt_str("test").unwrap(), "20");
1005 assert!((m.opt_present("t")));
1006 assert_eq!(m.opt_str("t").unwrap(), "20");
1009 panic!("test_reqopt failed (short arg)");
1015 fn test_reqopt_missing() {
1016 let args = vec!["blah".to_string()];
1017 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1018 let rs = getopts(&args, &opts);
1020 Err(OptionMissing(_)) => {}
1026 fn test_reqopt_no_arg() {
1027 let long_args = vec!["--test".to_string()];
1028 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1029 let rs = getopts(&long_args, &opts);
1031 Err(ArgumentMissing(_)) => {}
1034 let short_args = vec!["-t".to_string()];
1035 match getopts(&short_args, &opts) {
1036 Err(ArgumentMissing(_)) => {}
1042 fn test_reqopt_multi() {
1043 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1044 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1045 let rs = getopts(&args, &opts);
1047 Err(OptionDuplicated(_)) => {}
1055 let long_args = vec!["--test=20".to_string()];
1056 let opts = vec![optopt("t", "test", "testing", "TEST")];
1057 let rs = getopts(&long_args, &opts);
1060 assert!(m.opt_present("test"));
1061 assert_eq!(m.opt_str("test").unwrap(), "20");
1062 assert!((m.opt_present("t")));
1063 assert_eq!(m.opt_str("t").unwrap(), "20");
1067 let short_args = vec!["-t".to_string(), "20".to_string()];
1068 match getopts(&short_args, &opts) {
1070 assert!((m.opt_present("test")));
1071 assert_eq!(m.opt_str("test").unwrap(), "20");
1072 assert!((m.opt_present("t")));
1073 assert_eq!(m.opt_str("t").unwrap(), "20");
1080 fn test_optopt_missing() {
1081 let args = vec!["blah".to_string()];
1082 let opts = vec![optopt("t", "test", "testing", "TEST")];
1083 let rs = getopts(&args, &opts);
1086 assert!(!m.opt_present("test"));
1087 assert!(!m.opt_present("t"));
1094 fn test_optopt_no_arg() {
1095 let long_args = vec!["--test".to_string()];
1096 let opts = vec![optopt("t", "test", "testing", "TEST")];
1097 let rs = getopts(&long_args, &opts);
1099 Err(ArgumentMissing(_)) => {}
1102 let short_args = vec!["-t".to_string()];
1103 match getopts(&short_args, &opts) {
1104 Err(ArgumentMissing(_)) => {}
1110 fn test_optopt_multi() {
1111 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1112 let opts = vec![optopt("t", "test", "testing", "TEST")];
1113 let rs = getopts(&args, &opts);
1115 Err(OptionDuplicated(_)) => {}
1120 // Tests for optflag
1123 let long_args = vec!["--test".to_string()];
1124 let opts = vec![optflag("t", "test", "testing")];
1125 let rs = getopts(&long_args, &opts);
1128 assert!(m.opt_present("test"));
1129 assert!(m.opt_present("t"));
1133 let short_args = vec!["-t".to_string()];
1134 match getopts(&short_args, &opts) {
1136 assert!(m.opt_present("test"));
1137 assert!(m.opt_present("t"));
1144 fn test_optflag_missing() {
1145 let args = vec!["blah".to_string()];
1146 let opts = vec![optflag("t", "test", "testing")];
1147 let rs = getopts(&args, &opts);
1150 assert!(!m.opt_present("test"));
1151 assert!(!m.opt_present("t"));
1158 fn test_optflag_long_arg() {
1159 let args = vec!["--test=20".to_string()];
1160 let opts = vec![optflag("t", "test", "testing")];
1161 let rs = getopts(&args, &opts);
1163 Err(UnexpectedArgument(_)) => {}
1169 fn test_optflag_multi() {
1170 let args = vec!["--test".to_string(), "-t".to_string()];
1171 let opts = vec![optflag("t", "test", "testing")];
1172 let rs = getopts(&args, &opts);
1174 Err(OptionDuplicated(_)) => {}
1180 fn test_optflag_short_arg() {
1181 let args = vec!["-t".to_string(), "20".to_string()];
1182 let opts = vec![optflag("t", "test", "testing")];
1183 let rs = getopts(&args, &opts);
1186 // The next variable after the flag is just a free argument
1188 assert!(m.free[0] == "20");
1194 // Tests for optflagmulti
1196 fn test_optflagmulti_short1() {
1197 let args = vec!["-v".to_string()];
1198 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1199 let rs = getopts(&args, &opts);
1202 assert_eq!(m.opt_count("v"), 1);
1209 fn test_optflagmulti_short2a() {
1210 let args = vec!["-v".to_string(), "-v".to_string()];
1211 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1212 let rs = getopts(&args, &opts);
1215 assert_eq!(m.opt_count("v"), 2);
1222 fn test_optflagmulti_short2b() {
1223 let args = vec!["-vv".to_string()];
1224 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1225 let rs = getopts(&args, &opts);
1228 assert_eq!(m.opt_count("v"), 2);
1235 fn test_optflagmulti_long1() {
1236 let args = vec!["--verbose".to_string()];
1237 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1238 let rs = getopts(&args, &opts);
1241 assert_eq!(m.opt_count("verbose"), 1);
1248 fn test_optflagmulti_long2() {
1249 let args = vec!["--verbose".to_string(), "--verbose".to_string()];
1250 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1251 let rs = getopts(&args, &opts);
1254 assert_eq!(m.opt_count("verbose"), 2);
1261 fn test_optflagmulti_mix() {
1262 let args = vec!["--verbose".to_string(),
1265 "verbose".to_string()];
1266 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1267 let rs = getopts(&args, &opts);
1270 assert_eq!(m.opt_count("verbose"), 4);
1271 assert_eq!(m.opt_count("v"), 4);
1277 // Tests for optmulti
1279 fn test_optmulti() {
1280 let long_args = vec!["--test=20".to_string()];
1281 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1282 let rs = getopts(&long_args, &opts);
1285 assert!((m.opt_present("test")));
1286 assert_eq!(m.opt_str("test").unwrap(), "20");
1287 assert!((m.opt_present("t")));
1288 assert_eq!(m.opt_str("t").unwrap(), "20");
1292 let short_args = vec!["-t".to_string(), "20".to_string()];
1293 match getopts(&short_args, &opts) {
1295 assert!((m.opt_present("test")));
1296 assert_eq!(m.opt_str("test").unwrap(), "20");
1297 assert!((m.opt_present("t")));
1298 assert_eq!(m.opt_str("t").unwrap(), "20");
1305 fn test_optmulti_missing() {
1306 let args = vec!["blah".to_string()];
1307 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1308 let rs = getopts(&args, &opts);
1311 assert!(!m.opt_present("test"));
1312 assert!(!m.opt_present("t"));
1319 fn test_optmulti_no_arg() {
1320 let long_args = vec!["--test".to_string()];
1321 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1322 let rs = getopts(&long_args, &opts);
1324 Err(ArgumentMissing(_)) => {}
1327 let short_args = vec!["-t".to_string()];
1328 match getopts(&short_args, &opts) {
1329 Err(ArgumentMissing(_)) => {}
1335 fn test_optmulti_multi() {
1336 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1337 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1338 let rs = getopts(&args, &opts);
1341 assert!(m.opt_present("test"));
1342 assert_eq!(m.opt_str("test").unwrap(), "20");
1343 assert!(m.opt_present("t"));
1344 assert_eq!(m.opt_str("t").unwrap(), "20");
1345 let pair = m.opt_strs("test");
1346 assert!(pair[0] == "20");
1347 assert!(pair[1] == "30");
1354 fn test_unrecognized_option() {
1355 let long_args = vec!["--untest".to_string()];
1356 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1357 let rs = getopts(&long_args, &opts);
1359 Err(UnrecognizedOption(_)) => {}
1362 let short_args = vec!["-u".to_string()];
1363 match getopts(&short_args, &opts) {
1364 Err(UnrecognizedOption(_)) => {}
1370 fn test_combined() {
1371 let args = vec!["prog".to_string(),
1372 "free1".to_string(),
1375 "free2".to_string(),
1376 "--flag".to_string(),
1377 "--long=30".to_string(),
1386 "-60 70".to_string()];
1387 let opts = vec![optopt("s", "something", "something", "SOMETHING"),
1388 optflag("", "flag", "a flag"),
1389 reqopt("", "long", "hi", "LONG"),
1390 optflag("f", "", "another flag"),
1391 optmulti("m", "", "mmmmmm", "YUM"),
1392 optmulti("n", "", "nothing", "NOTHING"),
1393 optopt("", "notpresent", "nothing to see here", "NOPE")];
1394 let rs = getopts(&args, &opts);
1397 assert!(m.free[0] == "prog");
1398 assert!(m.free[1] == "free1");
1399 assert_eq!(m.opt_str("s").unwrap(), "20");
1400 assert!(m.free[2] == "free2");
1401 assert!((m.opt_present("flag")));
1402 assert_eq!(m.opt_str("long").unwrap(), "30");
1403 assert!((m.opt_present("f")));
1404 let pair = m.opt_strs("m");
1405 assert!(pair[0] == "40");
1406 assert!(pair[1] == "50");
1407 let pair = m.opt_strs("n");
1408 assert!(pair[0] == "-A B");
1409 assert!(pair[1] == "-60 70");
1410 assert!((!m.opt_present("notpresent")));
1418 let opts = vec![optopt("e", "", "encrypt", "ENCRYPT"),
1419 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1420 optopt("f", "", "flag", "FLAG")];
1422 let args_single = vec!["-e".to_string(), "foo".to_string()];
1423 let matches_single = &match getopts(&args_single, &opts) {
1424 result::Result::Ok(m) => m,
1425 result::Result::Err(_) => panic!(),
1427 assert!(matches_single.opts_present(&["e".to_string()]));
1428 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1429 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1430 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1431 assert!(!matches_single.opts_present(&["thing".to_string()]));
1432 assert!(!matches_single.opts_present(&[]));
1434 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1435 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1437 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1440 let args_both = vec!["-e".to_string(),
1442 "--encrypt".to_string(),
1444 let matches_both = &match getopts(&args_both, &opts) {
1445 result::Result::Ok(m) => m,
1446 result::Result::Err(_) => panic!(),
1448 assert!(matches_both.opts_present(&["e".to_string()]));
1449 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1450 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1451 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1452 assert!(!matches_both.opts_present(&["f".to_string()]));
1453 assert!(!matches_both.opts_present(&["thing".to_string()]));
1454 assert!(!matches_both.opts_present(&[]));
1456 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1457 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(),
1459 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1461 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1467 let args = vec!["-Lfoo".to_string(), "-M.".to_string()];
1468 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1469 optmulti("M", "", "something", "MMMM")];
1470 let matches = &match getopts(&args, &opts) {
1471 result::Result::Ok(m) => m,
1472 result::Result::Err(_) => panic!(),
1474 assert!(matches.opts_present(&["L".to_string()]));
1475 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1476 assert!(matches.opts_present(&["M".to_string()]));
1477 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1482 fn test_nospace_conflict() {
1483 let args = vec!["-vvLverbose".to_string(), "-v".to_string()];
1484 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1485 optflagmulti("v", "verbose", "Verbose")];
1486 let matches = &match getopts(&args, &opts) {
1487 result::Result::Ok(m) => m,
1488 result::Result::Err(e) => panic!("{}", e),
1490 assert!(matches.opts_present(&["L".to_string()]));
1491 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1492 assert!(matches.opts_present(&["v".to_string()]));
1493 assert_eq!(3, matches.opt_count("v"));
1497 fn test_long_to_short() {
1498 let mut short = Opt {
1499 name: Name::Long("banana".to_string()),
1500 hasarg: HasArg::Yes,
1502 aliases: Vec::new(),
1504 short.aliases = vec![Opt {
1505 name: Name::Short('b'),
1506 hasarg: HasArg::Yes,
1508 aliases: Vec::new(),
1510 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1512 assert!(verbose.long_to_short() == short);
1516 fn test_aliases_long_and_short() {
1517 let opts = vec![optflagmulti("a", "apple", "Desc")];
1519 let args = vec!["-a".to_string(), "--apple".to_string(), "-a".to_string()];
1521 let matches = getopts(&args, &opts).unwrap();
1522 assert_eq!(3, matches.opt_count("a"));
1523 assert_eq!(3, matches.opt_count("apple"));
1528 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1529 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1530 optflag("k", "kiwi", "Desc"),
1531 optflagopt("p", "", "Desc", "VAL"),
1532 optmulti("l", "", "Desc", "VAL")];
1538 -b --banana VAL Desc
1539 -a --012345678901234567890123456789 VAL
1546 let generated_usage = usage("Usage: fruits", &optgroups);
1548 debug!("expected: <<{}>>", expected);
1549 debug!("generated: <<{}>>", generated_usage);
1550 assert_eq!(generated_usage, expected);
1554 fn test_usage_description_wrapping() {
1555 // indentation should be 24 spaces
1556 // lines wrap after 78: or rather descriptions wrap after 54
1558 let optgroups = vec![optflag("k",
1561 "This is a long description which won't be wrapped..+.."),
1564 "This is a long description which _will_ be wrapped..+..")];
1570 -k --kiwi This is a long description which won't be wrapped..+..
1571 -a --apple This is a long description which _will_ be
1575 let usage = usage("Usage: fruits", &optgroups);
1577 debug!("expected: <<{}>>", expected);
1578 debug!("generated: <<{}>>", usage);
1579 assert!(usage == expected)
1583 fn test_usage_description_multibyte_handling() {
1584 let optgroups = vec![optflag("k",
1585 "k\u{2013}w\u{2013}",
1586 "The word kiwi is normally spelled with two i's"),
1589 "This \u{201C}description\u{201D} has some characters that \
1590 could confuse the line wrapping; an apple costs 0.51€ in \
1591 some parts of Europe.")];
1597 -k --k–w– The word kiwi is normally spelled with two i's
1598 -a --apple This “description” has some characters that could
1599 confuse the line wrapping; an apple costs 0.51€ in
1600 some parts of Europe.
1603 let usage = usage("Usage: fruits", &optgroups);
1605 debug!("expected: <<{}>>", expected);
1606 debug!("generated: <<{}>>", usage);
1607 assert!(usage == expected)
1611 fn test_short_usage() {
1612 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1613 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1614 optflag("k", "kiwi", "Desc"),
1615 optflagopt("p", "", "Desc", "VAL"),
1616 optmulti("l", "", "Desc", "VAL")];
1618 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1619 let generated_usage = short_usage("fruits", &optgroups);
1621 debug!("expected: <<{}>>", expected);
1622 debug!("generated: <<{}>>", generated_usage);
1623 assert_eq!(generated_usage, expected);
1627 fn test_args_with_equals() {
1628 let args = vec!("--one".to_string(), "A=B".to_string(),
1629 "--two=C=D".to_string());
1630 let opts = vec![optopt("o", "one", "One", "INFO"),
1631 optopt("t", "two", "Two", "INFO")];
1632 let matches = &match getopts(&args, &opts) {
1633 result::Result::Ok(m) => m,
1634 result::Result::Err(e) => panic!("{}", e)
1636 assert_eq!(matches.opts_str(&["o".to_string()]).unwrap(), "A=B");
1637 assert_eq!(matches.opts_str(&["t".to_string()]).unwrap(), "C=D");