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 // ignore-lexer-test FIXME #15677
13 //! Simple getopt alternative.
15 //! Construct a vector of options, either by using `reqopt`, `optopt`, and `optflag`
16 //! or by building them from components yourself, and pass them to `getopts`,
17 //! along with a vector of actual arguments (not including `argv[0]`). You'll
18 //! either get a failure code back, or a match. You'll have to verify whether
19 //! the amount of 'free' arguments in the match is what you expect. Use `opt_*`
20 //! accessors to get argument values out of the matches object.
22 //! Single-character options are expected to appear on the command line with a
23 //! single preceding dash; multiple-character options are expected to be
24 //! proceeded by two dashes. Options that expect an argument accept their
25 //! argument following either a space or an equals sign. Single-character
26 //! options don't require the space.
30 //! The following example shows simple command line parsing for an application
31 //! that requires an input file to be specified, accepts an optional output
32 //! file name following `-o`, and accepts both `-h` and `--help` as optional flags.
35 //! extern crate getopts;
36 //! use getopts::{optopt,optflag,getopts,OptGroup};
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 //! println!("Usage: {} [options]", program);
49 //! println!("-o\t\tOutput");
50 //! println!("-h --help\tUsage");
54 //! let args: Vec<String> = os::args();
56 //! let program = args[0].clone();
59 //! optopt("o", "", "set output file name", "NAME"),
60 //! optflag("h", "help", "print this help menu")
62 //! let matches = match getopts(args.tail(), opts) {
64 //! Err(f) => { panic!(f.to_string()) }
66 //! if matches.opt_present("h") {
67 //! print_usage(program.as_slice(), opts);
70 //! let output = matches.opt_str("o");
71 //! let input = if !matches.free.is_empty() {
72 //! matches.free[0].clone()
74 //! print_usage(program.as_slice(), opts);
77 //! do_work(input.as_slice(), output);
81 #![crate_name = "getopts"]
83 #![crate_type = "rlib"]
84 #![crate_type = "dylib"]
85 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
86 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
87 html_root_url = "http://doc.rust-lang.org/nightly/",
88 html_playground_url = "http://play.rust-lang.org/")]
89 #![feature(globs, phase)]
90 #![feature(import_shadowing)]
91 #![deny(missing_docs)]
93 #[cfg(test)] #[phase(plugin, link)] extern crate log;
102 use std::result::Result::{Err, Ok};
104 use std::string::String;
106 /// Name of an option. Either a string or a single char.
107 #[deriving(Clone, PartialEq, Eq)]
109 /// A string representing the long name of an option.
110 /// For example: "help"
112 /// A char representing the short name of an option.
117 /// Describes whether an option has an argument.
118 #[deriving(Clone, PartialEq, Eq)]
120 /// The option requires an argument.
122 /// The option takes no argument.
124 /// The option argument is optional.
128 /// Describes how often an option may occur.
129 #[deriving(Clone, PartialEq, Eq)]
131 /// The option occurs once.
133 /// The option occurs at most once.
135 /// The option occurs zero or more times.
139 /// A description of a possible option.
140 #[deriving(Clone, PartialEq, Eq)]
142 /// Name of the option
144 /// Whether it has an argument
146 /// How often it can occur
148 /// Which options it aliases
149 pub aliases: Vec<Opt>,
152 /// One group of options, e.g., both `-h` and `--help`, along with
153 /// their shared description and properties.
154 #[deriving(Clone, PartialEq, Eq)]
155 pub struct OptGroup {
156 /// Short name of the option, e.g. `h` for a `-h` option
157 pub short_name: String,
158 /// Long name of the option, e.g. `help` for a `--help` option
159 pub long_name: String,
160 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
162 /// Description for usage help text
164 /// Whether option has an argument
166 /// How often it can occur
170 /// Describes whether an option is given at all or has a value.
171 #[deriving(Clone, PartialEq, Eq)]
177 /// The result of checking command line arguments. Contains a vector
178 /// of matches and a vector of free strings.
179 #[deriving(Clone, PartialEq, Eq)]
181 /// Options that matched
183 /// Values of the Options that matched
184 vals: Vec<Vec<Optval>>,
185 /// Free string fragments
186 pub free: Vec<String>,
189 /// The type returned when the command line does not conform to the
190 /// expected format. Use the `Show` implementation to output detailed
192 #[deriving(Clone, PartialEq, Eq)]
194 /// The option requires an argument but none was passed.
195 ArgumentMissing(String),
196 /// The passed option is not declared among the possible options.
197 UnrecognizedOption(String),
198 /// A required option is not present.
199 OptionMissing(String),
200 /// A single occurrence option is being used multiple times.
201 OptionDuplicated(String),
202 /// There's an argument being passed to a non-argument option.
203 UnexpectedArgument(String),
206 /// The result of parsing a command line with a set of options.
207 pub type Result = result::Result<Matches, Fail>;
210 fn from_str(nm: &str) -> Name {
212 Short(nm.char_at(0u))
218 fn to_string(&self) -> String {
220 Short(ch) => ch.to_string(),
221 Long(ref s) => s.to_string()
227 /// Translate OptGroup into Opt.
228 /// (Both short and long names correspond to different Opts).
229 pub fn long_to_short(&self) -> Opt {
238 match (short_name.len(), long_name.len()) {
239 (0,0) => panic!("this long-format option was given no name"),
241 name: Long((long_name)),
247 name: Short(short_name.char_at(0)),
253 name: Long((long_name)),
258 name: Short(short_name.char_at(0)),
265 (_,_) => panic!("something is wrong with the long-form opt")
271 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
272 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
273 Some(id) => self.vals[id].clone(),
274 None => panic!("No option '{}' defined", nm)
278 fn opt_val(&self, nm: &str) -> Option<Optval> {
279 let vals = self.opt_vals(nm);
283 Some(vals[0].clone())
287 /// Returns true if an option was matched.
288 pub fn opt_present(&self, nm: &str) -> bool {
289 !self.opt_vals(nm).is_empty()
292 /// Returns the number of times an option was matched.
293 pub fn opt_count(&self, nm: &str) -> uint {
294 self.opt_vals(nm).len()
297 /// Returns true if any of several options were matched.
298 pub fn opts_present(&self, names: &[String]) -> bool {
299 for nm in names.iter() {
300 match find_opt(self.opts.as_slice(),
301 Name::from_str(nm.as_slice())) {
302 Some(id) if !self.vals[id].is_empty() => return true,
309 /// Returns the string argument supplied to one of several matching options or `None`.
310 pub fn opts_str(&self, names: &[String]) -> Option<String> {
311 for nm in names.iter() {
312 match self.opt_val(nm.as_slice()) {
313 Some(Val(ref s)) => return Some(s.clone()),
320 /// Returns a vector of the arguments provided to all matches of the given
323 /// Used when an option accepts multiple values.
324 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
325 let mut acc: Vec<String> = Vec::new();
326 let r = self.opt_vals(nm);
329 Val(ref s) => acc.push((*s).clone()),
336 /// Returns the string argument supplied to a matching option or `None`.
337 pub fn opt_str(&self, nm: &str) -> Option<String> {
338 let vals = self.opt_vals(nm);
340 return None::<String>;
343 Val(ref s) => Some((*s).clone()),
349 /// Returns the matching string, a default, or none.
351 /// Returns none if the option was not present, `def` if the option was
352 /// present but no argument was provided, and the argument if the option was
353 /// present and an argument was provided.
354 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
355 let vals = self.opt_vals(nm);
360 Val(ref s) => Some((*s).clone()),
361 _ => Some(def.to_string())
368 fn is_arg(arg: &str) -> bool {
369 arg.len() > 1 && arg.as_bytes()[0] == b'-'
372 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
373 // Search main options.
374 let pos = opts.iter().position(|opt| opt.name == nm);
379 // Search in aliases.
380 for candidate in opts.iter() {
381 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
382 return opts.iter().position(|opt| opt.name == candidate.name);
389 /// Create a long option that is required and takes an argument.
391 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
392 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
393 /// * `desc` - Description for usage help
394 /// * `hint` - Hint that is used in place of the argument in the usage help,
395 /// e.g. `"FILE"` for a `-o FILE` option
396 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
397 let len = short_name.len();
398 assert!(len == 1 || len == 0);
400 short_name: short_name.to_string(),
401 long_name: long_name.to_string(),
402 hint: hint.to_string(),
403 desc: desc.to_string(),
409 /// Create a long option that is optional 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 optopt(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_string(),
421 long_name: long_name.to_string(),
422 hint: hint.to_string(),
423 desc: desc.to_string(),
429 /// Create a long option that is optional and does not take 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 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
435 let len = short_name.len();
436 assert!(len == 1 || len == 0);
438 short_name: short_name.to_string(),
439 long_name: long_name.to_string(),
440 hint: "".to_string(),
441 desc: desc.to_string(),
447 /// Create a long option that can occur more than once and does not
448 /// take an argument.
450 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
451 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
452 /// * `desc` - Description for usage help
453 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
454 let len = short_name.len();
455 assert!(len == 1 || len == 0);
457 short_name: short_name.to_string(),
458 long_name: long_name.to_string(),
459 hint: "".to_string(),
460 desc: desc.to_string(),
466 /// Create a long option that is optional and takes an optional argument.
468 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
469 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
470 /// * `desc` - Description for usage help
471 /// * `hint` - Hint that is used in place of the argument in the usage help,
472 /// e.g. `"FILE"` for a `-o FILE` option
473 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
474 let len = short_name.len();
475 assert!(len == 1 || len == 0);
477 short_name: short_name.to_string(),
478 long_name: long_name.to_string(),
479 hint: hint.to_string(),
480 desc: desc.to_string(),
486 /// Create a long option that is optional, takes an argument, and may occur
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 optmulti(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_string(),
499 long_name: long_name.to_string(),
500 hint: hint.to_string(),
501 desc: desc.to_string(),
507 /// Create a generic option group, stating all parameters explicitly
508 pub fn opt(short_name: &str,
513 occur: Occur) -> OptGroup {
514 let len = short_name.len();
515 assert!(len == 1 || len == 0);
517 short_name: short_name.to_string(),
518 long_name: long_name.to_string(),
519 hint: hint.to_string(),
520 desc: desc.to_string(),
527 /// Convert a `Fail` enum into an error string.
528 #[deprecated="use `Show` (`{}` format specifier)"]
529 pub fn to_err_msg(self) -> String {
534 impl fmt::Show for Fail {
535 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
537 ArgumentMissing(ref nm) => {
538 write!(f, "Argument to option '{}' missing.", *nm)
540 UnrecognizedOption(ref nm) => {
541 write!(f, "Unrecognized option: '{}'.", *nm)
543 OptionMissing(ref nm) => {
544 write!(f, "Required option '{}' missing.", *nm)
546 OptionDuplicated(ref nm) => {
547 write!(f, "Option '{}' given more than once.", *nm)
549 UnexpectedArgument(ref nm) => {
550 write!(f, "Option '{}' does not take an argument.", *nm)
556 /// Parse command line arguments according to the provided options.
558 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
559 /// `opt_str`, etc. to interrogate results.
562 /// Returns `Err(Fail)` on failure: use the `Show` implementation of `Fail` to display
563 /// information about it.
564 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
565 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
566 let n_opts = opts.len();
568 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
570 let mut vals = Vec::from_fn(n_opts, f);
571 let mut free: Vec<String> = Vec::new();
575 let cur = args[i].clone();
576 let curlen = cur.len();
577 if !is_arg(cur.as_slice()) {
579 } else if cur == "--" {
581 while j < l { free.push(args[j].clone()); j += 1; }
585 let mut i_arg = None;
586 if cur.as_bytes()[1] == b'-' {
587 let tail = cur.slice(2, curlen);
588 let tail_eq: Vec<&str> = tail.split('=').collect();
589 if tail_eq.len() <= 1 {
590 names = vec!(Long(tail.to_string()));
593 vec!(Long(tail_eq[0].to_string()));
594 i_arg = Some(tail_eq[1].to_string());
600 let range = cur.char_range_at(j);
601 let opt = Short(range.ch);
603 /* In a series of potential options (eg. -aheJ), if we
604 see one which takes an argument, we assume all
605 subsequent characters make up the argument. This
606 allows options such as -L/usr/local/lib/foo to be
607 interpreted correctly
610 let opt_id = match find_opt(opts.as_slice(), opt.clone()) {
612 None => return Err(UnrecognizedOption(opt.to_string()))
617 let arg_follows = match opts[opt_id].hasarg {
622 if arg_follows && range.next < curlen {
623 i_arg = Some(cur.slice(range.next, curlen).to_string());
630 let mut name_pos = 0;
631 for nm in names.iter() {
633 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
635 None => return Err(UnrecognizedOption(nm.to_string()))
637 match opts[optid].hasarg {
639 if name_pos == names.len() && !i_arg.is_none() {
640 return Err(UnexpectedArgument(nm.to_string()));
642 vals[optid].push(Given);
645 if !i_arg.is_none() {
647 .push(Val((i_arg.clone())
649 } else if name_pos < names.len() || i + 1 == l ||
650 is_arg(args[i + 1].as_slice()) {
651 vals[optid].push(Given);
654 vals[optid].push(Val(args[i].clone()));
658 if !i_arg.is_none() {
659 vals[optid].push(Val(i_arg.clone().unwrap()));
660 } else if i + 1 == l {
661 return Err(ArgumentMissing(nm.to_string()));
664 vals[optid].push(Val(args[i].clone()));
672 for i in range(0u, n_opts) {
673 let n = vals[i].len();
674 let occ = opts[i].occur;
675 if occ == Req && n == 0 {
676 return Err(OptionMissing(opts[i].name.to_string()));
678 if occ != Multi && n > 1 {
679 return Err(OptionDuplicated(opts[i].name.to_string()));
689 /// Derive a usage message from a set of long options.
690 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
692 let desc_sep = format!("\n{}", " ".repeat(24));
694 let rows = opts.iter().map(|optref| {
695 let OptGroup{short_name,
700 ..} = (*optref).clone();
702 let mut row = " ".repeat(4);
705 match short_name.len() {
709 row.push_str(short_name.as_slice());
712 _ => panic!("the short name should only be 1 ascii char long"),
716 match long_name.len() {
720 row.push_str(long_name.as_slice());
728 Yes => row.push_str(hint.as_slice()),
731 row.push_str(hint.as_slice());
736 // FIXME: #5516 should be graphemes not codepoints
737 // here we just need to indent the start of the description
738 let rowlen = row.char_len();
740 for _ in range(0, 24 - rowlen) {
744 row.push_str(desc_sep.as_slice())
747 // Normalize desc to contain words separated by one space character
748 let mut desc_normalized_whitespace = String::new();
749 for word in desc.words() {
750 desc_normalized_whitespace.push_str(word);
751 desc_normalized_whitespace.push(' ');
754 // FIXME: #5516 should be graphemes not codepoints
755 let mut desc_rows = Vec::new();
756 each_split_within(desc_normalized_whitespace.as_slice(),
759 desc_rows.push(substr.to_string());
763 // FIXME: #5516 should be graphemes not codepoints
764 // wrapped description
765 row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
770 format!("{}\n\nOptions:\n{}\n", brief,
771 rows.collect::<Vec<String>>().connect("\n"))
774 fn format_option(opt: &OptGroup) -> String {
775 let mut line = String::new();
777 if opt.occur != Req {
781 // Use short_name is possible, but fallback to long_name.
782 if opt.short_name.len() > 0 {
784 line.push_str(opt.short_name.as_slice());
787 line.push_str(opt.long_name.as_slice());
790 if opt.hasarg != No {
792 if opt.hasarg == Maybe {
795 line.push_str(opt.hint.as_slice());
796 if opt.hasarg == Maybe {
801 if opt.occur != Req {
804 if opt.occur == Multi {
811 /// Derive a short one-line usage summary from a set of long options.
812 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
813 let mut line = format!("Usage: {} ", program_name);
814 line.push_str(opts.iter()
816 .collect::<Vec<String>>()
822 enum SplitWithinState {
823 A, // leading whitespace, initial state
825 C, // internal and trailing whitespace
828 Ws, // current char is whitespace
829 Cr // current char is not whitespace
832 UnderLim, // current char makes current substring still fit in limit
833 OverLim // current char makes current substring no longer fit in limit
837 /// Splits a string into substrings with possibly internal whitespace,
838 /// each of them at most `lim` bytes long. The substrings have leading and trailing
839 /// whitespace removed, and are only cut at whitespace boundaries.
841 /// Note: Function was moved here from `std::str` because this module is the only place that
842 /// uses it, and because it was too specific for a general string function.
846 /// Panics during iteration if the string contains a non-whitespace
847 /// sequence longer than the limit.
848 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
850 use self::SplitWithinState::*;
851 use self::Whitespace::*;
852 use self::LengthLimit::*;
853 // Just for fun, let's write this as a state machine:
855 let mut slice_start = 0;
856 let mut last_start = 0;
857 let mut last_end = 0;
859 let mut fake_i = ss.len();
864 // if the limit is larger than the string, lower it to save cycles
869 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
870 let whitespace = if c.is_whitespace() { Ws } else { Cr };
871 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
873 state = match (state, whitespace, limit) {
875 (A, Cr, _) => { slice_start = i; last_start = i; B }
877 (B, Cr, UnderLim) => { B }
878 (B, Cr, OverLim) if (i - last_start + 1) > lim
879 => panic!("word starting with {} longer than limit!",
880 ss.slice(last_start, i + 1)),
881 (B, Cr, OverLim) => {
882 *cont = it(ss.slice(slice_start, last_end));
883 slice_start = last_start;
886 (B, Ws, UnderLim) => {
890 (B, Ws, OverLim) => {
892 *cont = it(ss.slice(slice_start, last_end));
896 (C, Cr, UnderLim) => {
900 (C, Cr, OverLim) => {
901 *cont = it(ss.slice(slice_start, last_end));
907 (C, Ws, OverLim) => {
908 *cont = it(ss.slice(slice_start, last_end));
911 (C, Ws, UnderLim) => {
919 ss.char_indices().all(|x| machine(&mut cont, x));
921 // Let the automaton 'run out' by supplying trailing whitespace
922 while cont && match state { B | C => true, A => false } {
923 machine(&mut cont, (fake_i, ' '));
930 fn test_split_within() {
931 fn t(s: &str, i: uint, u: &[String]) {
932 let mut v = Vec::new();
933 each_split_within(s, i, |s| { v.push(s.to_string()); true });
934 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
938 t("hello", 15, &["hello".to_string()]);
939 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
940 "Mary had a".to_string(),
941 "little lamb".to_string(),
942 "Little lamb".to_string()
944 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
945 &["Mary had a little lamb\nLittle lamb".to_string()]);
953 use std::result::Result::{Err, Ok};
959 let long_args = vec!("--test=20".to_string());
960 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
961 let rs = getopts(long_args.as_slice(), opts.as_slice());
964 assert!(m.opt_present("test"));
965 assert_eq!(m.opt_str("test").unwrap(), "20");
966 assert!(m.opt_present("t"));
967 assert_eq!(m.opt_str("t").unwrap(), "20");
969 _ => { panic!("test_reqopt failed (long arg)"); }
971 let short_args = vec!("-t".to_string(), "20".to_string());
972 match getopts(short_args.as_slice(), opts.as_slice()) {
974 assert!((m.opt_present("test")));
975 assert_eq!(m.opt_str("test").unwrap(), "20");
976 assert!((m.opt_present("t")));
977 assert_eq!(m.opt_str("t").unwrap(), "20");
979 _ => { panic!("test_reqopt failed (short arg)"); }
984 fn test_reqopt_missing() {
985 let args = vec!("blah".to_string());
986 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
987 let rs = getopts(args.as_slice(), opts.as_slice());
989 Err(OptionMissing(_)) => {},
995 fn test_reqopt_no_arg() {
996 let long_args = vec!("--test".to_string());
997 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
998 let rs = getopts(long_args.as_slice(), opts.as_slice());
1000 Err(ArgumentMissing(_)) => {},
1003 let short_args = vec!("-t".to_string());
1004 match getopts(short_args.as_slice(), opts.as_slice()) {
1005 Err(ArgumentMissing(_)) => {},
1011 fn test_reqopt_multi() {
1012 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1013 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1014 let rs = getopts(args.as_slice(), opts.as_slice());
1016 Err(OptionDuplicated(_)) => {},
1024 let long_args = vec!("--test=20".to_string());
1025 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1026 let rs = getopts(long_args.as_slice(), opts.as_slice());
1029 assert!(m.opt_present("test"));
1030 assert_eq!(m.opt_str("test").unwrap(), "20");
1031 assert!((m.opt_present("t")));
1032 assert_eq!(m.opt_str("t").unwrap(), "20");
1036 let short_args = vec!("-t".to_string(), "20".to_string());
1037 match getopts(short_args.as_slice(), opts.as_slice()) {
1039 assert!((m.opt_present("test")));
1040 assert_eq!(m.opt_str("test").unwrap(), "20");
1041 assert!((m.opt_present("t")));
1042 assert_eq!(m.opt_str("t").unwrap(), "20");
1049 fn test_optopt_missing() {
1050 let args = vec!("blah".to_string());
1051 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1052 let rs = getopts(args.as_slice(), opts.as_slice());
1055 assert!(!m.opt_present("test"));
1056 assert!(!m.opt_present("t"));
1063 fn test_optopt_no_arg() {
1064 let long_args = vec!("--test".to_string());
1065 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1066 let rs = getopts(long_args.as_slice(), opts.as_slice());
1068 Err(ArgumentMissing(_)) => {},
1071 let short_args = vec!("-t".to_string());
1072 match getopts(short_args.as_slice(), opts.as_slice()) {
1073 Err(ArgumentMissing(_)) => {},
1079 fn test_optopt_multi() {
1080 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1081 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1082 let rs = getopts(args.as_slice(), opts.as_slice());
1084 Err(OptionDuplicated(_)) => {},
1089 // Tests for optflag
1092 let long_args = vec!("--test".to_string());
1093 let opts = vec!(optflag("t", "test", "testing"));
1094 let rs = getopts(long_args.as_slice(), opts.as_slice());
1097 assert!(m.opt_present("test"));
1098 assert!(m.opt_present("t"));
1102 let short_args = vec!("-t".to_string());
1103 match getopts(short_args.as_slice(), opts.as_slice()) {
1105 assert!(m.opt_present("test"));
1106 assert!(m.opt_present("t"));
1113 fn test_optflag_missing() {
1114 let args = vec!("blah".to_string());
1115 let opts = vec!(optflag("t", "test", "testing"));
1116 let rs = getopts(args.as_slice(), opts.as_slice());
1119 assert!(!m.opt_present("test"));
1120 assert!(!m.opt_present("t"));
1127 fn test_optflag_long_arg() {
1128 let args = vec!("--test=20".to_string());
1129 let opts = vec!(optflag("t", "test", "testing"));
1130 let rs = getopts(args.as_slice(), opts.as_slice());
1132 Err(UnexpectedArgument(_)) => {},
1138 fn test_optflag_multi() {
1139 let args = vec!("--test".to_string(), "-t".to_string());
1140 let opts = vec!(optflag("t", "test", "testing"));
1141 let rs = getopts(args.as_slice(), opts.as_slice());
1143 Err(OptionDuplicated(_)) => {},
1149 fn test_optflag_short_arg() {
1150 let args = vec!("-t".to_string(), "20".to_string());
1151 let opts = vec!(optflag("t", "test", "testing"));
1152 let rs = getopts(args.as_slice(), opts.as_slice());
1155 // The next variable after the flag is just a free argument
1157 assert!(m.free[0] == "20");
1163 // Tests for optflagmulti
1165 fn test_optflagmulti_short1() {
1166 let args = vec!("-v".to_string());
1167 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1168 let rs = getopts(args.as_slice(), opts.as_slice());
1171 assert_eq!(m.opt_count("v"), 1);
1178 fn test_optflagmulti_short2a() {
1179 let args = vec!("-v".to_string(), "-v".to_string());
1180 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1181 let rs = getopts(args.as_slice(), opts.as_slice());
1184 assert_eq!(m.opt_count("v"), 2);
1191 fn test_optflagmulti_short2b() {
1192 let args = vec!("-vv".to_string());
1193 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1194 let rs = getopts(args.as_slice(), opts.as_slice());
1197 assert_eq!(m.opt_count("v"), 2);
1204 fn test_optflagmulti_long1() {
1205 let args = vec!("--verbose".to_string());
1206 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1207 let rs = getopts(args.as_slice(), opts.as_slice());
1210 assert_eq!(m.opt_count("verbose"), 1);
1217 fn test_optflagmulti_long2() {
1218 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1219 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1220 let rs = getopts(args.as_slice(), opts.as_slice());
1223 assert_eq!(m.opt_count("verbose"), 2);
1230 fn test_optflagmulti_mix() {
1231 let args = vec!("--verbose".to_string(), "-v".to_string(),
1232 "-vv".to_string(), "verbose".to_string());
1233 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1234 let rs = getopts(args.as_slice(), opts.as_slice());
1237 assert_eq!(m.opt_count("verbose"), 4);
1238 assert_eq!(m.opt_count("v"), 4);
1244 // Tests for optmulti
1246 fn test_optmulti() {
1247 let long_args = vec!("--test=20".to_string());
1248 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1249 let rs = getopts(long_args.as_slice(), opts.as_slice());
1252 assert!((m.opt_present("test")));
1253 assert_eq!(m.opt_str("test").unwrap(), "20");
1254 assert!((m.opt_present("t")));
1255 assert_eq!(m.opt_str("t").unwrap(), "20");
1259 let short_args = vec!("-t".to_string(), "20".to_string());
1260 match getopts(short_args.as_slice(), opts.as_slice()) {
1262 assert!((m.opt_present("test")));
1263 assert_eq!(m.opt_str("test").unwrap(), "20");
1264 assert!((m.opt_present("t")));
1265 assert_eq!(m.opt_str("t").unwrap(), "20");
1272 fn test_optmulti_missing() {
1273 let args = vec!("blah".to_string());
1274 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1275 let rs = getopts(args.as_slice(), opts.as_slice());
1278 assert!(!m.opt_present("test"));
1279 assert!(!m.opt_present("t"));
1286 fn test_optmulti_no_arg() {
1287 let long_args = vec!("--test".to_string());
1288 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1289 let rs = getopts(long_args.as_slice(), opts.as_slice());
1291 Err(ArgumentMissing(_)) => {},
1294 let short_args = vec!("-t".to_string());
1295 match getopts(short_args.as_slice(), opts.as_slice()) {
1296 Err(ArgumentMissing(_)) => {},
1302 fn test_optmulti_multi() {
1303 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1304 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1305 let rs = getopts(args.as_slice(), opts.as_slice());
1308 assert!(m.opt_present("test"));
1309 assert_eq!(m.opt_str("test").unwrap(), "20");
1310 assert!(m.opt_present("t"));
1311 assert_eq!(m.opt_str("t").unwrap(), "20");
1312 let pair = m.opt_strs("test");
1313 assert!(pair[0] == "20");
1314 assert!(pair[1] == "30");
1321 fn test_unrecognized_option() {
1322 let long_args = vec!("--untest".to_string());
1323 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1324 let rs = getopts(long_args.as_slice(), opts.as_slice());
1326 Err(UnrecognizedOption(_)) => {},
1329 let short_args = vec!("-u".to_string());
1330 match getopts(short_args.as_slice(), opts.as_slice()) {
1331 Err(UnrecognizedOption(_)) => {},
1337 fn test_combined() {
1339 vec!("prog".to_string(),
1340 "free1".to_string(),
1343 "free2".to_string(),
1344 "--flag".to_string(),
1345 "--long=30".to_string(),
1354 "-60 70".to_string());
1356 vec!(optopt("s", "something", "something", "SOMETHING"),
1357 optflag("", "flag", "a flag"),
1358 reqopt("", "long", "hi", "LONG"),
1359 optflag("f", "", "another flag"),
1360 optmulti("m", "", "mmmmmm", "YUM"),
1361 optmulti("n", "", "nothing", "NOTHING"),
1362 optopt("", "notpresent", "nothing to see here", "NOPE"));
1363 let rs = getopts(args.as_slice(), opts.as_slice());
1366 assert!(m.free[0] == "prog");
1367 assert!(m.free[1] == "free1");
1368 assert_eq!(m.opt_str("s").unwrap(), "20");
1369 assert!(m.free[2] == "free2");
1370 assert!((m.opt_present("flag")));
1371 assert_eq!(m.opt_str("long").unwrap(), "30");
1372 assert!((m.opt_present("f")));
1373 let pair = m.opt_strs("m");
1374 assert!(pair[0] == "40");
1375 assert!(pair[1] == "50");
1376 let pair = m.opt_strs("n");
1377 assert!(pair[0] == "-A B");
1378 assert!(pair[1] == "-60 70");
1379 assert!((!m.opt_present("notpresent")));
1387 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1388 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1389 optopt("f", "", "flag", "FLAG"));
1391 let args_single = vec!("-e".to_string(), "foo".to_string());
1392 let matches_single = &match getopts(args_single.as_slice(),
1394 result::Result::Ok(m) => m,
1395 result::Result::Err(_) => panic!()
1397 assert!(matches_single.opts_present(&["e".to_string()]));
1398 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1399 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1400 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1401 assert!(!matches_single.opts_present(&["thing".to_string()]));
1402 assert!(!matches_single.opts_present(&[]));
1404 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1405 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1407 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1410 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1412 let matches_both = &match getopts(args_both.as_slice(),
1414 result::Result::Ok(m) => m,
1415 result::Result::Err(_) => panic!()
1417 assert!(matches_both.opts_present(&["e".to_string()]));
1418 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1419 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1420 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1421 assert!(!matches_both.opts_present(&["f".to_string()]));
1422 assert!(!matches_both.opts_present(&["thing".to_string()]));
1423 assert!(!matches_both.opts_present(&[]));
1425 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1426 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1427 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1429 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1435 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1436 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1437 optmulti("M", "", "something", "MMMM"));
1438 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1439 result::Result::Ok(m) => m,
1440 result::Result::Err(_) => panic!()
1442 assert!(matches.opts_present(&["L".to_string()]));
1443 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1444 assert!(matches.opts_present(&["M".to_string()]));
1445 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1450 fn test_nospace_conflict() {
1451 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1452 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1453 optflagmulti("v", "verbose", "Verbose"));
1454 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1455 result::Result::Ok(m) => m,
1456 result::Result::Err(e) => panic!( "{}", e )
1458 assert!(matches.opts_present(&["L".to_string()]));
1459 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1460 assert!(matches.opts_present(&["v".to_string()]));
1461 assert_eq!(3, matches.opt_count("v"));
1465 fn test_long_to_short() {
1466 let mut short = Opt {
1467 name: Name::Long("banana".to_string()),
1468 hasarg: HasArg::Yes,
1470 aliases: Vec::new(),
1472 short.aliases = vec!(Opt { name: Name::Short('b'),
1473 hasarg: HasArg::Yes,
1475 aliases: Vec::new() });
1476 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1478 assert!(verbose.long_to_short() == short);
1482 fn test_aliases_long_and_short() {
1484 optflagmulti("a", "apple", "Desc"));
1486 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1488 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1489 assert_eq!(3, matches.opt_count("a"));
1490 assert_eq!(3, matches.opt_count("apple"));
1495 let optgroups = vec!(
1496 reqopt("b", "banana", "Desc", "VAL"),
1497 optopt("a", "012345678901234567890123456789",
1499 optflag("k", "kiwi", "Desc"),
1500 optflagopt("p", "", "Desc", "VAL"),
1501 optmulti("l", "", "Desc", "VAL"));
1507 -b --banana VAL Desc
1508 -a --012345678901234567890123456789 VAL
1515 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1517 debug!("expected: <<{}>>", expected);
1518 debug!("generated: <<{}>>", generated_usage);
1519 assert_eq!(generated_usage, expected);
1523 fn test_usage_description_wrapping() {
1524 // indentation should be 24 spaces
1525 // lines wrap after 78: or rather descriptions wrap after 54
1527 let optgroups = vec!(
1528 optflag("k", "kiwi",
1529 "This is a long description which won't be wrapped..+.."), // 54
1530 optflag("a", "apple",
1531 "This is a long description which _will_ be wrapped..+.."));
1537 -k --kiwi This is a long description which won't be wrapped..+..
1538 -a --apple This is a long description which _will_ be
1542 let usage = usage("Usage: fruits", optgroups.as_slice());
1544 debug!("expected: <<{}>>", expected);
1545 debug!("generated: <<{}>>", usage);
1546 assert!(usage == expected)
1550 fn test_usage_description_multibyte_handling() {
1551 let optgroups = vec!(
1552 optflag("k", "k\u2013w\u2013",
1553 "The word kiwi is normally spelled with two i's"),
1554 optflag("a", "apple",
1555 "This \u201Cdescription\u201D has some characters that could \
1556 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1562 -k --k–w– The word kiwi is normally spelled with two i's
1563 -a --apple This “description” has some characters that could
1564 confuse the line wrapping; an apple costs 0.51€ in
1565 some parts of Europe.
1568 let usage = usage("Usage: fruits", optgroups.as_slice());
1570 debug!("expected: <<{}>>", expected);
1571 debug!("generated: <<{}>>", usage);
1572 assert!(usage == expected)
1576 fn test_short_usage() {
1577 let optgroups = vec!(
1578 reqopt("b", "banana", "Desc", "VAL"),
1579 optopt("a", "012345678901234567890123456789",
1581 optflag("k", "kiwi", "Desc"),
1582 optflagopt("p", "", "Desc", "VAL"),
1583 optmulti("l", "", "Desc", "VAL"));
1585 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1586 let generated_usage = short_usage("fruits", optgroups.as_slice());
1588 debug!("expected: <<{}>>", expected);
1589 debug!("generated: <<{}>>", generated_usage);
1590 assert_eq!(generated_usage, expected);