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);
81 // Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
82 #![cfg_attr(stage0, feature(custom_attribute))]
83 #![crate_name = "getopts"]
84 #![unstable(feature = "rustc_private",
85 reason = "use the crates.io `getopts` library instead",
88 #![crate_type = "rlib"]
89 #![crate_type = "dylib"]
90 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
91 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
92 html_root_url = "https://doc.rust-lang.org/nightly/",
93 html_playground_url = "https://play.rust-lang.org/",
94 test(attr(deny(warnings))))]
96 #![deny(missing_docs)]
97 #![feature(staged_api)]
99 #![cfg_attr(test, feature(rustc_private))]
101 #[cfg(test)] #[macro_use] extern crate log;
108 use self::SplitWithinState::*;
109 use self::Whitespace::*;
110 use self::LengthLimit::*;
113 use std::iter::repeat;
116 /// Name of an option. Either a string or a single char.
117 #[derive(Clone, PartialEq, Eq, Debug)]
119 /// A string representing the long name of an option.
120 /// For example: "help"
122 /// A char representing the short name of an option.
127 /// Describes whether an option has an argument.
128 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
130 /// The option requires an argument.
132 /// The option takes no argument.
134 /// The option argument is optional.
138 /// Describes how often an option may occur.
139 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
141 /// The option occurs once.
143 /// The option occurs at most once.
145 /// The option occurs zero or more times.
149 /// A description of a possible option.
150 #[derive(Clone, PartialEq, Eq, Debug)]
152 /// Name of the option
154 /// Whether it has an argument
156 /// How often it can occur
158 /// Which options it aliases
159 pub aliases: Vec<Opt>,
162 /// One group of options, e.g., both `-h` and `--help`, along with
163 /// their shared description and properties.
164 #[derive(Clone, PartialEq, Eq, Debug)]
165 pub struct OptGroup {
166 /// Short name of the option, e.g. `h` for a `-h` option
167 pub short_name: String,
168 /// Long name of the option, e.g. `help` for a `--help` option
169 pub long_name: String,
170 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
172 /// Description for usage help text
174 /// Whether option has an argument
176 /// How often it can occur
180 /// Describes whether an option is given at all or has a value.
181 #[derive(Clone, PartialEq, Eq, Debug)]
187 /// The result of checking command line arguments. Contains a vector
188 /// of matches and a vector of free strings.
189 #[derive(Clone, PartialEq, Eq, Debug)]
191 /// Options that matched
193 /// Values of the Options that matched
194 vals: Vec<Vec<Optval>>,
195 /// Free string fragments
196 pub free: Vec<String>,
199 /// The type returned when the command line does not conform to the
200 /// expected format. Use the `Debug` implementation to output detailed
202 #[derive(Clone, PartialEq, Eq, Debug)]
204 /// The option requires an argument but none was passed.
205 ArgumentMissing(String),
206 /// The passed option is not declared among the possible options.
207 UnrecognizedOption(String),
208 /// A required option is not present.
209 OptionMissing(String),
210 /// A single occurrence option is being used multiple times.
211 OptionDuplicated(String),
212 /// There's an argument being passed to a non-argument option.
213 UnexpectedArgument(String),
216 /// The type of failure that occurred.
217 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
218 #[allow(missing_docs)]
227 /// The result of parsing a command line with a set of options.
228 pub type Result = result::Result<Matches, Fail>;
231 fn from_str(nm: &str) -> Name {
239 fn to_string(&self) -> String {
241 Short(ch) => ch.to_string(),
242 Long(ref s) => s.to_owned()
248 /// Translate OptGroup into Opt.
249 /// (Both short and long names correspond to different Opts).
250 pub fn long_to_short(&self) -> Opt {
259 match (short_name.len(), long_name.len()) {
260 (0,0) => panic!("this long-format option was given no name"),
262 name: Long((long_name)),
268 name: Short(short_name.char_at(0)),
274 name: Long((long_name)),
279 name: Short(short_name.char_at(0)),
286 (_,_) => panic!("something is wrong with the long-form opt")
292 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
293 match find_opt(&self.opts[..], Name::from_str(nm)) {
294 Some(id) => self.vals[id].clone(),
295 None => panic!("No option '{}' defined", nm)
299 fn opt_val(&self, nm: &str) -> Option<Optval> {
300 let vals = self.opt_vals(nm);
304 Some(vals[0].clone())
308 /// Returns true if an option was matched.
309 pub fn opt_present(&self, nm: &str) -> bool {
310 !self.opt_vals(nm).is_empty()
313 /// Returns the number of times an option was matched.
314 pub fn opt_count(&self, nm: &str) -> usize {
315 self.opt_vals(nm).len()
318 /// Returns true if any of several options were matched.
319 pub fn opts_present(&self, names: &[String]) -> bool {
321 match find_opt(&self.opts, Name::from_str(&**nm)) {
322 Some(id) if !self.vals[id].is_empty() => return true,
329 /// Returns the string argument supplied to one of several matching options or `None`.
330 pub fn opts_str(&self, names: &[String]) -> Option<String> {
332 match self.opt_val(&nm[..]) {
333 Some(Val(ref s)) => return Some(s.clone()),
340 /// Returns a vector of the arguments provided to all matches of the given
343 /// Used when an option accepts multiple values.
344 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
345 let mut acc: Vec<String> = Vec::new();
346 let r = self.opt_vals(nm);
349 Val(ref s) => acc.push((*s).clone()),
356 /// Returns the string argument supplied to a matching option or `None`.
357 pub fn opt_str(&self, nm: &str) -> Option<String> {
358 let vals = self.opt_vals(nm);
360 return None::<String>;
363 Val(ref s) => Some((*s).clone()),
369 /// Returns the matching string, a default, or none.
371 /// Returns none if the option was not present, `def` if the option was
372 /// present but no argument was provided, and the argument if the option was
373 /// present and an argument was provided.
374 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
375 let vals = self.opt_vals(nm);
380 Val(ref s) => Some((*s).clone()),
381 _ => Some(def.to_owned())
388 fn is_arg(arg: &str) -> bool {
389 arg.len() > 1 && arg.as_bytes()[0] == b'-'
392 fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
393 // Search main options.
394 let pos = opts.iter().position(|opt| opt.name == nm);
399 // Search in aliases.
400 for candidate in opts {
401 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
402 return opts.iter().position(|opt| opt.name == candidate.name);
409 /// Create a long option that is required and takes an argument.
411 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
412 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
413 /// * `desc` - Description for usage help
414 /// * `hint` - Hint that is used in place of the argument in the usage help,
415 /// e.g. `"FILE"` for a `-o FILE` option
416 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
417 let len = short_name.len();
418 assert!(len == 1 || len == 0);
420 short_name: short_name.to_owned(),
421 long_name: long_name.to_owned(),
422 hint: hint.to_owned(),
423 desc: desc.to_owned(),
429 /// Create a long option that is optional and takes an argument.
431 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
432 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
433 /// * `desc` - Description for usage help
434 /// * `hint` - Hint that is used in place of the argument in the usage help,
435 /// e.g. `"FILE"` for a `-o FILE` option
436 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
437 let len = short_name.len();
438 assert!(len == 1 || len == 0);
440 short_name: short_name.to_owned(),
441 long_name: long_name.to_owned(),
442 hint: hint.to_owned(),
443 desc: desc.to_owned(),
449 /// Create a long option that is optional and does not take an argument.
451 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
452 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
453 /// * `desc` - Description for usage help
454 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
455 let len = short_name.len();
456 assert!(len == 1 || len == 0);
458 short_name: short_name.to_owned(),
459 long_name: long_name.to_owned(),
461 desc: desc.to_owned(),
467 /// Create a long option that can occur more than once and does not
468 /// take an argument.
470 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
471 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
472 /// * `desc` - Description for usage help
473 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
474 let len = short_name.len();
475 assert!(len == 1 || len == 0);
477 short_name: short_name.to_owned(),
478 long_name: long_name.to_owned(),
480 desc: desc.to_owned(),
486 /// Create a long option that is optional and takes an optional argument.
488 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
489 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
490 /// * `desc` - Description for usage help
491 /// * `hint` - Hint that is used in place of the argument in the usage help,
492 /// e.g. `"FILE"` for a `-o FILE` option
493 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
494 let len = short_name.len();
495 assert!(len == 1 || len == 0);
497 short_name: short_name.to_owned(),
498 long_name: long_name.to_owned(),
499 hint: hint.to_owned(),
500 desc: desc.to_owned(),
506 /// Create a long option that is optional, takes an argument, and may occur
509 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
510 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
511 /// * `desc` - Description for usage help
512 /// * `hint` - Hint that is used in place of the argument in the usage help,
513 /// e.g. `"FILE"` for a `-o FILE` option
514 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
515 let len = short_name.len();
516 assert!(len == 1 || len == 0);
518 short_name: short_name.to_owned(),
519 long_name: long_name.to_owned(),
520 hint: hint.to_owned(),
521 desc: desc.to_owned(),
527 /// Create a generic option group, stating all parameters explicitly
528 pub fn opt(short_name: &str,
533 occur: Occur) -> OptGroup {
534 let len = short_name.len();
535 assert!(len == 1 || len == 0);
537 short_name: short_name.to_owned(),
538 long_name: long_name.to_owned(),
539 hint: hint.to_owned(),
540 desc: desc.to_owned(),
546 impl fmt::Display for Fail {
547 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
549 ArgumentMissing(ref nm) => {
550 write!(f, "Argument to option '{}' missing.", *nm)
552 UnrecognizedOption(ref nm) => {
553 write!(f, "Unrecognized option: '{}'.", *nm)
555 OptionMissing(ref nm) => {
556 write!(f, "Required option '{}' missing.", *nm)
558 OptionDuplicated(ref nm) => {
559 write!(f, "Option '{}' given more than once.", *nm)
561 UnexpectedArgument(ref nm) => {
562 write!(f, "Option '{}' does not take an argument.", *nm)
568 /// Parse command line arguments according to the provided options.
570 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
571 /// `opt_str`, etc. to interrogate results.
574 /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` to display
575 /// information about it.
576 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
577 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
578 let n_opts = opts.len();
580 fn f(_x: usize) -> Vec<Optval> { Vec::new() }
582 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
583 let mut free: Vec<String> = Vec::new();
587 let cur = args[i].clone();
588 let curlen = cur.len();
589 if !is_arg(&cur[..]) {
591 } else if cur == "--" {
593 while j < l { free.push(args[j].clone()); j += 1; }
597 let mut i_arg = None;
598 if cur.as_bytes()[1] == b'-' {
599 let tail = &cur[2..curlen];
600 let tail_eq: Vec<&str> = tail.split('=').collect();
601 if tail_eq.len() <= 1 {
602 names = vec!(Long(tail.to_owned()));
605 vec!(Long(tail_eq[0].to_owned()));
606 i_arg = Some(tail_eq[1].to_owned());
612 let ch = cur.char_at(j);
615 /* In a series of potential options (eg. -aheJ), if we
616 see one which takes an argument, we assume all
617 subsequent characters make up the argument. This
618 allows options such as -L/usr/local/lib/foo to be
619 interpreted correctly
622 let opt_id = match find_opt(&opts, opt.clone()) {
624 None => return Err(UnrecognizedOption(opt.to_string()))
629 let arg_follows = match opts[opt_id].hasarg {
634 let next = j + ch.len_utf8();
635 if arg_follows && next < curlen {
636 i_arg = Some((&cur[next..curlen]).to_owned());
643 let mut name_pos = 0;
646 let optid = match find_opt(&opts, (*nm).clone()) {
648 None => return Err(UnrecognizedOption(nm.to_string()))
650 match opts[optid].hasarg {
652 if name_pos == names.len() && !i_arg.is_none() {
653 return Err(UnexpectedArgument(nm.to_string()));
655 let v = &mut vals[optid];
659 if !i_arg.is_none() {
660 let v = &mut vals[optid];
661 v.push(Val((i_arg.clone())
663 } else if name_pos < names.len() || i + 1 == l ||
664 is_arg(&args[i + 1][..]) {
665 let v = &mut vals[optid];
669 let v = &mut vals[optid];
670 v.push(Val(args[i].clone()));
674 if !i_arg.is_none() {
675 let v = &mut vals[optid];
676 v.push(Val(i_arg.clone().unwrap()));
677 } else if i + 1 == l {
678 return Err(ArgumentMissing(nm.to_string()));
681 let v = &mut vals[optid];
682 v.push(Val(args[i].clone()));
691 let n = vals[i].len();
692 let occ = opts[i].occur;
693 if occ == Req && n == 0 {
694 return Err(OptionMissing(opts[i].name.to_string()));
696 if occ != Multi && n > 1 {
697 return Err(OptionDuplicated(opts[i].name.to_string()));
707 /// Derive a usage message from a set of long options.
708 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
710 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
712 let rows = opts.iter().map(|optref| {
713 let OptGroup{short_name,
718 ..} = (*optref).clone();
720 let mut row = repeat(" ").take(4).collect::<String>();
723 match short_name.len() {
727 row.push_str(&short_name[..]);
730 _ => panic!("the short name should only be 1 ascii char long"),
734 match long_name.len() {
738 row.push_str(&long_name[..]);
746 Yes => row.push_str(&hint[..]),
749 row.push_str(&hint[..]);
754 // FIXME: #5516 should be graphemes not codepoints
755 // here we just need to indent the start of the description
756 let rowlen = row.chars().count();
758 for _ in 0..24 - rowlen {
762 row.push_str(&desc_sep[..]);
765 // Normalize desc to contain words separated by one space character
766 let mut desc_normalized_whitespace = String::new();
767 for word in desc.split_whitespace() {
768 desc_normalized_whitespace.push_str(word);
769 desc_normalized_whitespace.push(' ');
772 // FIXME: #5516 should be graphemes not codepoints
773 let mut desc_rows = Vec::new();
774 each_split_within(&desc_normalized_whitespace[..], 54, |substr| {
775 desc_rows.push(substr.to_owned());
779 // FIXME: #5516 should be graphemes not codepoints
780 // wrapped description
781 row.push_str(&desc_rows.join(&desc_sep[..]));
786 format!("{}\n\nOptions:\n{}\n", brief,
787 rows.collect::<Vec<String>>().join("\n"))
790 fn format_option(opt: &OptGroup) -> String {
791 let mut line = String::new();
793 if opt.occur != Req {
797 // Use short_name is possible, but fallback to long_name.
798 if !opt.short_name.is_empty() {
800 line.push_str(&opt.short_name[..]);
803 line.push_str(&opt.long_name[..]);
806 if opt.hasarg != No {
808 if opt.hasarg == Maybe {
811 line.push_str(&opt.hint[..]);
812 if opt.hasarg == Maybe {
817 if opt.occur != Req {
820 if opt.occur == Multi {
827 /// Derive a short one-line usage summary from a set of long options.
828 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
829 let mut line = format!("Usage: {} ", program_name);
830 line.push_str(&opts.iter()
832 .collect::<Vec<String>>()
837 #[derive(Copy, Clone)]
838 enum SplitWithinState {
839 A, // leading whitespace, initial state
841 C, // internal and trailing whitespace
843 #[derive(Copy, Clone)]
845 Ws, // current char is whitespace
846 Cr // current char is not whitespace
848 #[derive(Copy, Clone)]
850 UnderLim, // current char makes current substring still fit in limit
851 OverLim // current char makes current substring no longer fit in limit
855 /// Splits a string into substrings with possibly internal whitespace,
856 /// each of them at most `lim` bytes long. The substrings have leading and trailing
857 /// whitespace removed, and are only cut at whitespace boundaries.
859 /// Note: Function was moved here from `std::str` because this module is the only place that
860 /// uses it, and because it was too specific for a general string function.
864 /// Panics during iteration if the string contains a non-whitespace
865 /// sequence longer than the limit.
866 fn each_split_within<F>(ss: &str, lim: usize, mut it: F) -> bool where
867 F: FnMut(&str) -> bool
869 // Just for fun, let's write this as a state machine:
871 let mut slice_start = 0;
872 let mut last_start = 0;
873 let mut last_end = 0;
875 let mut fake_i = ss.len();
880 // if the limit is larger than the string, lower it to save cycles
885 let mut machine = |cont: &mut bool, (i, c): (usize, char)| -> bool {
886 let whitespace = if c.is_whitespace() { Ws } else { Cr };
887 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
889 state = match (state, whitespace, limit) {
891 (A, Cr, _) => { slice_start = i; last_start = i; B }
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]),
897 (B, Cr, OverLim) => {
898 *cont = it(&ss[slice_start..last_end]);
899 slice_start = last_start;
902 (B, Ws, UnderLim) => {
906 (B, Ws, OverLim) => {
908 *cont = it(&ss[slice_start..last_end]);
912 (C, Cr, UnderLim) => {
916 (C, Cr, OverLim) => {
917 *cont = it(&ss[slice_start..last_end]);
923 (C, Ws, OverLim) => {
924 *cont = it(&ss[slice_start..last_end]);
927 (C, Ws, UnderLim) => {
935 ss.char_indices().all(|x| machine(&mut cont, x));
937 // Let the automaton 'run out' by supplying trailing whitespace
938 while cont && match state { B | C => true, A => false } {
939 machine(&mut cont, (fake_i, ' '));
946 fn test_split_within() {
947 fn t(s: &str, i: usize, u: &[String]) {
948 let mut v = Vec::new();
949 each_split_within(s, i, |s| { v.push(s.to_string()); true });
950 assert!(v.iter().zip(u).all(|(a,b)| a == b));
954 t("hello", 15, &["hello".to_string()]);
955 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
956 "Mary had a".to_string(),
957 "little lamb".to_string(),
958 "Little lamb".to_string()
960 t("\nMary had a little lamb\nLittle lamb\n", ::std::usize::MAX,
961 &["Mary had a little lamb\nLittle lamb".to_string()]);
969 use std::result::Result::{Err, Ok};
975 let long_args = vec!("--test=20".to_string());
976 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
977 let rs = getopts(&long_args, &opts);
980 assert!(m.opt_present("test"));
981 assert_eq!(m.opt_str("test").unwrap(), "20");
982 assert!(m.opt_present("t"));
983 assert_eq!(m.opt_str("t").unwrap(), "20");
985 _ => { panic!("test_reqopt failed (long arg)"); }
987 let short_args = vec!("-t".to_string(), "20".to_string());
988 match getopts(&short_args, &opts) {
990 assert!((m.opt_present("test")));
991 assert_eq!(m.opt_str("test").unwrap(), "20");
992 assert!((m.opt_present("t")));
993 assert_eq!(m.opt_str("t").unwrap(), "20");
995 _ => { panic!("test_reqopt failed (short arg)"); }
1000 fn test_reqopt_missing() {
1001 let args = vec!("blah".to_string());
1002 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1003 let rs = getopts(&args, &opts);
1005 Err(OptionMissing(_)) => {},
1011 fn test_reqopt_no_arg() {
1012 let long_args = vec!("--test".to_string());
1013 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1014 let rs = getopts(&long_args, &opts);
1016 Err(ArgumentMissing(_)) => {},
1019 let short_args = vec!("-t".to_string());
1020 match getopts(&short_args, &opts) {
1021 Err(ArgumentMissing(_)) => {},
1027 fn test_reqopt_multi() {
1028 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1029 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1030 let rs = getopts(&args, &opts);
1032 Err(OptionDuplicated(_)) => {},
1040 let long_args = vec!("--test=20".to_string());
1041 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1042 let rs = getopts(&long_args, &opts);
1045 assert!(m.opt_present("test"));
1046 assert_eq!(m.opt_str("test").unwrap(), "20");
1047 assert!((m.opt_present("t")));
1048 assert_eq!(m.opt_str("t").unwrap(), "20");
1052 let short_args = vec!("-t".to_string(), "20".to_string());
1053 match getopts(&short_args, &opts) {
1055 assert!((m.opt_present("test")));
1056 assert_eq!(m.opt_str("test").unwrap(), "20");
1057 assert!((m.opt_present("t")));
1058 assert_eq!(m.opt_str("t").unwrap(), "20");
1065 fn test_optopt_missing() {
1066 let args = vec!("blah".to_string());
1067 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1068 let rs = getopts(&args, &opts);
1071 assert!(!m.opt_present("test"));
1072 assert!(!m.opt_present("t"));
1079 fn test_optopt_no_arg() {
1080 let long_args = vec!("--test".to_string());
1081 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1082 let rs = getopts(&long_args, &opts);
1084 Err(ArgumentMissing(_)) => {},
1087 let short_args = vec!("-t".to_string());
1088 match getopts(&short_args, &opts) {
1089 Err(ArgumentMissing(_)) => {},
1095 fn test_optopt_multi() {
1096 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1097 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1098 let rs = getopts(&args, &opts);
1100 Err(OptionDuplicated(_)) => {},
1105 // Tests for optflag
1108 let long_args = vec!("--test".to_string());
1109 let opts = vec!(optflag("t", "test", "testing"));
1110 let rs = getopts(&long_args, &opts);
1113 assert!(m.opt_present("test"));
1114 assert!(m.opt_present("t"));
1118 let short_args = vec!("-t".to_string());
1119 match getopts(&short_args, &opts) {
1121 assert!(m.opt_present("test"));
1122 assert!(m.opt_present("t"));
1129 fn test_optflag_missing() {
1130 let args = vec!("blah".to_string());
1131 let opts = vec!(optflag("t", "test", "testing"));
1132 let rs = getopts(&args, &opts);
1135 assert!(!m.opt_present("test"));
1136 assert!(!m.opt_present("t"));
1143 fn test_optflag_long_arg() {
1144 let args = vec!("--test=20".to_string());
1145 let opts = vec!(optflag("t", "test", "testing"));
1146 let rs = getopts(&args, &opts);
1148 Err(UnexpectedArgument(_)) => {},
1154 fn test_optflag_multi() {
1155 let args = vec!("--test".to_string(), "-t".to_string());
1156 let opts = vec!(optflag("t", "test", "testing"));
1157 let rs = getopts(&args, &opts);
1159 Err(OptionDuplicated(_)) => {},
1165 fn test_optflag_short_arg() {
1166 let args = vec!("-t".to_string(), "20".to_string());
1167 let opts = vec!(optflag("t", "test", "testing"));
1168 let rs = getopts(&args, &opts);
1171 // The next variable after the flag is just a free argument
1173 assert!(m.free[0] == "20");
1179 // Tests for optflagmulti
1181 fn test_optflagmulti_short1() {
1182 let args = vec!("-v".to_string());
1183 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1184 let rs = getopts(&args, &opts);
1187 assert_eq!(m.opt_count("v"), 1);
1194 fn test_optflagmulti_short2a() {
1195 let args = vec!("-v".to_string(), "-v".to_string());
1196 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1197 let rs = getopts(&args, &opts);
1200 assert_eq!(m.opt_count("v"), 2);
1207 fn test_optflagmulti_short2b() {
1208 let args = vec!("-vv".to_string());
1209 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1210 let rs = getopts(&args, &opts);
1213 assert_eq!(m.opt_count("v"), 2);
1220 fn test_optflagmulti_long1() {
1221 let args = vec!("--verbose".to_string());
1222 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1223 let rs = getopts(&args, &opts);
1226 assert_eq!(m.opt_count("verbose"), 1);
1233 fn test_optflagmulti_long2() {
1234 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1235 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1236 let rs = getopts(&args, &opts);
1239 assert_eq!(m.opt_count("verbose"), 2);
1246 fn test_optflagmulti_mix() {
1247 let args = vec!("--verbose".to_string(), "-v".to_string(),
1248 "-vv".to_string(), "verbose".to_string());
1249 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1250 let rs = getopts(&args, &opts);
1253 assert_eq!(m.opt_count("verbose"), 4);
1254 assert_eq!(m.opt_count("v"), 4);
1260 // Tests for optmulti
1262 fn test_optmulti() {
1263 let long_args = vec!("--test=20".to_string());
1264 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1265 let rs = getopts(&long_args, &opts);
1268 assert!((m.opt_present("test")));
1269 assert_eq!(m.opt_str("test").unwrap(), "20");
1270 assert!((m.opt_present("t")));
1271 assert_eq!(m.opt_str("t").unwrap(), "20");
1275 let short_args = vec!("-t".to_string(), "20".to_string());
1276 match getopts(&short_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");
1288 fn test_optmulti_missing() {
1289 let args = vec!("blah".to_string());
1290 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1291 let rs = getopts(&args, &opts);
1294 assert!(!m.opt_present("test"));
1295 assert!(!m.opt_present("t"));
1302 fn test_optmulti_no_arg() {
1303 let long_args = vec!("--test".to_string());
1304 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1305 let rs = getopts(&long_args, &opts);
1307 Err(ArgumentMissing(_)) => {},
1310 let short_args = vec!("-t".to_string());
1311 match getopts(&short_args, &opts) {
1312 Err(ArgumentMissing(_)) => {},
1318 fn test_optmulti_multi() {
1319 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1320 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1321 let rs = getopts(&args, &opts);
1324 assert!(m.opt_present("test"));
1325 assert_eq!(m.opt_str("test").unwrap(), "20");
1326 assert!(m.opt_present("t"));
1327 assert_eq!(m.opt_str("t").unwrap(), "20");
1328 let pair = m.opt_strs("test");
1329 assert!(pair[0] == "20");
1330 assert!(pair[1] == "30");
1337 fn test_unrecognized_option() {
1338 let long_args = vec!("--untest".to_string());
1339 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1340 let rs = getopts(&long_args, &opts);
1342 Err(UnrecognizedOption(_)) => {},
1345 let short_args = vec!("-u".to_string());
1346 match getopts(&short_args, &opts) {
1347 Err(UnrecognizedOption(_)) => {},
1353 fn test_combined() {
1355 vec!("prog".to_string(),
1356 "free1".to_string(),
1359 "free2".to_string(),
1360 "--flag".to_string(),
1361 "--long=30".to_string(),
1370 "-60 70".to_string());
1372 vec!(optopt("s", "something", "something", "SOMETHING"),
1373 optflag("", "flag", "a flag"),
1374 reqopt("", "long", "hi", "LONG"),
1375 optflag("f", "", "another flag"),
1376 optmulti("m", "", "mmmmmm", "YUM"),
1377 optmulti("n", "", "nothing", "NOTHING"),
1378 optopt("", "notpresent", "nothing to see here", "NOPE"));
1379 let rs = getopts(&args, &opts);
1382 assert!(m.free[0] == "prog");
1383 assert!(m.free[1] == "free1");
1384 assert_eq!(m.opt_str("s").unwrap(), "20");
1385 assert!(m.free[2] == "free2");
1386 assert!((m.opt_present("flag")));
1387 assert_eq!(m.opt_str("long").unwrap(), "30");
1388 assert!((m.opt_present("f")));
1389 let pair = m.opt_strs("m");
1390 assert!(pair[0] == "40");
1391 assert!(pair[1] == "50");
1392 let pair = m.opt_strs("n");
1393 assert!(pair[0] == "-A B");
1394 assert!(pair[1] == "-60 70");
1395 assert!((!m.opt_present("notpresent")));
1403 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1404 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1405 optopt("f", "", "flag", "FLAG"));
1407 let args_single = vec!("-e".to_string(), "foo".to_string());
1408 let matches_single = &match getopts(&args_single, &opts) {
1409 result::Result::Ok(m) => m,
1410 result::Result::Err(_) => panic!()
1412 assert!(matches_single.opts_present(&["e".to_string()]));
1413 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1414 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1415 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1416 assert!(!matches_single.opts_present(&["thing".to_string()]));
1417 assert!(!matches_single.opts_present(&[]));
1419 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1420 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1422 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1425 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1427 let matches_both = &match getopts(&args_both, &opts) {
1428 result::Result::Ok(m) => m,
1429 result::Result::Err(_) => panic!()
1431 assert!(matches_both.opts_present(&["e".to_string()]));
1432 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1433 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1434 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1435 assert!(!matches_both.opts_present(&["f".to_string()]));
1436 assert!(!matches_both.opts_present(&["thing".to_string()]));
1437 assert!(!matches_both.opts_present(&[]));
1439 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1440 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1441 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1443 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1449 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1450 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1451 optmulti("M", "", "something", "MMMM"));
1452 let matches = &match getopts(&args, &opts) {
1453 result::Result::Ok(m) => m,
1454 result::Result::Err(_) => panic!()
1456 assert!(matches.opts_present(&["L".to_string()]));
1457 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1458 assert!(matches.opts_present(&["M".to_string()]));
1459 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1464 fn test_nospace_conflict() {
1465 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1466 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1467 optflagmulti("v", "verbose", "Verbose"));
1468 let matches = &match getopts(&args, &opts) {
1469 result::Result::Ok(m) => m,
1470 result::Result::Err(e) => panic!( "{}", e )
1472 assert!(matches.opts_present(&["L".to_string()]));
1473 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1474 assert!(matches.opts_present(&["v".to_string()]));
1475 assert_eq!(3, matches.opt_count("v"));
1479 fn test_long_to_short() {
1480 let mut short = Opt {
1481 name: Name::Long("banana".to_string()),
1482 hasarg: HasArg::Yes,
1484 aliases: Vec::new(),
1486 short.aliases = vec!(Opt { name: Name::Short('b'),
1487 hasarg: HasArg::Yes,
1489 aliases: Vec::new() });
1490 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1492 assert!(verbose.long_to_short() == short);
1496 fn test_aliases_long_and_short() {
1498 optflagmulti("a", "apple", "Desc"));
1500 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1502 let matches = getopts(&args, &opts).unwrap();
1503 assert_eq!(3, matches.opt_count("a"));
1504 assert_eq!(3, matches.opt_count("apple"));
1509 let optgroups = vec!(
1510 reqopt("b", "banana", "Desc", "VAL"),
1511 optopt("a", "012345678901234567890123456789",
1513 optflag("k", "kiwi", "Desc"),
1514 optflagopt("p", "", "Desc", "VAL"),
1515 optmulti("l", "", "Desc", "VAL"));
1521 -b --banana VAL Desc
1522 -a --012345678901234567890123456789 VAL
1529 let generated_usage = usage("Usage: fruits", &optgroups);
1531 debug!("expected: <<{}>>", expected);
1532 debug!("generated: <<{}>>", generated_usage);
1533 assert_eq!(generated_usage, expected);
1537 fn test_usage_description_wrapping() {
1538 // indentation should be 24 spaces
1539 // lines wrap after 78: or rather descriptions wrap after 54
1541 let optgroups = vec!(
1542 optflag("k", "kiwi",
1543 "This is a long description which won't be wrapped..+.."), // 54
1544 optflag("a", "apple",
1545 "This is a long description which _will_ be wrapped..+.."));
1551 -k --kiwi This is a long description which won't be wrapped..+..
1552 -a --apple This is a long description which _will_ be
1556 let usage = usage("Usage: fruits", &optgroups);
1558 debug!("expected: <<{}>>", expected);
1559 debug!("generated: <<{}>>", usage);
1560 assert!(usage == expected)
1564 fn test_usage_description_multibyte_handling() {
1565 let optgroups = vec!(
1566 optflag("k", "k\u{2013}w\u{2013}",
1567 "The word kiwi is normally spelled with two i's"),
1568 optflag("a", "apple",
1569 "This \u{201C}description\u{201D} has some characters that could \
1570 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1576 -k --k–w– The word kiwi is normally spelled with two i's
1577 -a --apple This “description” has some characters that could
1578 confuse the line wrapping; an apple costs 0.51€ in
1579 some parts of Europe.
1582 let usage = usage("Usage: fruits", &optgroups);
1584 debug!("expected: <<{}>>", expected);
1585 debug!("generated: <<{}>>", usage);
1586 assert!(usage == expected)
1590 fn test_short_usage() {
1591 let optgroups = vec!(
1592 reqopt("b", "banana", "Desc", "VAL"),
1593 optopt("a", "012345678901234567890123456789",
1595 optflag("k", "kiwi", "Desc"),
1596 optflagopt("p", "", "Desc", "VAL"),
1597 optmulti("l", "", "Desc", "VAL"));
1599 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1600 let generated_usage = short_usage("fruits", &optgroups);
1602 debug!("expected: <<{}>>", expected);
1603 debug!("generated: <<{}>>", generated_usage);
1604 assert_eq!(generated_usage, expected);