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) => { fail!(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 #![license = "MIT/ASL2"]
86 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
87 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
88 html_root_url = "http://doc.rust-lang.org/master/",
89 html_playground_url = "http://play.rust-lang.org/")]
90 #![feature(globs, phase)]
93 #[cfg(test)] extern crate debug;
94 #[cfg(test)] #[phase(plugin, link)] extern crate log;
96 use std::cmp::PartialEq;
98 use std::result::{Err, Ok};
100 use std::string::String;
102 /// Name of an option. Either a string or a single char.
103 #[deriving(Clone, PartialEq, Eq)]
105 /// A string representing the long name of an option.
106 /// For example: "help"
108 /// A char representing the short name of an option.
113 /// Describes whether an option has an argument.
114 #[deriving(Clone, PartialEq, Eq)]
116 /// The option requires an argument.
118 /// The option is just a flag, therefore no argument.
120 /// The option argument is optional and it could or not exist.
124 /// Describes how often an option may occur.
125 #[deriving(Clone, PartialEq, Eq)]
127 /// The option occurs once.
129 /// The option could or not occur.
131 /// The option occurs once or multiple times.
135 /// A description of a possible option.
136 #[deriving(Clone, PartialEq, Eq)]
138 /// Name of the option
140 /// Whether it has an argument
142 /// How often it can occur
144 /// Which options it aliases
145 pub aliases: Vec<Opt>,
148 /// One group of options, e.g., both -h and --help, along with
149 /// their shared description and properties.
150 #[deriving(Clone, PartialEq, Eq)]
151 pub struct OptGroup {
152 /// Short Name of the `OptGroup`
153 pub short_name: String,
154 /// Long Name of the `OptGroup`
155 pub long_name: String,
160 /// Whether it has an argument
162 /// How often it can occur
166 /// Describes whether an option is given at all or has a value.
167 #[deriving(Clone, PartialEq, Eq)]
173 /// The result of checking command line arguments. Contains a vector
174 /// of matches and a vector of free strings.
175 #[deriving(Clone, PartialEq, Eq)]
177 /// Options that matched
179 /// Values of the Options that matched
180 vals: Vec<Vec<Optval>>,
181 /// Free string fragments
182 pub free: Vec<String>,
185 /// The type returned when the command line does not conform to the
186 /// expected format. Use the `Show` implementation to output detailed
188 #[deriving(Clone, PartialEq, Eq)]
190 /// The option requires an argument but none was passed.
191 ArgumentMissing(String),
192 /// The passed option is not declared among the possible options.
193 UnrecognizedOption(String),
194 /// A required option is not present.
195 OptionMissing(String),
196 /// A single occurrence option is being used multiple times.
197 OptionDuplicated(String),
198 /// There's an argument being passed to a non-argument option.
199 UnexpectedArgument(String),
202 /// The type of failure that occurred.
203 #[deriving(PartialEq, Eq)]
204 #[allow(missing_doc)]
213 /// The result of parsing a command line with a set of options.
214 pub type Result = result::Result<Matches, Fail_>;
217 fn from_str(nm: &str) -> Name {
219 Short(nm.char_at(0u))
225 fn to_string(&self) -> String {
227 Short(ch) => ch.to_string(),
228 Long(ref s) => s.to_string()
234 /// Translate OptGroup into Opt.
235 /// (Both short and long names correspond to different Opts).
236 pub fn long_to_short(&self) -> Opt {
238 short_name: short_name,
239 long_name: long_name,
245 match (short_name.len(), long_name.len()) {
246 (0,0) => fail!("this long-format option was given no name"),
248 name: Long((long_name)),
254 name: Short(short_name.as_slice().char_at(0)),
260 name: Long((long_name)),
265 name: Short(short_name.as_slice().char_at(0)),
272 (_,_) => fail!("something is wrong with the long-form opt")
278 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
279 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
280 Some(id) => self.vals[id].clone(),
281 None => fail!("No option '{}' defined", nm)
285 fn opt_val(&self, nm: &str) -> Option<Optval> {
286 let vals = self.opt_vals(nm);
290 Some(vals[0].clone())
294 /// Returns true if an option was matched.
295 pub fn opt_present(&self, nm: &str) -> bool {
296 !self.opt_vals(nm).is_empty()
299 /// Returns the number of times an option was matched.
300 pub fn opt_count(&self, nm: &str) -> uint {
301 self.opt_vals(nm).len()
304 /// Returns true if any of several options were matched.
305 pub fn opts_present(&self, names: &[String]) -> bool {
306 for nm in names.iter() {
307 match find_opt(self.opts.as_slice(),
308 Name::from_str(nm.as_slice())) {
309 Some(id) if !self.vals[id].is_empty() => return true,
316 /// Returns the string argument supplied to one of several matching options or `None`.
317 pub fn opts_str(&self, names: &[String]) -> Option<String> {
318 for nm in names.iter() {
319 match self.opt_val(nm.as_slice()) {
320 Some(Val(ref s)) => return Some(s.clone()),
327 /// Returns a vector of the arguments provided to all matches of the given
330 /// Used when an option accepts multiple values.
331 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
332 let mut acc: Vec<String> = Vec::new();
333 let r = self.opt_vals(nm);
336 Val(ref s) => acc.push((*s).clone()),
343 /// Returns the string argument supplied to a matching option or `None`.
344 pub fn opt_str(&self, nm: &str) -> Option<String> {
345 let vals = self.opt_vals(nm);
347 return None::<String>;
350 Val(ref s) => Some((*s).clone()),
356 /// Returns the matching string, a default, or none.
358 /// Returns none if the option was not present, `def` if the option was
359 /// present but no argument was provided, and the argument if the option was
360 /// present and an argument was provided.
361 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
362 let vals = self.opt_vals(nm);
367 Val(ref s) => Some((*s).clone()),
368 _ => Some(def.to_string())
374 fn is_arg(arg: &str) -> bool {
375 arg.len() > 1 && arg.as_bytes()[0] == '-' as u8
378 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
379 // Search main options.
380 let pos = opts.iter().position(|opt| opt.name == nm);
385 // Search in aliases.
386 for candidate in opts.iter() {
387 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
388 return opts.iter().position(|opt| opt.name == candidate.name);
395 /// Create a long option that is required and takes an argument.
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.
410 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
411 let len = short_name.len();
412 assert!(len == 1 || len == 0);
414 short_name: short_name.to_string(),
415 long_name: long_name.to_string(),
416 hint: hint.to_string(),
417 desc: desc.to_string(),
423 /// Create a long option that is optional and does not take an argument.
424 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
425 let len = short_name.len();
426 assert!(len == 1 || len == 0);
428 short_name: short_name.to_string(),
429 long_name: long_name.to_string(),
430 hint: "".to_string(),
431 desc: desc.to_string(),
437 /// Create a long option that can occur more than once and does not
438 /// take an argument.
439 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
440 let len = short_name.len();
441 assert!(len == 1 || len == 0);
443 short_name: short_name.to_string(),
444 long_name: long_name.to_string(),
445 hint: "".to_string(),
446 desc: desc.to_string(),
452 /// Create a long option that is optional and takes an optional argument.
453 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &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: hint.to_string(),
460 desc: desc.to_string(),
466 /// Create a long option that is optional, takes an argument, and may occur
468 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
469 let len = short_name.len();
470 assert!(len == 1 || len == 0);
472 short_name: short_name.to_string(),
473 long_name: long_name.to_string(),
474 hint: hint.to_string(),
475 desc: desc.to_string(),
481 /// Create a generic option group, stating all parameters explicitly
482 pub fn opt(short_name: &str,
487 occur: Occur) -> OptGroup {
488 let len = short_name.len();
489 assert!(len == 1 || len == 0);
491 short_name: short_name.to_string(),
492 long_name: long_name.to_string(),
493 hint: hint.to_string(),
494 desc: desc.to_string(),
501 /// Convert a `Fail_` enum into an error string.
502 #[deprecated="use `Show` (`{}` format specifier)"]
503 pub fn to_err_msg(self) -> String {
508 impl fmt::Show for Fail_ {
509 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
511 ArgumentMissing(ref nm) => {
512 write!(f, "Argument to option '{}' missing.", *nm)
514 UnrecognizedOption(ref nm) => {
515 write!(f, "Unrecognized option: '{}'.", *nm)
517 OptionMissing(ref nm) => {
518 write!(f, "Required option '{}' missing.", *nm)
520 OptionDuplicated(ref nm) => {
521 write!(f, "Option '{}' given more than once.", *nm)
523 UnexpectedArgument(ref nm) => {
524 write!(f, "Option '{}' does not take an argument.", *nm)
530 /// Parse command line arguments according to the provided options.
532 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
533 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on
534 /// failure: use the `Show` implementation of `Fail_` to display
535 /// information about it.
536 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
537 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
538 let n_opts = opts.len();
540 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
542 let mut vals = Vec::from_fn(n_opts, f);
543 let mut free: Vec<String> = Vec::new();
547 let cur = args[i].clone();
548 let curlen = cur.len();
549 if !is_arg(cur.as_slice()) {
551 } else if cur.as_slice() == "--" {
553 while j < l { free.push(args[j].clone()); j += 1; }
557 let mut i_arg = None;
558 if cur.as_bytes()[1] == '-' as u8 {
559 let tail = cur.as_slice().slice(2, curlen);
560 let tail_eq: Vec<&str> = tail.split('=').collect();
561 if tail_eq.len() <= 1 {
562 names = vec!(Long(tail.to_string()));
565 vec!(Long(tail_eq[0].to_string()));
566 i_arg = Some(tail_eq[1].to_string());
570 let mut last_valid_opt_id = None;
573 let range = cur.as_slice().char_range_at(j);
574 let opt = Short(range.ch);
576 /* In a series of potential options (eg. -aheJ), if we
577 see one which takes an argument, we assume all
578 subsequent characters make up the argument. This
579 allows options such as -L/usr/local/lib/foo to be
580 interpreted correctly
583 match find_opt(opts.as_slice(), opt.clone()) {
584 Some(id) => last_valid_opt_id = Some(id),
587 last_valid_opt_id.is_some() &&
588 match opts[last_valid_opt_id.unwrap()]
594 if arg_follows && j < curlen {
595 i_arg = Some(cur.as_slice()
596 .slice(j, curlen).to_string());
599 last_valid_opt_id = None;
607 let mut name_pos = 0;
608 for nm in names.iter() {
610 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
612 None => return Err(UnrecognizedOption(nm.to_string()))
614 match opts[optid].hasarg {
616 if !i_arg.is_none() {
617 return Err(UnexpectedArgument(nm.to_string()));
619 vals.get_mut(optid).push(Given);
622 if !i_arg.is_none() {
624 .push(Val((i_arg.clone())
626 } else if name_pos < names.len() || i + 1 == l ||
627 is_arg(args[i + 1].as_slice()) {
628 vals.get_mut(optid).push(Given);
631 vals.get_mut(optid).push(Val(args[i].clone()));
635 if !i_arg.is_none() {
636 vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
637 } else if i + 1 == l {
638 return Err(ArgumentMissing(nm.to_string()));
641 vals.get_mut(optid).push(Val(args[i].clone()));
651 let n = vals[i].len();
652 let occ = opts[i].occur;
655 return Err(OptionMissing(opts[i].name.to_string()));
660 return Err(OptionDuplicated(opts[i].name.to_string()));
672 /// Derive a usage message from a set of long options.
673 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
675 let desc_sep = format!("\n{}", " ".repeat(24));
677 let mut rows = opts.iter().map(|optref| {
678 let OptGroup{short_name: short_name,
679 long_name: long_name,
683 ..} = (*optref).clone();
685 let mut row = " ".repeat(4);
688 match short_name.len() {
692 row.push_str(short_name.as_slice());
695 _ => fail!("the short name should only be 1 ascii char long"),
699 match long_name.len() {
703 row.push_str(long_name.as_slice());
711 Yes => row.push_str(hint.as_slice()),
714 row.push_str(hint.as_slice());
719 // FIXME: #5516 should be graphemes not codepoints
720 // here we just need to indent the start of the description
721 let rowlen = row.as_slice().char_len();
723 for _ in range(0, 24 - rowlen) {
727 row.push_str(desc_sep.as_slice())
730 // Normalize desc to contain words separated by one space character
731 let mut desc_normalized_whitespace = String::new();
732 for word in desc.as_slice().words() {
733 desc_normalized_whitespace.push_str(word);
734 desc_normalized_whitespace.push_char(' ');
737 // FIXME: #5516 should be graphemes not codepoints
738 let mut desc_rows = Vec::new();
739 each_split_within(desc_normalized_whitespace.as_slice(),
742 desc_rows.push(substr.to_string());
746 // FIXME: #5516 should be graphemes not codepoints
747 // wrapped description
748 row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
753 format!("{}\n\nOptions:\n{}\n", brief,
754 rows.collect::<Vec<String>>().connect("\n"))
757 fn format_option(opt: &OptGroup) -> String {
758 let mut line = String::new();
760 if opt.occur != Req {
764 // Use short_name is possible, but fallback to long_name.
765 if opt.short_name.len() > 0 {
767 line.push_str(opt.short_name.as_slice());
770 line.push_str(opt.long_name.as_slice());
773 if opt.hasarg != No {
775 if opt.hasarg == Maybe {
778 line.push_str(opt.hint.as_slice());
779 if opt.hasarg == Maybe {
784 if opt.occur != Req {
787 if opt.occur == Multi {
794 /// Derive a short one-line usage summary from a set of long options.
795 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
796 let mut line = format!("Usage: {} ", program_name);
797 line.push_str(opts.iter()
799 .collect::<Vec<String>>()
806 /// Splits a string into substrings with possibly internal whitespace,
807 /// each of them at most `lim` bytes long. The substrings have leading and trailing
808 /// whitespace removed, and are only cut at whitespace boundaries.
810 /// Note: Function was moved here from `std::str` because this module is the only place that
811 /// uses it, and because it was to specific for a general string function.
815 /// Fails during iteration if the string contains a non-whitespace
816 /// sequence longer than the limit.
817 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
819 // Just for fun, let's write this as a state machine:
821 enum SplitWithinState {
822 A, // leading whitespace, initial state
824 C, // internal and trailing whitespace
827 Ws, // current char is whitespace
828 Cr // current char is not whitespace
831 UnderLim, // current char makes current substring still fit in limit
832 OverLim // current char makes current substring no longer fit in limit
835 let mut slice_start = 0;
836 let mut last_start = 0;
837 let mut last_end = 0;
839 let mut fake_i = ss.len();
844 // if the limit is larger than the string, lower it to save cycles
849 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
850 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
851 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
853 state = match (state, whitespace, limit) {
855 (A, Cr, _) => { slice_start = i; last_start = i; B }
857 (B, Cr, UnderLim) => { B }
858 (B, Cr, OverLim) if (i - last_start + 1) > lim
859 => fail!("word starting with {} longer than limit!",
860 ss.slice(last_start, i + 1)),
861 (B, Cr, OverLim) => {
862 *cont = it(ss.slice(slice_start, last_end));
863 slice_start = last_start;
866 (B, Ws, UnderLim) => {
870 (B, Ws, OverLim) => {
872 *cont = it(ss.slice(slice_start, last_end));
876 (C, Cr, UnderLim) => {
880 (C, Cr, OverLim) => {
881 *cont = it(ss.slice(slice_start, last_end));
887 (C, Ws, OverLim) => {
888 *cont = it(ss.slice(slice_start, last_end));
891 (C, Ws, UnderLim) => {
899 ss.char_indices().all(|x| machine(&mut cont, x));
901 // Let the automaton 'run out' by supplying trailing whitespace
902 while cont && match state { B | C => true, A => false } {
903 machine(&mut cont, (fake_i, ' '));
910 fn test_split_within() {
911 fn t(s: &str, i: uint, u: &[String]) {
912 let mut v = Vec::new();
913 each_split_within(s, i, |s| { v.push(s.to_string()); true });
914 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
918 t("hello", 15, ["hello".to_string()]);
919 t("\nMary had a little lamb\nLittle lamb\n", 15, [
920 "Mary had a".to_string(),
921 "little lamb".to_string(),
922 "Little lamb".to_string()
924 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
925 ["Mary had a little lamb\nLittle lamb".to_string()]);
932 use std::result::{Err, Ok};
935 fn check_fail_type(f: Fail_, ft: FailType) {
937 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
938 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
939 OptionMissing(_) => assert!(ft == OptionMissing_),
940 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
941 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
948 let long_args = vec!("--test=20".to_string());
949 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
950 let rs = getopts(long_args.as_slice(), opts.as_slice());
953 assert!(m.opt_present("test"));
954 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
955 assert!(m.opt_present("t"));
956 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
958 _ => { fail!("test_reqopt failed (long arg)"); }
960 let short_args = vec!("-t".to_string(), "20".to_string());
961 match getopts(short_args.as_slice(), opts.as_slice()) {
963 assert!((m.opt_present("test")));
964 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
965 assert!((m.opt_present("t")));
966 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
968 _ => { fail!("test_reqopt failed (short arg)"); }
973 fn test_reqopt_missing() {
974 let args = vec!("blah".to_string());
975 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
976 let rs = getopts(args.as_slice(), opts.as_slice());
978 Err(f) => check_fail_type(f, OptionMissing_),
984 fn test_reqopt_no_arg() {
985 let long_args = vec!("--test".to_string());
986 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
987 let rs = getopts(long_args.as_slice(), opts.as_slice());
989 Err(f) => check_fail_type(f, ArgumentMissing_),
992 let short_args = vec!("-t".to_string());
993 match getopts(short_args.as_slice(), opts.as_slice()) {
994 Err(f) => check_fail_type(f, ArgumentMissing_),
1000 fn test_reqopt_multi() {
1001 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1002 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1003 let rs = getopts(args.as_slice(), opts.as_slice());
1005 Err(f) => check_fail_type(f, OptionDuplicated_),
1013 let long_args = vec!("--test=20".to_string());
1014 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1015 let rs = getopts(long_args.as_slice(), opts.as_slice());
1018 assert!(m.opt_present("test"));
1019 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1020 assert!((m.opt_present("t")));
1021 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1025 let short_args = vec!("-t".to_string(), "20".to_string());
1026 match getopts(short_args.as_slice(), opts.as_slice()) {
1028 assert!((m.opt_present("test")));
1029 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1030 assert!((m.opt_present("t")));
1031 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1038 fn test_optopt_missing() {
1039 let args = vec!("blah".to_string());
1040 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1041 let rs = getopts(args.as_slice(), opts.as_slice());
1044 assert!(!m.opt_present("test"));
1045 assert!(!m.opt_present("t"));
1052 fn test_optopt_no_arg() {
1053 let long_args = vec!("--test".to_string());
1054 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1055 let rs = getopts(long_args.as_slice(), opts.as_slice());
1057 Err(f) => check_fail_type(f, ArgumentMissing_),
1060 let short_args = vec!("-t".to_string());
1061 match getopts(short_args.as_slice(), opts.as_slice()) {
1062 Err(f) => check_fail_type(f, ArgumentMissing_),
1068 fn test_optopt_multi() {
1069 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1070 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1071 let rs = getopts(args.as_slice(), opts.as_slice());
1073 Err(f) => check_fail_type(f, OptionDuplicated_),
1078 // Tests for optflag
1081 let long_args = vec!("--test".to_string());
1082 let opts = vec!(optflag("t", "test", "testing"));
1083 let rs = getopts(long_args.as_slice(), opts.as_slice());
1086 assert!(m.opt_present("test"));
1087 assert!(m.opt_present("t"));
1091 let short_args = vec!("-t".to_string());
1092 match getopts(short_args.as_slice(), opts.as_slice()) {
1094 assert!(m.opt_present("test"));
1095 assert!(m.opt_present("t"));
1102 fn test_optflag_missing() {
1103 let args = vec!("blah".to_string());
1104 let opts = vec!(optflag("t", "test", "testing"));
1105 let rs = getopts(args.as_slice(), opts.as_slice());
1108 assert!(!m.opt_present("test"));
1109 assert!(!m.opt_present("t"));
1116 fn test_optflag_long_arg() {
1117 let args = vec!("--test=20".to_string());
1118 let opts = vec!(optflag("t", "test", "testing"));
1119 let rs = getopts(args.as_slice(), opts.as_slice());
1122 check_fail_type(f, UnexpectedArgument_);
1129 fn test_optflag_multi() {
1130 let args = vec!("--test".to_string(), "-t".to_string());
1131 let opts = vec!(optflag("t", "test", "testing"));
1132 let rs = getopts(args.as_slice(), opts.as_slice());
1134 Err(f) => check_fail_type(f, OptionDuplicated_),
1140 fn test_optflag_short_arg() {
1141 let args = vec!("-t".to_string(), "20".to_string());
1142 let opts = vec!(optflag("t", "test", "testing"));
1143 let rs = getopts(args.as_slice(), opts.as_slice());
1146 // The next variable after the flag is just a free argument
1148 assert!(*m.free.get(0) == "20".to_string());
1154 // Tests for optflagmulti
1156 fn test_optflagmulti_short1() {
1157 let args = vec!("-v".to_string());
1158 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1159 let rs = getopts(args.as_slice(), opts.as_slice());
1162 assert_eq!(m.opt_count("v"), 1);
1169 fn test_optflagmulti_short2a() {
1170 let args = vec!("-v".to_string(), "-v".to_string());
1171 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1172 let rs = getopts(args.as_slice(), opts.as_slice());
1175 assert_eq!(m.opt_count("v"), 2);
1182 fn test_optflagmulti_short2b() {
1183 let args = vec!("-vv".to_string());
1184 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1185 let rs = getopts(args.as_slice(), opts.as_slice());
1188 assert_eq!(m.opt_count("v"), 2);
1195 fn test_optflagmulti_long1() {
1196 let args = vec!("--verbose".to_string());
1197 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1198 let rs = getopts(args.as_slice(), opts.as_slice());
1201 assert_eq!(m.opt_count("verbose"), 1);
1208 fn test_optflagmulti_long2() {
1209 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1210 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1211 let rs = getopts(args.as_slice(), opts.as_slice());
1214 assert_eq!(m.opt_count("verbose"), 2);
1221 fn test_optflagmulti_mix() {
1222 let args = vec!("--verbose".to_string(), "-v".to_string(),
1223 "-vv".to_string(), "verbose".to_string());
1224 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1225 let rs = getopts(args.as_slice(), opts.as_slice());
1228 assert_eq!(m.opt_count("verbose"), 4);
1229 assert_eq!(m.opt_count("v"), 4);
1235 // Tests for optmulti
1237 fn test_optmulti() {
1238 let long_args = vec!("--test=20".to_string());
1239 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1240 let rs = getopts(long_args.as_slice(), opts.as_slice());
1243 assert!((m.opt_present("test")));
1244 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1245 assert!((m.opt_present("t")));
1246 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1250 let short_args = vec!("-t".to_string(), "20".to_string());
1251 match getopts(short_args.as_slice(), opts.as_slice()) {
1253 assert!((m.opt_present("test")));
1254 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1255 assert!((m.opt_present("t")));
1256 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1263 fn test_optmulti_missing() {
1264 let args = vec!("blah".to_string());
1265 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1266 let rs = getopts(args.as_slice(), opts.as_slice());
1269 assert!(!m.opt_present("test"));
1270 assert!(!m.opt_present("t"));
1277 fn test_optmulti_no_arg() {
1278 let long_args = vec!("--test".to_string());
1279 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1280 let rs = getopts(long_args.as_slice(), opts.as_slice());
1282 Err(f) => check_fail_type(f, ArgumentMissing_),
1285 let short_args = vec!("-t".to_string());
1286 match getopts(short_args.as_slice(), opts.as_slice()) {
1287 Err(f) => check_fail_type(f, ArgumentMissing_),
1293 fn test_optmulti_multi() {
1294 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1295 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1296 let rs = getopts(args.as_slice(), opts.as_slice());
1299 assert!(m.opt_present("test"));
1300 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1301 assert!(m.opt_present("t"));
1302 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1303 let pair = m.opt_strs("test");
1304 assert!(*pair.get(0) == "20".to_string());
1305 assert!(*pair.get(1) == "30".to_string());
1312 fn test_unrecognized_option() {
1313 let long_args = vec!("--untest".to_string());
1314 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1315 let rs = getopts(long_args.as_slice(), opts.as_slice());
1317 Err(f) => check_fail_type(f, UnrecognizedOption_),
1320 let short_args = vec!("-u".to_string());
1321 match getopts(short_args.as_slice(), opts.as_slice()) {
1322 Err(f) => check_fail_type(f, UnrecognizedOption_),
1328 fn test_combined() {
1330 vec!("prog".to_string(),
1331 "free1".to_string(),
1334 "free2".to_string(),
1335 "--flag".to_string(),
1336 "--long=30".to_string(),
1345 "-60 70".to_string());
1347 vec!(optopt("s", "something", "something", "SOMETHING"),
1348 optflag("", "flag", "a flag"),
1349 reqopt("", "long", "hi", "LONG"),
1350 optflag("f", "", "another flag"),
1351 optmulti("m", "", "mmmmmm", "YUM"),
1352 optmulti("n", "", "nothing", "NOTHING"),
1353 optopt("", "notpresent", "nothing to see here", "NOPE"));
1354 let rs = getopts(args.as_slice(), opts.as_slice());
1357 assert!(*m.free.get(0) == "prog".to_string());
1358 assert!(*m.free.get(1) == "free1".to_string());
1359 assert_eq!(m.opt_str("s").unwrap(), "20".to_string());
1360 assert!(*m.free.get(2) == "free2".to_string());
1361 assert!((m.opt_present("flag")));
1362 assert_eq!(m.opt_str("long").unwrap(), "30".to_string());
1363 assert!((m.opt_present("f")));
1364 let pair = m.opt_strs("m");
1365 assert!(*pair.get(0) == "40".to_string());
1366 assert!(*pair.get(1) == "50".to_string());
1367 let pair = m.opt_strs("n");
1368 assert!(*pair.get(0) == "-A B".to_string());
1369 assert!(*pair.get(1) == "-60 70".to_string());
1370 assert!((!m.opt_present("notpresent")));
1378 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1379 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1380 optopt("f", "", "flag", "FLAG"));
1382 let args_single = vec!("-e".to_string(), "foo".to_string());
1383 let matches_single = &match getopts(args_single.as_slice(),
1386 result::Err(_) => fail!()
1388 assert!(matches_single.opts_present(["e".to_string()]));
1389 assert!(matches_single.opts_present(["encrypt".to_string(), "e".to_string()]));
1390 assert!(matches_single.opts_present(["e".to_string(), "encrypt".to_string()]));
1391 assert!(!matches_single.opts_present(["encrypt".to_string()]));
1392 assert!(!matches_single.opts_present(["thing".to_string()]));
1393 assert!(!matches_single.opts_present([]));
1395 assert_eq!(matches_single.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1396 assert_eq!(matches_single.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1398 assert_eq!(matches_single.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1401 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1403 let matches_both = &match getopts(args_both.as_slice(),
1406 result::Err(_) => fail!()
1408 assert!(matches_both.opts_present(["e".to_string()]));
1409 assert!(matches_both.opts_present(["encrypt".to_string()]));
1410 assert!(matches_both.opts_present(["encrypt".to_string(), "e".to_string()]));
1411 assert!(matches_both.opts_present(["e".to_string(), "encrypt".to_string()]));
1412 assert!(!matches_both.opts_present(["f".to_string()]));
1413 assert!(!matches_both.opts_present(["thing".to_string()]));
1414 assert!(!matches_both.opts_present([]));
1416 assert_eq!(matches_both.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1417 assert_eq!(matches_both.opts_str(["encrypt".to_string()]).unwrap(), "foo".to_string());
1418 assert_eq!(matches_both.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1420 assert_eq!(matches_both.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1426 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1427 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1428 optmulti("M", "", "something", "MMMM"));
1429 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1431 result::Err(_) => fail!()
1433 assert!(matches.opts_present(["L".to_string()]));
1434 assert_eq!(matches.opts_str(["L".to_string()]).unwrap(), "foo".to_string());
1435 assert!(matches.opts_present(["M".to_string()]));
1436 assert_eq!(matches.opts_str(["M".to_string()]).unwrap(), ".".to_string());
1441 fn test_long_to_short() {
1442 let mut short = Opt {
1443 name: Long("banana".to_string()),
1446 aliases: Vec::new(),
1448 short.aliases = vec!(Opt { name: Short('b'),
1451 aliases: Vec::new() });
1452 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1454 assert!(verbose.long_to_short() == short);
1458 fn test_aliases_long_and_short() {
1460 optflagmulti("a", "apple", "Desc"));
1462 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1464 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1465 assert_eq!(3, matches.opt_count("a"));
1466 assert_eq!(3, matches.opt_count("apple"));
1471 let optgroups = vec!(
1472 reqopt("b", "banana", "Desc", "VAL"),
1473 optopt("a", "012345678901234567890123456789",
1475 optflag("k", "kiwi", "Desc"),
1476 optflagopt("p", "", "Desc", "VAL"),
1477 optmulti("l", "", "Desc", "VAL"));
1483 -b --banana VAL Desc
1484 -a --012345678901234567890123456789 VAL
1491 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1493 debug!("expected: <<{}>>", expected);
1494 debug!("generated: <<{}>>", generated_usage);
1495 assert_eq!(generated_usage, expected);
1499 fn test_usage_description_wrapping() {
1500 // indentation should be 24 spaces
1501 // lines wrap after 78: or rather descriptions wrap after 54
1503 let optgroups = vec!(
1504 optflag("k", "kiwi",
1505 "This is a long description which won't be wrapped..+.."), // 54
1506 optflag("a", "apple",
1507 "This is a long description which _will_ be wrapped..+.."));
1513 -k --kiwi This is a long description which won't be wrapped..+..
1514 -a --apple This is a long description which _will_ be
1518 let usage = usage("Usage: fruits", optgroups.as_slice());
1520 debug!("expected: <<{}>>", expected);
1521 debug!("generated: <<{}>>", usage);
1522 assert!(usage == expected)
1526 fn test_usage_description_multibyte_handling() {
1527 let optgroups = vec!(
1528 optflag("k", "k\u2013w\u2013",
1529 "The word kiwi is normally spelled with two i's"),
1530 optflag("a", "apple",
1531 "This \u201Cdescription\u201D has some characters that could \
1532 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1538 -k --k–w– The word kiwi is normally spelled with two i's
1539 -a --apple This “description” has some characters that could
1540 confuse the line wrapping; an apple costs 0.51€ in
1541 some parts of Europe.
1544 let usage = usage("Usage: fruits", optgroups.as_slice());
1546 debug!("expected: <<{}>>", expected);
1547 debug!("generated: <<{}>>", usage);
1548 assert!(usage == expected)
1552 fn test_short_usage() {
1553 let optgroups = vec!(
1554 reqopt("b", "banana", "Desc", "VAL"),
1555 optopt("a", "012345678901234567890123456789",
1557 optflag("k", "kiwi", "Desc"),
1558 optflagopt("p", "", "Desc", "VAL"),
1559 optmulti("l", "", "Desc", "VAL"));
1561 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1562 let generated_usage = short_usage("fruits", optgroups.as_slice());
1564 debug!("expected: <<{}>>", expected);
1565 debug!("generated: <<{}>>", generated_usage);
1566 assert_eq!(generated_usage, expected);