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 //! extern crate getopts;
34 //! use getopts::{optopt,optflag,getopts,OptGroup};
37 //! fn do_work(inp: &str, out: Option<String>) {
38 //! println!("{}", inp);
40 //! Some(x) => println!("{}", x),
41 //! None => println!("No Output"),
45 //! fn print_usage(program: &str, _opts: &[OptGroup]) {
46 //! println!("Usage: {} [options]", program);
47 //! println!("-o\t\tOutput");
48 //! println!("-h --help\tUsage");
52 //! let args: Vec<String> = os::args();
54 //! let program = args.get(0).clone();
57 //! optopt("o", "", "set output file name", "NAME"),
58 //! optflag("h", "help", "print this help menu")
60 //! let matches = match getopts(args.tail(), opts) {
62 //! Err(f) => { fail!(f.to_str()) }
64 //! if matches.opt_present("h") {
65 //! print_usage(program.as_slice(), opts);
68 //! let output = matches.opt_str("o");
69 //! let input = if !matches.free.is_empty() {
70 //! (*matches.free.get(0)).clone()
72 //! print_usage(program.as_slice(), opts);
75 //! do_work(input.as_slice(), output);
79 #![crate_id = "getopts#0.11.0-pre"]
81 #![crate_type = "rlib"]
82 #![crate_type = "dylib"]
83 #![license = "MIT/ASL2"]
84 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
85 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
86 html_root_url = "http://doc.rust-lang.org/",
87 html_playground_url = "http://play.rust-lang.org/")]
88 #![feature(globs, phase)]
91 #[cfg(test)] extern crate debug;
92 #[cfg(test)] #[phase(plugin, link)] extern crate log;
94 use std::cmp::PartialEq;
96 use std::result::{Err, Ok};
98 use std::string::String;
100 /// Name of an option. Either a string or a single char.
101 #[deriving(Clone, PartialEq, Eq)]
103 /// A string representing the long name of an option.
104 /// For example: "help"
106 /// A char representing the short name of an option.
111 /// Describes whether an option has an argument.
112 #[deriving(Clone, PartialEq, Eq)]
114 /// The option requires an argument.
116 /// The option is just a flag, therefore no argument.
118 /// The option argument is optional and it could or not exist.
122 /// Describes how often an option may occur.
123 #[deriving(Clone, PartialEq, Eq)]
125 /// The option occurs once.
127 /// The option could or not occur.
129 /// The option occurs once or multiple times.
133 /// A description of a possible option.
134 #[deriving(Clone, PartialEq, Eq)]
136 /// Name of the option
138 /// Whether it has an argument
140 /// How often it can occur
142 /// Which options it aliases
143 pub aliases: Vec<Opt>,
146 /// One group of options, e.g., both -h and --help, along with
147 /// their shared description and properties.
148 #[deriving(Clone, PartialEq, Eq)]
149 pub struct OptGroup {
150 /// Short Name of the `OptGroup`
151 pub short_name: String,
152 /// Long Name of the `OptGroup`
153 pub long_name: String,
158 /// Whether it has an argument
160 /// How often it can occur
164 /// Describes whether an option is given at all or has a value.
165 #[deriving(Clone, PartialEq, Eq)]
171 /// The result of checking command line arguments. Contains a vector
172 /// of matches and a vector of free strings.
173 #[deriving(Clone, PartialEq, Eq)]
175 /// Options that matched
177 /// Values of the Options that matched
178 vals: Vec<Vec<Optval>>,
179 /// Free string fragments
180 pub free: Vec<String>,
183 /// The type returned when the command line does not conform to the
184 /// expected format. Use the `Show` implementation to output detailed
186 #[deriving(Clone, PartialEq, Eq)]
188 /// The option requires an argument but none was passed.
189 ArgumentMissing(String),
190 /// The passed option is not declared among the possible options.
191 UnrecognizedOption(String),
192 /// A required option is not present.
193 OptionMissing(String),
194 /// A single occurrence option is being used multiple times.
195 OptionDuplicated(String),
196 /// There's an argument being passed to a non-argument option.
197 UnexpectedArgument(String),
200 /// The type of failure that occurred.
201 #[deriving(PartialEq, Eq)]
202 #[allow(missing_doc)]
211 /// The result of parsing a command line with a set of options.
212 pub type Result = result::Result<Matches, Fail_>;
215 fn from_str(nm: &str) -> Name {
217 Short(nm.char_at(0u))
223 fn to_str(&self) -> String {
225 Short(ch) => ch.to_str(),
226 Long(ref s) => s.to_string()
232 /// Translate OptGroup into Opt.
233 /// (Both short and long names correspond to different Opts).
234 pub fn long_to_short(&self) -> Opt {
236 short_name: short_name,
237 long_name: long_name,
243 match (short_name.len(), long_name.len()) {
244 (0,0) => fail!("this long-format option was given no name"),
246 name: Long((long_name)),
252 name: Short(short_name.as_slice().char_at(0)),
258 name: Long((long_name)),
263 name: Short(short_name.as_slice().char_at(0)),
270 (_,_) => fail!("something is wrong with the long-form opt")
276 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
277 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
278 Some(id) => (*self.vals.get(id)).clone(),
279 None => fail!("No option '{}' defined", nm)
283 fn opt_val(&self, nm: &str) -> Option<Optval> {
284 let vals = self.opt_vals(nm);
288 Some((*vals.get(0)).clone())
292 /// Returns true if an option was matched.
293 pub fn opt_present(&self, nm: &str) -> bool {
294 !self.opt_vals(nm).is_empty()
297 /// Returns the number of times an option was matched.
298 pub fn opt_count(&self, nm: &str) -> uint {
299 self.opt_vals(nm).len()
302 /// Returns true if any of several options were matched.
303 pub fn opts_present(&self, names: &[String]) -> bool {
304 for nm in names.iter() {
305 match find_opt(self.opts.as_slice(),
306 Name::from_str(nm.as_slice())) {
307 Some(id) if !self.vals.get(id).is_empty() => return true,
314 /// Returns the string argument supplied to one of several matching options or `None`.
315 pub fn opts_str(&self, names: &[String]) -> Option<String> {
316 for nm in names.iter() {
317 match self.opt_val(nm.as_slice()) {
318 Some(Val(ref s)) => return Some(s.clone()),
325 /// Returns a vector of the arguments provided to all matches of the given
328 /// Used when an option accepts multiple values.
329 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
330 let mut acc: Vec<String> = Vec::new();
331 let r = self.opt_vals(nm);
334 Val(ref s) => acc.push((*s).clone()),
341 /// Returns the string argument supplied to a matching option or `None`.
342 pub fn opt_str(&self, nm: &str) -> Option<String> {
343 let vals = self.opt_vals(nm);
345 return None::<String>;
348 &Val(ref s) => Some((*s).clone()),
354 /// Returns the matching string, a default, or none.
356 /// Returns none if the option was not present, `def` if the option was
357 /// present but no argument was provided, and the argument if the option was
358 /// present and an argument was provided.
359 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
360 let vals = self.opt_vals(nm);
365 &Val(ref s) => Some((*s).clone()),
366 _ => Some(def.to_string())
372 fn is_arg(arg: &str) -> bool {
373 arg.len() > 1 && arg[0] == '-' as u8
376 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
377 // Search main options.
378 let pos = opts.iter().position(|opt| opt.name == nm);
383 // Search in aliases.
384 for candidate in opts.iter() {
385 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
386 return opts.iter().position(|opt| opt.name == candidate.name);
393 /// Create a long option that is required and takes an argument.
394 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
395 let len = short_name.len();
396 assert!(len == 1 || len == 0);
398 short_name: short_name.to_string(),
399 long_name: long_name.to_string(),
400 hint: hint.to_string(),
401 desc: desc.to_string(),
407 /// Create a long option that is optional and takes an argument.
408 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
409 let len = short_name.len();
410 assert!(len == 1 || len == 0);
412 short_name: short_name.to_string(),
413 long_name: long_name.to_string(),
414 hint: hint.to_string(),
415 desc: desc.to_string(),
421 /// Create a long option that is optional and does not take an argument.
422 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
423 let len = short_name.len();
424 assert!(len == 1 || len == 0);
426 short_name: short_name.to_string(),
427 long_name: long_name.to_string(),
428 hint: "".to_string(),
429 desc: desc.to_string(),
435 /// Create a long option that can occur more than once and does not
436 /// take an argument.
437 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
438 let len = short_name.len();
439 assert!(len == 1 || len == 0);
441 short_name: short_name.to_string(),
442 long_name: long_name.to_string(),
443 hint: "".to_string(),
444 desc: desc.to_string(),
450 /// Create a long option that is optional and takes an optional argument.
451 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
452 let len = short_name.len();
453 assert!(len == 1 || len == 0);
455 short_name: short_name.to_string(),
456 long_name: long_name.to_string(),
457 hint: hint.to_string(),
458 desc: desc.to_string(),
464 /// Create a long option that is optional, takes an argument, and may occur
466 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
467 let len = short_name.len();
468 assert!(len == 1 || len == 0);
470 short_name: short_name.to_string(),
471 long_name: long_name.to_string(),
472 hint: hint.to_string(),
473 desc: desc.to_string(),
479 /// Create a generic option group, stating all parameters explicitly
480 pub fn opt(short_name: &str,
485 occur: Occur) -> OptGroup {
486 let len = short_name.len();
487 assert!(len == 1 || len == 0);
489 short_name: short_name.to_string(),
490 long_name: long_name.to_string(),
491 hint: hint.to_string(),
492 desc: desc.to_string(),
499 /// Convert a `Fail_` enum into an error string.
500 #[deprecated="use `Show` (`{}` format specifier)"]
501 pub fn to_err_msg(self) -> String {
506 impl fmt::Show for Fail_ {
507 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509 ArgumentMissing(ref nm) => {
510 write!(f, "Argument to option '{}' missing.", *nm)
512 UnrecognizedOption(ref nm) => {
513 write!(f, "Unrecognized option: '{}'.", *nm)
515 OptionMissing(ref nm) => {
516 write!(f, "Required option '{}' missing.", *nm)
518 OptionDuplicated(ref nm) => {
519 write!(f, "Option '{}' given more than once.", *nm)
521 UnexpectedArgument(ref nm) => {
522 write!(f, "Option '{}' does not take an argument.", *nm)
528 /// Parse command line arguments according to the provided options.
530 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
531 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on
532 /// failure: use the `Show` implementation of `Fail_` to display
533 /// information about it.
534 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
535 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
536 let n_opts = opts.len();
538 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
540 let mut vals = Vec::from_fn(n_opts, f);
541 let mut free: Vec<String> = Vec::new();
545 let cur = args[i].clone();
546 let curlen = cur.len();
547 if !is_arg(cur.as_slice()) {
549 } else if cur.as_slice() == "--" {
551 while j < l { free.push(args[j].clone()); j += 1; }
555 let mut i_arg = None;
556 if cur.as_slice()[1] == '-' as u8 {
557 let tail = cur.as_slice().slice(2, curlen);
558 let tail_eq: Vec<&str> = tail.split('=').collect();
559 if tail_eq.len() <= 1 {
560 names = vec!(Long(tail.to_string()));
563 vec!(Long((*tail_eq.get(0)).to_string()));
564 i_arg = Some((*tail_eq.get(1)).to_string());
568 let mut last_valid_opt_id = None;
571 let range = cur.as_slice().char_range_at(j);
572 let opt = Short(range.ch);
574 /* In a series of potential options (eg. -aheJ), if we
575 see one which takes an argument, we assume all
576 subsequent characters make up the argument. This
577 allows options such as -L/usr/local/lib/foo to be
578 interpreted correctly
581 match find_opt(opts.as_slice(), opt.clone()) {
582 Some(id) => last_valid_opt_id = Some(id),
585 last_valid_opt_id.is_some() &&
586 match opts.get(last_valid_opt_id.unwrap())
592 if arg_follows && j < curlen {
593 i_arg = Some(cur.as_slice()
594 .slice(j, curlen).to_string());
597 last_valid_opt_id = None;
605 let mut name_pos = 0;
606 for nm in names.iter() {
608 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
610 None => return Err(UnrecognizedOption(nm.to_str()))
612 match opts.get(optid).hasarg {
614 if !i_arg.is_none() {
615 return Err(UnexpectedArgument(nm.to_str()));
617 vals.get_mut(optid).push(Given);
620 if !i_arg.is_none() {
622 .push(Val((i_arg.clone())
624 } else if name_pos < names.len() || i + 1 == l ||
625 is_arg(args[i + 1].as_slice()) {
626 vals.get_mut(optid).push(Given);
629 vals.get_mut(optid).push(Val(args[i].clone()));
633 if !i_arg.is_none() {
634 vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
635 } else if i + 1 == l {
636 return Err(ArgumentMissing(nm.to_str()));
639 vals.get_mut(optid).push(Val(args[i].clone()));
649 let n = vals.get(i).len();
650 let occ = opts.get(i).occur;
653 return Err(OptionMissing(opts.get(i).name.to_str()));
658 return Err(OptionDuplicated(opts.get(i).name.to_str()));
670 /// Derive a usage message from a set of long options.
671 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
673 let desc_sep = format!("\n{}", " ".repeat(24));
675 let mut rows = opts.iter().map(|optref| {
676 let OptGroup{short_name: short_name,
677 long_name: long_name,
681 ..} = (*optref).clone();
683 let mut row = " ".repeat(4);
686 match short_name.len() {
690 row.push_str(short_name.as_slice());
693 _ => fail!("the short name should only be 1 ascii char long"),
697 match long_name.len() {
701 row.push_str(long_name.as_slice());
709 Yes => row.push_str(hint.as_slice()),
712 row.push_str(hint.as_slice());
717 // FIXME: #5516 should be graphemes not codepoints
718 // here we just need to indent the start of the description
719 let rowlen = row.as_slice().char_len();
721 for _ in range(0, 24 - rowlen) {
725 row.push_str(desc_sep.as_slice())
728 // Normalize desc to contain words separated by one space character
729 let mut desc_normalized_whitespace = String::new();
730 for word in desc.as_slice().words() {
731 desc_normalized_whitespace.push_str(word);
732 desc_normalized_whitespace.push_char(' ');
735 // FIXME: #5516 should be graphemes not codepoints
736 let mut desc_rows = Vec::new();
737 each_split_within(desc_normalized_whitespace.as_slice(),
740 desc_rows.push(substr.to_string());
744 // FIXME: #5516 should be graphemes not codepoints
745 // wrapped description
746 row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
751 format!("{}\n\nOptions:\n{}\n", brief,
752 rows.collect::<Vec<String>>().connect("\n"))
755 fn format_option(opt: &OptGroup) -> String {
756 let mut line = String::new();
758 if opt.occur != Req {
762 // Use short_name is possible, but fallback to long_name.
763 if opt.short_name.len() > 0 {
765 line.push_str(opt.short_name.as_slice());
768 line.push_str(opt.long_name.as_slice());
771 if opt.hasarg != No {
773 if opt.hasarg == Maybe {
776 line.push_str(opt.hint.as_slice());
777 if opt.hasarg == Maybe {
782 if opt.occur != Req {
785 if opt.occur == Multi {
792 /// Derive a short one-line usage summary from a set of long options.
793 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
794 let mut line = format!("Usage: {} ", program_name);
795 line.push_str(opts.iter()
797 .collect::<Vec<String>>()
804 /// Splits a string into substrings with possibly internal whitespace,
805 /// each of them at most `lim` bytes long. The substrings have leading and trailing
806 /// whitespace removed, and are only cut at whitespace boundaries.
808 /// Note: Function was moved here from `std::str` because this module is the only place that
809 /// uses it, and because it was to specific for a general string function.
813 /// Fails during iteration if the string contains a non-whitespace
814 /// sequence longer than the limit.
815 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
817 // Just for fun, let's write this as a state machine:
819 enum SplitWithinState {
820 A, // leading whitespace, initial state
822 C, // internal and trailing whitespace
825 Ws, // current char is whitespace
826 Cr // current char is not whitespace
829 UnderLim, // current char makes current substring still fit in limit
830 OverLim // current char makes current substring no longer fit in limit
833 let mut slice_start = 0;
834 let mut last_start = 0;
835 let mut last_end = 0;
837 let mut fake_i = ss.len();
842 // if the limit is larger than the string, lower it to save cycles
847 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
848 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
849 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
851 state = match (state, whitespace, limit) {
853 (A, Cr, _) => { slice_start = i; last_start = i; B }
855 (B, Cr, UnderLim) => { B }
856 (B, Cr, OverLim) if (i - last_start + 1) > lim
857 => fail!("word starting with {} longer than limit!",
858 ss.slice(last_start, i + 1)),
859 (B, Cr, OverLim) => {
860 *cont = it(ss.slice(slice_start, last_end));
861 slice_start = last_start;
864 (B, Ws, UnderLim) => {
868 (B, Ws, OverLim) => {
870 *cont = it(ss.slice(slice_start, last_end));
874 (C, Cr, UnderLim) => {
878 (C, Cr, OverLim) => {
879 *cont = it(ss.slice(slice_start, last_end));
885 (C, Ws, OverLim) => {
886 *cont = it(ss.slice(slice_start, last_end));
889 (C, Ws, UnderLim) => {
897 ss.char_indices().advance(|x| machine(&mut cont, x));
899 // Let the automaton 'run out' by supplying trailing whitespace
900 while cont && match state { B | C => true, A => false } {
901 machine(&mut cont, (fake_i, ' '));
908 fn test_split_within() {
909 fn t(s: &str, i: uint, u: &[String]) {
910 let mut v = Vec::new();
911 each_split_within(s, i, |s| { v.push(s.to_string()); true });
912 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
916 t("hello", 15, ["hello".to_string()]);
917 t("\nMary had a little lamb\nLittle lamb\n", 15, [
918 "Mary had a".to_string(),
919 "little lamb".to_string(),
920 "Little lamb".to_string()
922 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
923 ["Mary had a little lamb\nLittle lamb".to_string()]);
930 use std::result::{Err, Ok};
933 fn check_fail_type(f: Fail_, ft: FailType) {
935 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
936 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
937 OptionMissing(_) => assert!(ft == OptionMissing_),
938 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
939 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
946 let long_args = vec!("--test=20".to_string());
947 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
948 let rs = getopts(long_args.as_slice(), opts.as_slice());
951 assert!(m.opt_present("test"));
952 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
953 assert!(m.opt_present("t"));
954 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
956 _ => { fail!("test_reqopt failed (long arg)"); }
958 let short_args = vec!("-t".to_string(), "20".to_string());
959 match getopts(short_args.as_slice(), opts.as_slice()) {
961 assert!((m.opt_present("test")));
962 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
963 assert!((m.opt_present("t")));
964 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
966 _ => { fail!("test_reqopt failed (short arg)"); }
971 fn test_reqopt_missing() {
972 let args = vec!("blah".to_string());
973 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
974 let rs = getopts(args.as_slice(), opts.as_slice());
976 Err(f) => check_fail_type(f, OptionMissing_),
982 fn test_reqopt_no_arg() {
983 let long_args = vec!("--test".to_string());
984 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
985 let rs = getopts(long_args.as_slice(), opts.as_slice());
987 Err(f) => check_fail_type(f, ArgumentMissing_),
990 let short_args = vec!("-t".to_string());
991 match getopts(short_args.as_slice(), opts.as_slice()) {
992 Err(f) => check_fail_type(f, ArgumentMissing_),
998 fn test_reqopt_multi() {
999 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1000 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1001 let rs = getopts(args.as_slice(), opts.as_slice());
1003 Err(f) => check_fail_type(f, OptionDuplicated_),
1011 let long_args = vec!("--test=20".to_string());
1012 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1013 let rs = getopts(long_args.as_slice(), opts.as_slice());
1016 assert!(m.opt_present("test"));
1017 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1018 assert!((m.opt_present("t")));
1019 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1023 let short_args = vec!("-t".to_string(), "20".to_string());
1024 match getopts(short_args.as_slice(), opts.as_slice()) {
1026 assert!((m.opt_present("test")));
1027 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1028 assert!((m.opt_present("t")));
1029 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1036 fn test_optopt_missing() {
1037 let args = vec!("blah".to_string());
1038 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1039 let rs = getopts(args.as_slice(), opts.as_slice());
1042 assert!(!m.opt_present("test"));
1043 assert!(!m.opt_present("t"));
1050 fn test_optopt_no_arg() {
1051 let long_args = vec!("--test".to_string());
1052 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1053 let rs = getopts(long_args.as_slice(), opts.as_slice());
1055 Err(f) => check_fail_type(f, ArgumentMissing_),
1058 let short_args = vec!("-t".to_string());
1059 match getopts(short_args.as_slice(), opts.as_slice()) {
1060 Err(f) => check_fail_type(f, ArgumentMissing_),
1066 fn test_optopt_multi() {
1067 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1068 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1069 let rs = getopts(args.as_slice(), opts.as_slice());
1071 Err(f) => check_fail_type(f, OptionDuplicated_),
1076 // Tests for optflag
1079 let long_args = vec!("--test".to_string());
1080 let opts = vec!(optflag("t", "test", "testing"));
1081 let rs = getopts(long_args.as_slice(), opts.as_slice());
1084 assert!(m.opt_present("test"));
1085 assert!(m.opt_present("t"));
1089 let short_args = vec!("-t".to_string());
1090 match getopts(short_args.as_slice(), opts.as_slice()) {
1092 assert!(m.opt_present("test"));
1093 assert!(m.opt_present("t"));
1100 fn test_optflag_missing() {
1101 let args = vec!("blah".to_string());
1102 let opts = vec!(optflag("t", "test", "testing"));
1103 let rs = getopts(args.as_slice(), opts.as_slice());
1106 assert!(!m.opt_present("test"));
1107 assert!(!m.opt_present("t"));
1114 fn test_optflag_long_arg() {
1115 let args = vec!("--test=20".to_string());
1116 let opts = vec!(optflag("t", "test", "testing"));
1117 let rs = getopts(args.as_slice(), opts.as_slice());
1120 check_fail_type(f, UnexpectedArgument_);
1127 fn test_optflag_multi() {
1128 let args = vec!("--test".to_string(), "-t".to_string());
1129 let opts = vec!(optflag("t", "test", "testing"));
1130 let rs = getopts(args.as_slice(), opts.as_slice());
1132 Err(f) => check_fail_type(f, OptionDuplicated_),
1138 fn test_optflag_short_arg() {
1139 let args = vec!("-t".to_string(), "20".to_string());
1140 let opts = vec!(optflag("t", "test", "testing"));
1141 let rs = getopts(args.as_slice(), opts.as_slice());
1144 // The next variable after the flag is just a free argument
1146 assert!(*m.free.get(0) == "20".to_string());
1152 // Tests for optflagmulti
1154 fn test_optflagmulti_short1() {
1155 let args = vec!("-v".to_string());
1156 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1157 let rs = getopts(args.as_slice(), opts.as_slice());
1160 assert_eq!(m.opt_count("v"), 1);
1167 fn test_optflagmulti_short2a() {
1168 let args = vec!("-v".to_string(), "-v".to_string());
1169 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1170 let rs = getopts(args.as_slice(), opts.as_slice());
1173 assert_eq!(m.opt_count("v"), 2);
1180 fn test_optflagmulti_short2b() {
1181 let args = vec!("-vv".to_string());
1182 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1183 let rs = getopts(args.as_slice(), opts.as_slice());
1186 assert_eq!(m.opt_count("v"), 2);
1193 fn test_optflagmulti_long1() {
1194 let args = vec!("--verbose".to_string());
1195 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1196 let rs = getopts(args.as_slice(), opts.as_slice());
1199 assert_eq!(m.opt_count("verbose"), 1);
1206 fn test_optflagmulti_long2() {
1207 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1208 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1209 let rs = getopts(args.as_slice(), opts.as_slice());
1212 assert_eq!(m.opt_count("verbose"), 2);
1219 fn test_optflagmulti_mix() {
1220 let args = vec!("--verbose".to_string(), "-v".to_string(),
1221 "-vv".to_string(), "verbose".to_string());
1222 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1223 let rs = getopts(args.as_slice(), opts.as_slice());
1226 assert_eq!(m.opt_count("verbose"), 4);
1227 assert_eq!(m.opt_count("v"), 4);
1233 // Tests for optmulti
1235 fn test_optmulti() {
1236 let long_args = vec!("--test=20".to_string());
1237 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1238 let rs = getopts(long_args.as_slice(), opts.as_slice());
1241 assert!((m.opt_present("test")));
1242 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1243 assert!((m.opt_present("t")));
1244 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1248 let short_args = vec!("-t".to_string(), "20".to_string());
1249 match getopts(short_args.as_slice(), opts.as_slice()) {
1251 assert!((m.opt_present("test")));
1252 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1253 assert!((m.opt_present("t")));
1254 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1261 fn test_optmulti_missing() {
1262 let args = vec!("blah".to_string());
1263 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1264 let rs = getopts(args.as_slice(), opts.as_slice());
1267 assert!(!m.opt_present("test"));
1268 assert!(!m.opt_present("t"));
1275 fn test_optmulti_no_arg() {
1276 let long_args = vec!("--test".to_string());
1277 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1278 let rs = getopts(long_args.as_slice(), opts.as_slice());
1280 Err(f) => check_fail_type(f, ArgumentMissing_),
1283 let short_args = vec!("-t".to_string());
1284 match getopts(short_args.as_slice(), opts.as_slice()) {
1285 Err(f) => check_fail_type(f, ArgumentMissing_),
1291 fn test_optmulti_multi() {
1292 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1293 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1294 let rs = getopts(args.as_slice(), opts.as_slice());
1297 assert!(m.opt_present("test"));
1298 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1299 assert!(m.opt_present("t"));
1300 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1301 let pair = m.opt_strs("test");
1302 assert!(*pair.get(0) == "20".to_string());
1303 assert!(*pair.get(1) == "30".to_string());
1310 fn test_unrecognized_option() {
1311 let long_args = vec!("--untest".to_string());
1312 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1313 let rs = getopts(long_args.as_slice(), opts.as_slice());
1315 Err(f) => check_fail_type(f, UnrecognizedOption_),
1318 let short_args = vec!("-u".to_string());
1319 match getopts(short_args.as_slice(), opts.as_slice()) {
1320 Err(f) => check_fail_type(f, UnrecognizedOption_),
1326 fn test_combined() {
1328 vec!("prog".to_string(),
1329 "free1".to_string(),
1332 "free2".to_string(),
1333 "--flag".to_string(),
1334 "--long=30".to_string(),
1343 "-60 70".to_string());
1345 vec!(optopt("s", "something", "something", "SOMETHING"),
1346 optflag("", "flag", "a flag"),
1347 reqopt("", "long", "hi", "LONG"),
1348 optflag("f", "", "another flag"),
1349 optmulti("m", "", "mmmmmm", "YUM"),
1350 optmulti("n", "", "nothing", "NOTHING"),
1351 optopt("", "notpresent", "nothing to see here", "NOPE"));
1352 let rs = getopts(args.as_slice(), opts.as_slice());
1355 assert!(*m.free.get(0) == "prog".to_string());
1356 assert!(*m.free.get(1) == "free1".to_string());
1357 assert_eq!(m.opt_str("s").unwrap(), "20".to_string());
1358 assert!(*m.free.get(2) == "free2".to_string());
1359 assert!((m.opt_present("flag")));
1360 assert_eq!(m.opt_str("long").unwrap(), "30".to_string());
1361 assert!((m.opt_present("f")));
1362 let pair = m.opt_strs("m");
1363 assert!(*pair.get(0) == "40".to_string());
1364 assert!(*pair.get(1) == "50".to_string());
1365 let pair = m.opt_strs("n");
1366 assert!(*pair.get(0) == "-A B".to_string());
1367 assert!(*pair.get(1) == "-60 70".to_string());
1368 assert!((!m.opt_present("notpresent")));
1376 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1377 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1378 optopt("f", "", "flag", "FLAG"));
1380 let args_single = vec!("-e".to_string(), "foo".to_string());
1381 let matches_single = &match getopts(args_single.as_slice(),
1384 result::Err(_) => fail!()
1386 assert!(matches_single.opts_present(["e".to_string()]));
1387 assert!(matches_single.opts_present(["encrypt".to_string(), "e".to_string()]));
1388 assert!(matches_single.opts_present(["e".to_string(), "encrypt".to_string()]));
1389 assert!(!matches_single.opts_present(["encrypt".to_string()]));
1390 assert!(!matches_single.opts_present(["thing".to_string()]));
1391 assert!(!matches_single.opts_present([]));
1393 assert_eq!(matches_single.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1394 assert_eq!(matches_single.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1396 assert_eq!(matches_single.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1399 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1401 let matches_both = &match getopts(args_both.as_slice(),
1404 result::Err(_) => fail!()
1406 assert!(matches_both.opts_present(["e".to_string()]));
1407 assert!(matches_both.opts_present(["encrypt".to_string()]));
1408 assert!(matches_both.opts_present(["encrypt".to_string(), "e".to_string()]));
1409 assert!(matches_both.opts_present(["e".to_string(), "encrypt".to_string()]));
1410 assert!(!matches_both.opts_present(["f".to_string()]));
1411 assert!(!matches_both.opts_present(["thing".to_string()]));
1412 assert!(!matches_both.opts_present([]));
1414 assert_eq!(matches_both.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1415 assert_eq!(matches_both.opts_str(["encrypt".to_string()]).unwrap(), "foo".to_string());
1416 assert_eq!(matches_both.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1418 assert_eq!(matches_both.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1424 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1425 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1426 optmulti("M", "", "something", "MMMM"));
1427 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1429 result::Err(_) => fail!()
1431 assert!(matches.opts_present(["L".to_string()]));
1432 assert_eq!(matches.opts_str(["L".to_string()]).unwrap(), "foo".to_string());
1433 assert!(matches.opts_present(["M".to_string()]));
1434 assert_eq!(matches.opts_str(["M".to_string()]).unwrap(), ".".to_string());
1439 fn test_long_to_short() {
1440 let mut short = Opt {
1441 name: Long("banana".to_string()),
1444 aliases: Vec::new(),
1446 short.aliases = vec!(Opt { name: Short('b'),
1449 aliases: Vec::new() });
1450 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1452 assert!(verbose.long_to_short() == short);
1456 fn test_aliases_long_and_short() {
1458 optflagmulti("a", "apple", "Desc"));
1460 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1462 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1463 assert_eq!(3, matches.opt_count("a"));
1464 assert_eq!(3, matches.opt_count("apple"));
1469 let optgroups = vec!(
1470 reqopt("b", "banana", "Desc", "VAL"),
1471 optopt("a", "012345678901234567890123456789",
1473 optflag("k", "kiwi", "Desc"),
1474 optflagopt("p", "", "Desc", "VAL"),
1475 optmulti("l", "", "Desc", "VAL"));
1481 -b --banana VAL Desc
1482 -a --012345678901234567890123456789 VAL
1489 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1491 debug!("expected: <<{}>>", expected);
1492 debug!("generated: <<{}>>", generated_usage);
1493 assert_eq!(generated_usage, expected);
1497 fn test_usage_description_wrapping() {
1498 // indentation should be 24 spaces
1499 // lines wrap after 78: or rather descriptions wrap after 54
1501 let optgroups = vec!(
1502 optflag("k", "kiwi",
1503 "This is a long description which won't be wrapped..+.."), // 54
1504 optflag("a", "apple",
1505 "This is a long description which _will_ be wrapped..+.."));
1511 -k --kiwi This is a long description which won't be wrapped..+..
1512 -a --apple This is a long description which _will_ be
1516 let usage = usage("Usage: fruits", optgroups.as_slice());
1518 debug!("expected: <<{}>>", expected);
1519 debug!("generated: <<{}>>", usage);
1520 assert!(usage == expected)
1524 fn test_usage_description_multibyte_handling() {
1525 let optgroups = vec!(
1526 optflag("k", "k\u2013w\u2013",
1527 "The word kiwi is normally spelled with two i's"),
1528 optflag("a", "apple",
1529 "This \u201Cdescription\u201D has some characters that could \
1530 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1536 -k --k–w– The word kiwi is normally spelled with two i's
1537 -a --apple This “description” has some characters that could
1538 confuse the line wrapping; an apple costs 0.51€ in
1539 some parts of Europe.
1542 let usage = usage("Usage: fruits", optgroups.as_slice());
1544 debug!("expected: <<{}>>", expected);
1545 debug!("generated: <<{}>>", usage);
1546 assert!(usage == expected)
1550 fn test_short_usage() {
1551 let optgroups = vec!(
1552 reqopt("b", "banana", "Desc", "VAL"),
1553 optopt("a", "012345678901234567890123456789",
1555 optflag("k", "kiwi", "Desc"),
1556 optflagopt("p", "", "Desc", "VAL"),
1557 optmulti("l", "", "Desc", "VAL"));
1559 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1560 let generated_usage = short_usage("fruits", optgroups.as_slice());
1562 debug!("expected: <<{}>>", expected);
1563 debug!("generated: <<{}>>", generated_usage);
1564 assert_eq!(generated_usage, expected);