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<~str>) {
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 = os::args();
54 //! let program = args[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_err_msg()) }
64 //! if matches.opt_present("h") {
65 //! print_usage(program, opts);
68 //! let output = matches.opt_str("o");
69 //! let input: &str = if !matches.free.is_empty() {
70 //! (*matches.free.get(0)).clone()
72 //! print_usage(program, opts);
75 //! do_work(input, output);
79 #![crate_id = "getopts#0.11-pre"]
80 #![crate_type = "rlib"]
81 #![crate_type = "dylib"]
82 #![license = "MIT/ASL2"]
83 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
84 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
85 html_root_url = "http://static.rust-lang.org/doc/master")]
86 #![feature(globs, phase)]
88 #![deny(deprecated_owned_vector)]
90 #[cfg(test)] #[phase(syntax, link)] extern crate log;
93 use std::result::{Err, Ok};
95 use std::strbuf::StrBuf;
97 /// Name of an option. Either a string or a single char.
98 #[deriving(Clone, Eq)]
100 /// A string representing the long name of an option.
101 /// For example: "help"
103 /// A char representing the short name of an option.
108 /// Describes whether an option has an argument.
109 #[deriving(Clone, Eq)]
111 /// The option requires an argument.
113 /// The option is just a flag, therefore no argument.
115 /// The option argument is optional and it could or not exist.
119 /// Describes how often an option may occur.
120 #[deriving(Clone, Eq)]
122 /// The option occurs once.
124 /// The option could or not occur.
126 /// The option occurs once or multiple times.
130 /// A description of a possible option.
131 #[deriving(Clone, Eq)]
133 /// Name of the option
135 /// Whether it has an argument
137 /// How often it can occur
139 /// Which options it aliases
140 pub aliases: Vec<Opt> ,
143 /// One group of options, e.g., both -h and --help, along with
144 /// their shared description and properties.
145 #[deriving(Clone, Eq)]
146 pub struct OptGroup {
147 /// Short Name of the `OptGroup`
148 pub short_name: ~str,
149 /// Long Name of the `OptGroup`
155 /// Whether it has an argument
157 /// How often it can occur
161 /// Describes wether an option is given at all or has a value.
162 #[deriving(Clone, Eq)]
168 /// The result of checking command line arguments. Contains a vector
169 /// of matches and a vector of free strings.
170 #[deriving(Clone, Eq)]
172 /// Options that matched
174 /// Values of the Options that matched
175 vals: Vec<Vec<Optval> > ,
176 /// Free string fragments
180 /// The type returned when the command line does not conform to the
181 /// expected format. Call the `to_err_msg` method to retrieve the
182 /// error as a string.
183 #[deriving(Clone, Eq, Show)]
185 /// The option requires an argument but none was passed.
186 ArgumentMissing(~str),
187 /// The passed option is not declared among the possible options.
188 UnrecognizedOption(~str),
189 /// A required option is not present.
191 /// A single occurence option is being used multiple times.
192 OptionDuplicated(~str),
193 /// There's an argument being passed to a non-argument option.
194 UnexpectedArgument(~str),
197 /// The type of failure that occurred.
199 #[allow(missing_doc)]
208 /// The result of parsing a command line with a set of options.
209 pub type Result = result::Result<Matches, Fail_>;
212 fn from_str(nm: &str) -> Name {
214 Short(nm.char_at(0u))
220 fn to_str(&self) -> ~str {
222 Short(ch) => ch.to_str(),
223 Long(ref s) => s.to_owned()
229 /// Translate OptGroup into Opt.
230 /// (Both short and long names correspond to different Opts).
231 pub fn long_to_short(&self) -> Opt {
233 short_name: short_name,
234 long_name: long_name,
240 match (short_name.len(), long_name.len()) {
241 (0,0) => fail!("this long-format option was given no name"),
243 name: Long((long_name)),
249 name: Short(short_name.char_at(0)),
255 name: Long((long_name)),
260 name: Short(short_name.char_at(0)),
267 (_,_) => fail!("something is wrong with the long-form opt")
273 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
274 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
275 Some(id) => (*self.vals.get(id)).clone(),
276 None => fail!("No option '{}' defined", nm)
280 fn opt_val(&self, nm: &str) -> Option<Optval> {
281 let vals = self.opt_vals(nm);
285 Some((*vals.get(0)).clone())
289 /// Returns true if an option was matched.
290 pub fn opt_present(&self, nm: &str) -> bool {
291 !self.opt_vals(nm).is_empty()
294 /// Returns the number of times an option was matched.
295 pub fn opt_count(&self, nm: &str) -> uint {
296 self.opt_vals(nm).len()
299 /// Returns true if any of several options were matched.
300 pub fn opts_present(&self, names: &[~str]) -> bool {
301 for nm in names.iter() {
302 match find_opt(self.opts.as_slice(), Name::from_str(*nm)) {
303 Some(id) if !self.vals.get(id).is_empty() => return true,
310 /// Returns the string argument supplied to one of several matching options or `None`.
311 pub fn opts_str(&self, names: &[~str]) -> Option<~str> {
312 for nm in names.iter() {
313 match self.opt_val(*nm) {
314 Some(Val(ref s)) => return Some(s.clone()),
321 /// Returns a vector of the arguments provided to all matches of the given
324 /// Used when an option accepts multiple values.
325 pub fn opt_strs(&self, nm: &str) -> Vec<~str> {
326 let mut acc: Vec<~str> = Vec::new();
327 let r = self.opt_vals(nm);
330 Val(ref s) => acc.push((*s).clone()),
337 /// Returns the string argument supplied to a matching option or `None`.
338 pub fn opt_str(&self, nm: &str) -> Option<~str> {
339 let vals = self.opt_vals(nm);
344 &Val(ref s) => Some((*s).clone()),
350 /// Returns the matching string, a default, or none.
352 /// Returns none if the option was not present, `def` if the option was
353 /// present but no argument was provided, and the argument if the option was
354 /// present and an argument was provided.
355 pub fn opt_default(&self, nm: &str, def: &str) -> Option<~str> {
356 let vals = self.opt_vals(nm);
357 if vals.is_empty() { return None; }
359 &Val(ref s) => Some((*s).clone()),
360 _ => Some(def.to_owned())
366 fn is_arg(arg: &str) -> bool {
367 arg.len() > 1 && arg[0] == '-' as u8
370 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
371 // Search main options.
372 let pos = opts.iter().position(|opt| opt.name == nm);
377 // Search in aliases.
378 for candidate in opts.iter() {
379 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
380 return opts.iter().position(|opt| opt.name == candidate.name);
387 /// Create a long option that is required and takes an argument.
388 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
389 let len = short_name.len();
390 assert!(len == 1 || len == 0);
392 short_name: short_name.to_owned(),
393 long_name: long_name.to_owned(),
394 hint: hint.to_owned(),
395 desc: desc.to_owned(),
401 /// Create a long option that is optional and takes an argument.
402 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
403 let len = short_name.len();
404 assert!(len == 1 || len == 0);
406 short_name: short_name.to_owned(),
407 long_name: long_name.to_owned(),
408 hint: hint.to_owned(),
409 desc: desc.to_owned(),
415 /// Create a long option that is optional and does not take an argument.
416 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
417 let len = short_name.len();
418 assert!(len == 1 || len == 0);
420 short_name: short_name.to_owned(),
421 long_name: long_name.to_owned(),
423 desc: desc.to_owned(),
429 /// Create a long option that can occur more than once and does not
430 /// take an argument.
431 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
432 let len = short_name.len();
433 assert!(len == 1 || len == 0);
435 short_name: short_name.to_owned(),
436 long_name: long_name.to_owned(),
438 desc: desc.to_owned(),
444 /// Create a long option that is optional and takes an optional argument.
445 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
446 let len = short_name.len();
447 assert!(len == 1 || len == 0);
449 short_name: short_name.to_owned(),
450 long_name: long_name.to_owned(),
451 hint: hint.to_owned(),
452 desc: desc.to_owned(),
458 /// Create a long option that is optional, takes an argument, and may occur
460 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
461 let len = short_name.len();
462 assert!(len == 1 || len == 0);
464 short_name: short_name.to_owned(),
465 long_name: long_name.to_owned(),
466 hint: hint.to_owned(),
467 desc: desc.to_owned(),
473 /// Create a generic option group, stating all parameters explicitly
474 pub fn opt(short_name: &str,
479 occur: Occur) -> OptGroup {
480 let len = short_name.len();
481 assert!(len == 1 || len == 0);
483 short_name: short_name.to_owned(),
484 long_name: long_name.to_owned(),
485 hint: hint.to_owned(),
486 desc: desc.to_owned(),
493 /// Convert a `Fail_` enum into an error string.
494 pub fn to_err_msg(self) -> ~str {
496 ArgumentMissing(ref nm) => {
497 format!("Argument to option '{}' missing.", *nm)
499 UnrecognizedOption(ref nm) => {
500 format!("Unrecognized option: '{}'.", *nm)
502 OptionMissing(ref nm) => {
503 format!("Required option '{}' missing.", *nm)
505 OptionDuplicated(ref nm) => {
506 format!("Option '{}' given more than once.", *nm)
508 UnexpectedArgument(ref nm) => {
509 format!("Option '{}' does not take an argument.", *nm)
515 /// Parse command line arguments according to the provided options.
517 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
518 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on failure.
519 /// Use `to_err_msg` to get an error message.
520 pub fn getopts(args: &[~str], optgrps: &[OptGroup]) -> Result {
521 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
522 let n_opts = opts.len();
524 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
526 let mut vals = Vec::from_fn(n_opts, f);
527 let mut free: Vec<~str> = Vec::new();
531 let cur = args[i].clone();
532 let curlen = cur.len();
535 } else if cur == "--".to_owned() {
537 while j < l { free.push(args[j].clone()); j += 1; }
541 let mut i_arg = None;
542 if cur[1] == '-' as u8 {
543 let tail = cur.slice(2, curlen);
544 let tail_eq: Vec<&str> = tail.split('=').collect();
545 if tail_eq.len() <= 1 {
546 names = vec!(Long(tail.to_owned()));
549 vec!(Long((*tail_eq.get(0)).to_owned()));
550 i_arg = Some((*tail_eq.get(1)).to_owned());
554 let mut last_valid_opt_id = None;
557 let range = cur.char_range_at(j);
558 let opt = Short(range.ch);
560 /* In a series of potential options (eg. -aheJ), if we
561 see one which takes an argument, we assume all
562 subsequent characters make up the argument. This
563 allows options such as -L/usr/local/lib/foo to be
564 interpreted correctly
567 match find_opt(opts.as_slice(), opt.clone()) {
568 Some(id) => last_valid_opt_id = Some(id),
571 last_valid_opt_id.is_some() &&
572 match opts.get(last_valid_opt_id.unwrap())
578 if arg_follows && j < curlen {
579 i_arg = Some(cur.slice(j, curlen).to_owned());
582 last_valid_opt_id = None;
590 let mut name_pos = 0;
591 for nm in names.iter() {
593 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
595 None => return Err(UnrecognizedOption(nm.to_str()))
597 match opts.get(optid).hasarg {
599 if !i_arg.is_none() {
600 return Err(UnexpectedArgument(nm.to_str()));
602 vals.get_mut(optid).push(Given);
605 if !i_arg.is_none() {
607 .push(Val((i_arg.clone())
609 } else if name_pos < names.len() ||
610 i + 1 == l || is_arg(args[i + 1]) {
611 vals.get_mut(optid).push(Given);
614 vals.get_mut(optid).push(Val(args[i].clone()));
618 if !i_arg.is_none() {
619 vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
620 } else if i + 1 == l {
621 return Err(ArgumentMissing(nm.to_str()));
624 vals.get_mut(optid).push(Val(args[i].clone()));
634 let n = vals.get(i).len();
635 let occ = opts.get(i).occur;
638 return Err(OptionMissing(opts.get(i).name.to_str()));
643 return Err(OptionDuplicated(opts.get(i).name.to_str()));
655 /// Derive a usage message from a set of long options.
656 pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {
658 let desc_sep = "\n" + " ".repeat(24);
660 let mut rows = opts.iter().map(|optref| {
661 let OptGroup{short_name: short_name,
662 long_name: long_name,
666 ..} = (*optref).clone();
668 let mut row = StrBuf::from_owned_str(" ".repeat(4));
671 match short_name.len() {
675 row.push_str(short_name);
678 _ => fail!("the short name should only be 1 ascii char long"),
682 match long_name.len() {
686 row.push_str(long_name);
694 Yes => row.push_str(hint),
702 // FIXME: #5516 should be graphemes not codepoints
703 // here we just need to indent the start of the description
704 let rowlen = row.as_slice().char_len();
706 for _ in range(0, 24 - rowlen) {
710 row.push_str(desc_sep)
713 // Normalize desc to contain words separated by one space character
714 let mut desc_normalized_whitespace = StrBuf::new();
715 for word in desc.words() {
716 desc_normalized_whitespace.push_str(word);
717 desc_normalized_whitespace.push_char(' ');
720 // FIXME: #5516 should be graphemes not codepoints
721 let mut desc_rows = Vec::new();
722 each_split_within(desc_normalized_whitespace.as_slice(),
725 desc_rows.push(substr.to_owned());
729 // FIXME: #5516 should be graphemes not codepoints
730 // wrapped description
731 row.push_str(desc_rows.connect(desc_sep));
736 format!("{}\n\nOptions:\n{}\n", brief, rows.collect::<Vec<~str> >().connect("\n"))
739 fn format_option(opt: &OptGroup) -> ~str {
740 let mut line = StrBuf::new();
742 if opt.occur != Req {
746 // Use short_name is possible, but fallback to long_name.
747 if opt.short_name.len() > 0 {
749 line.push_str(opt.short_name);
752 line.push_str(opt.long_name);
755 if opt.hasarg != No {
757 if opt.hasarg == Maybe {
760 line.push_str(opt.hint);
761 if opt.hasarg == Maybe {
766 if opt.occur != Req {
769 if opt.occur == Multi {
776 /// Derive a short one-line usage summary from a set of long options.
777 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> ~str {
778 let mut line = StrBuf::from_str("Usage: " + program_name + " ");
779 line.push_str(opts.iter().map(format_option).collect::<Vec<~str>>().connect(" "));
784 /// Splits a string into substrings with possibly internal whitespace,
785 /// each of them at most `lim` bytes long. The substrings have leading and trailing
786 /// whitespace removed, and are only cut at whitespace boundaries.
788 /// Note: Function was moved here from `std::str` because this module is the only place that
789 /// uses it, and because it was to specific for a general string function.
793 /// Fails during iteration if the string contains a non-whitespace
794 /// sequence longer than the limit.
795 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
797 // Just for fun, let's write this as a state machine:
799 enum SplitWithinState {
800 A, // leading whitespace, initial state
802 C, // internal and trailing whitespace
805 Ws, // current char is whitespace
806 Cr // current char is not whitespace
809 UnderLim, // current char makes current substring still fit in limit
810 OverLim // current char makes current substring no longer fit in limit
813 let mut slice_start = 0;
814 let mut last_start = 0;
815 let mut last_end = 0;
817 let mut fake_i = ss.len();
822 // if the limit is larger than the string, lower it to save cycles
827 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
828 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
829 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
831 state = match (state, whitespace, limit) {
833 (A, Cr, _) => { slice_start = i; last_start = i; B }
835 (B, Cr, UnderLim) => { B }
836 (B, Cr, OverLim) if (i - last_start + 1) > lim
837 => fail!("word starting with {} longer than limit!",
838 ss.slice(last_start, i + 1)),
839 (B, Cr, OverLim) => {
840 *cont = it(ss.slice(slice_start, last_end));
841 slice_start = last_start;
844 (B, Ws, UnderLim) => {
848 (B, Ws, OverLim) => {
850 *cont = it(ss.slice(slice_start, last_end));
854 (C, Cr, UnderLim) => {
858 (C, Cr, OverLim) => {
859 *cont = it(ss.slice(slice_start, last_end));
865 (C, Ws, OverLim) => {
866 *cont = it(ss.slice(slice_start, last_end));
869 (C, Ws, UnderLim) => {
877 ss.char_indices().advance(|x| machine(&mut cont, x));
879 // Let the automaton 'run out' by supplying trailing whitespace
880 while cont && match state { B | C => true, A => false } {
881 machine(&mut cont, (fake_i, ' '));
888 fn test_split_within() {
889 fn t(s: &str, i: uint, u: &[~str]) {
890 let mut v = Vec::new();
891 each_split_within(s, i, |s| { v.push(s.to_owned()); true });
892 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
896 t("hello", 15, ["hello".to_owned()]);
897 t("\nMary had a little lamb\nLittle lamb\n", 15,
898 ["Mary had a".to_owned(), "little lamb".to_owned(), "Little lamb".to_owned()]);
899 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
900 ["Mary had a little lamb\nLittle lamb".to_owned()]);
907 use std::result::{Err, Ok};
910 fn check_fail_type(f: Fail_, ft: FailType) {
912 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
913 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
914 OptionMissing(_) => assert!(ft == OptionMissing_),
915 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
916 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
923 let long_args = vec!("--test=20".to_owned());
924 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
925 let rs = getopts(long_args.as_slice(), opts.as_slice());
928 assert!(m.opt_present("test"));
929 assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
930 assert!(m.opt_present("t"));
931 assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
933 _ => { fail!("test_reqopt failed (long arg)"); }
935 let short_args = vec!("-t".to_owned(), "20".to_owned());
936 match getopts(short_args.as_slice(), opts.as_slice()) {
938 assert!((m.opt_present("test")));
939 assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
940 assert!((m.opt_present("t")));
941 assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
943 _ => { fail!("test_reqopt failed (short arg)"); }
948 fn test_reqopt_missing() {
949 let args = vec!("blah".to_owned());
950 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
951 let rs = getopts(args.as_slice(), opts.as_slice());
953 Err(f) => check_fail_type(f, OptionMissing_),
959 fn test_reqopt_no_arg() {
960 let long_args = vec!("--test".to_owned());
961 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
962 let rs = getopts(long_args.as_slice(), opts.as_slice());
964 Err(f) => check_fail_type(f, ArgumentMissing_),
967 let short_args = vec!("-t".to_owned());
968 match getopts(short_args.as_slice(), opts.as_slice()) {
969 Err(f) => check_fail_type(f, ArgumentMissing_),
975 fn test_reqopt_multi() {
976 let args = vec!("--test=20".to_owned(), "-t".to_owned(), "30".to_owned());
977 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
978 let rs = getopts(args.as_slice(), opts.as_slice());
980 Err(f) => check_fail_type(f, OptionDuplicated_),
988 let long_args = vec!("--test=20".to_owned());
989 let opts = vec!(optopt("t", "test", "testing", "TEST"));
990 let rs = getopts(long_args.as_slice(), opts.as_slice());
993 assert!(m.opt_present("test"));
994 assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
995 assert!((m.opt_present("t")));
996 assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
1000 let short_args = vec!("-t".to_owned(), "20".to_owned());
1001 match getopts(short_args.as_slice(), opts.as_slice()) {
1003 assert!((m.opt_present("test")));
1004 assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
1005 assert!((m.opt_present("t")));
1006 assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
1013 fn test_optopt_missing() {
1014 let args = vec!("blah".to_owned());
1015 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1016 let rs = getopts(args.as_slice(), opts.as_slice());
1019 assert!(!m.opt_present("test"));
1020 assert!(!m.opt_present("t"));
1027 fn test_optopt_no_arg() {
1028 let long_args = vec!("--test".to_owned());
1029 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1030 let rs = getopts(long_args.as_slice(), opts.as_slice());
1032 Err(f) => check_fail_type(f, ArgumentMissing_),
1035 let short_args = vec!("-t".to_owned());
1036 match getopts(short_args.as_slice(), opts.as_slice()) {
1037 Err(f) => check_fail_type(f, ArgumentMissing_),
1043 fn test_optopt_multi() {
1044 let args = vec!("--test=20".to_owned(), "-t".to_owned(), "30".to_owned());
1045 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1046 let rs = getopts(args.as_slice(), opts.as_slice());
1048 Err(f) => check_fail_type(f, OptionDuplicated_),
1053 // Tests for optflag
1056 let long_args = vec!("--test".to_owned());
1057 let opts = vec!(optflag("t", "test", "testing"));
1058 let rs = getopts(long_args.as_slice(), opts.as_slice());
1061 assert!(m.opt_present("test"));
1062 assert!(m.opt_present("t"));
1066 let short_args = vec!("-t".to_owned());
1067 match getopts(short_args.as_slice(), opts.as_slice()) {
1069 assert!(m.opt_present("test"));
1070 assert!(m.opt_present("t"));
1077 fn test_optflag_missing() {
1078 let args = vec!("blah".to_owned());
1079 let opts = vec!(optflag("t", "test", "testing"));
1080 let rs = getopts(args.as_slice(), opts.as_slice());
1083 assert!(!m.opt_present("test"));
1084 assert!(!m.opt_present("t"));
1091 fn test_optflag_long_arg() {
1092 let args = vec!("--test=20".to_owned());
1093 let opts = vec!(optflag("t", "test", "testing"));
1094 let rs = getopts(args.as_slice(), opts.as_slice());
1097 error!("{:?}", f.clone().to_err_msg());
1098 check_fail_type(f, UnexpectedArgument_);
1105 fn test_optflag_multi() {
1106 let args = vec!("--test".to_owned(), "-t".to_owned());
1107 let opts = vec!(optflag("t", "test", "testing"));
1108 let rs = getopts(args.as_slice(), opts.as_slice());
1110 Err(f) => check_fail_type(f, OptionDuplicated_),
1116 fn test_optflag_short_arg() {
1117 let args = vec!("-t".to_owned(), "20".to_owned());
1118 let opts = vec!(optflag("t", "test", "testing"));
1119 let rs = getopts(args.as_slice(), opts.as_slice());
1122 // The next variable after the flag is just a free argument
1124 assert!(*m.free.get(0) == "20".to_owned());
1130 // Tests for optflagmulti
1132 fn test_optflagmulti_short1() {
1133 let args = vec!("-v".to_owned());
1134 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1135 let rs = getopts(args.as_slice(), opts.as_slice());
1138 assert_eq!(m.opt_count("v"), 1);
1145 fn test_optflagmulti_short2a() {
1146 let args = vec!("-v".to_owned(), "-v".to_owned());
1147 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1148 let rs = getopts(args.as_slice(), opts.as_slice());
1151 assert_eq!(m.opt_count("v"), 2);
1158 fn test_optflagmulti_short2b() {
1159 let args = vec!("-vv".to_owned());
1160 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1161 let rs = getopts(args.as_slice(), opts.as_slice());
1164 assert_eq!(m.opt_count("v"), 2);
1171 fn test_optflagmulti_long1() {
1172 let args = vec!("--verbose".to_owned());
1173 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1174 let rs = getopts(args.as_slice(), opts.as_slice());
1177 assert_eq!(m.opt_count("verbose"), 1);
1184 fn test_optflagmulti_long2() {
1185 let args = vec!("--verbose".to_owned(), "--verbose".to_owned());
1186 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1187 let rs = getopts(args.as_slice(), opts.as_slice());
1190 assert_eq!(m.opt_count("verbose"), 2);
1197 fn test_optflagmulti_mix() {
1198 let args = vec!("--verbose".to_owned(), "-v".to_owned(),
1199 "-vv".to_owned(), "verbose".to_owned());
1200 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1201 let rs = getopts(args.as_slice(), opts.as_slice());
1204 assert_eq!(m.opt_count("verbose"), 4);
1205 assert_eq!(m.opt_count("v"), 4);
1211 // Tests for optmulti
1213 fn test_optmulti() {
1214 let long_args = vec!("--test=20".to_owned());
1215 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1216 let rs = getopts(long_args.as_slice(), opts.as_slice());
1219 assert!((m.opt_present("test")));
1220 assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
1221 assert!((m.opt_present("t")));
1222 assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
1226 let short_args = vec!("-t".to_owned(), "20".to_owned());
1227 match getopts(short_args.as_slice(), opts.as_slice()) {
1229 assert!((m.opt_present("test")));
1230 assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
1231 assert!((m.opt_present("t")));
1232 assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
1239 fn test_optmulti_missing() {
1240 let args = vec!("blah".to_owned());
1241 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1242 let rs = getopts(args.as_slice(), opts.as_slice());
1245 assert!(!m.opt_present("test"));
1246 assert!(!m.opt_present("t"));
1253 fn test_optmulti_no_arg() {
1254 let long_args = vec!("--test".to_owned());
1255 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1256 let rs = getopts(long_args.as_slice(), opts.as_slice());
1258 Err(f) => check_fail_type(f, ArgumentMissing_),
1261 let short_args = vec!("-t".to_owned());
1262 match getopts(short_args.as_slice(), opts.as_slice()) {
1263 Err(f) => check_fail_type(f, ArgumentMissing_),
1269 fn test_optmulti_multi() {
1270 let args = vec!("--test=20".to_owned(), "-t".to_owned(), "30".to_owned());
1271 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1272 let rs = getopts(args.as_slice(), opts.as_slice());
1275 assert!(m.opt_present("test"));
1276 assert_eq!(m.opt_str("test").unwrap(), "20".to_owned());
1277 assert!(m.opt_present("t"));
1278 assert_eq!(m.opt_str("t").unwrap(), "20".to_owned());
1279 let pair = m.opt_strs("test");
1280 assert!(*pair.get(0) == "20".to_owned());
1281 assert!(*pair.get(1) == "30".to_owned());
1288 fn test_unrecognized_option() {
1289 let long_args = vec!("--untest".to_owned());
1290 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1291 let rs = getopts(long_args.as_slice(), opts.as_slice());
1293 Err(f) => check_fail_type(f, UnrecognizedOption_),
1296 let short_args = vec!("-u".to_owned());
1297 match getopts(short_args.as_slice(), opts.as_slice()) {
1298 Err(f) => check_fail_type(f, UnrecognizedOption_),
1304 fn test_combined() {
1306 vec!("prog".to_owned(), "free1".to_owned(), "-s".to_owned(), "20".to_owned(),
1307 "free2".to_owned(), "--flag".to_owned(), "--long=30".to_owned(), "-f".to_owned(),
1308 "-m".to_owned(), "40".to_owned(), "-m".to_owned(), "50".to_owned(), "-n".to_owned(),
1309 "-A B".to_owned(), "-n".to_owned(), "-60 70".to_owned());
1311 vec!(optopt("s", "something", "something", "SOMETHING"),
1312 optflag("", "flag", "a flag"),
1313 reqopt("", "long", "hi", "LONG"),
1314 optflag("f", "", "another flag"),
1315 optmulti("m", "", "mmmmmm", "YUM"),
1316 optmulti("n", "", "nothing", "NOTHING"),
1317 optopt("", "notpresent", "nothing to see here", "NOPE"));
1318 let rs = getopts(args.as_slice(), opts.as_slice());
1321 assert!(*m.free.get(0) == "prog".to_owned());
1322 assert!(*m.free.get(1) == "free1".to_owned());
1323 assert_eq!(m.opt_str("s").unwrap(), "20".to_owned());
1324 assert!(*m.free.get(2) == "free2".to_owned());
1325 assert!((m.opt_present("flag")));
1326 assert_eq!(m.opt_str("long").unwrap(), "30".to_owned());
1327 assert!((m.opt_present("f")));
1328 let pair = m.opt_strs("m");
1329 assert!(*pair.get(0) == "40".to_owned());
1330 assert!(*pair.get(1) == "50".to_owned());
1331 let pair = m.opt_strs("n");
1332 assert!(*pair.get(0) == "-A B".to_owned());
1333 assert!(*pair.get(1) == "-60 70".to_owned());
1334 assert!((!m.opt_present("notpresent")));
1342 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1343 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1344 optopt("f", "", "flag", "FLAG"));
1346 let args_single = vec!("-e".to_owned(), "foo".to_owned());
1347 let matches_single = &match getopts(args_single.as_slice(),
1350 result::Err(_) => fail!()
1352 assert!(matches_single.opts_present(["e".to_owned()]));
1353 assert!(matches_single.opts_present(["encrypt".to_owned(), "e".to_owned()]));
1354 assert!(matches_single.opts_present(["e".to_owned(), "encrypt".to_owned()]));
1355 assert!(!matches_single.opts_present(["encrypt".to_owned()]));
1356 assert!(!matches_single.opts_present(["thing".to_owned()]));
1357 assert!(!matches_single.opts_present([]));
1359 assert_eq!(matches_single.opts_str(["e".to_owned()]).unwrap(), "foo".to_owned());
1360 assert_eq!(matches_single.opts_str(["e".to_owned(), "encrypt".to_owned()]).unwrap(),
1362 assert_eq!(matches_single.opts_str(["encrypt".to_owned(), "e".to_owned()]).unwrap(),
1365 let args_both = vec!("-e".to_owned(), "foo".to_owned(), "--encrypt".to_owned(),
1367 let matches_both = &match getopts(args_both.as_slice(),
1370 result::Err(_) => fail!()
1372 assert!(matches_both.opts_present(["e".to_owned()]));
1373 assert!(matches_both.opts_present(["encrypt".to_owned()]));
1374 assert!(matches_both.opts_present(["encrypt".to_owned(), "e".to_owned()]));
1375 assert!(matches_both.opts_present(["e".to_owned(), "encrypt".to_owned()]));
1376 assert!(!matches_both.opts_present(["f".to_owned()]));
1377 assert!(!matches_both.opts_present(["thing".to_owned()]));
1378 assert!(!matches_both.opts_present([]));
1380 assert_eq!(matches_both.opts_str(["e".to_owned()]).unwrap(), "foo".to_owned());
1381 assert_eq!(matches_both.opts_str(["encrypt".to_owned()]).unwrap(), "foo".to_owned());
1382 assert_eq!(matches_both.opts_str(["e".to_owned(), "encrypt".to_owned()]).unwrap(),
1384 assert_eq!(matches_both.opts_str(["encrypt".to_owned(), "e".to_owned()]).unwrap(),
1390 let args = vec!("-Lfoo".to_owned(), "-M.".to_owned());
1391 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1392 optmulti("M", "", "something", "MMMM"));
1393 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1395 result::Err(_) => fail!()
1397 assert!(matches.opts_present(["L".to_owned()]));
1398 assert_eq!(matches.opts_str(["L".to_owned()]).unwrap(), "foo".to_owned());
1399 assert!(matches.opts_present(["M".to_owned()]));
1400 assert_eq!(matches.opts_str(["M".to_owned()]).unwrap(), ".".to_owned());
1405 fn test_long_to_short() {
1406 let mut short = Opt {
1407 name: Long("banana".to_owned()),
1410 aliases: Vec::new(),
1412 short.aliases = vec!(Opt { name: Short('b'),
1415 aliases: Vec::new() });
1416 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1418 assert!(verbose.long_to_short() == short);
1422 fn test_aliases_long_and_short() {
1424 optflagmulti("a", "apple", "Desc"));
1426 let args = vec!("-a".to_owned(), "--apple".to_owned(), "-a".to_owned());
1428 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1429 assert_eq!(3, matches.opt_count("a"));
1430 assert_eq!(3, matches.opt_count("apple"));
1435 let optgroups = vec!(
1436 reqopt("b", "banana", "Desc", "VAL"),
1437 optopt("a", "012345678901234567890123456789",
1439 optflag("k", "kiwi", "Desc"),
1440 optflagopt("p", "", "Desc", "VAL"),
1441 optmulti("l", "", "Desc", "VAL"));
1447 -b --banana VAL Desc
1448 -a --012345678901234567890123456789 VAL
1455 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1457 debug!("expected: <<{}>>", expected);
1458 debug!("generated: <<{}>>", generated_usage);
1459 assert_eq!(generated_usage, expected);
1463 fn test_usage_description_wrapping() {
1464 // indentation should be 24 spaces
1465 // lines wrap after 78: or rather descriptions wrap after 54
1467 let optgroups = vec!(
1468 optflag("k", "kiwi",
1469 "This is a long description which won't be wrapped..+.."), // 54
1470 optflag("a", "apple",
1471 "This is a long description which _will_ be wrapped..+.."));
1477 -k --kiwi This is a long description which won't be wrapped..+..
1478 -a --apple This is a long description which _will_ be
1482 let usage = usage("Usage: fruits", optgroups.as_slice());
1484 debug!("expected: <<{}>>", expected);
1485 debug!("generated: <<{}>>", usage);
1486 assert!(usage == expected)
1490 fn test_usage_description_multibyte_handling() {
1491 let optgroups = vec!(
1492 optflag("k", "k\u2013w\u2013",
1493 "The word kiwi is normally spelled with two i's"),
1494 optflag("a", "apple",
1495 "This \u201Cdescription\u201D has some characters that could \
1496 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1502 -k --k–w– The word kiwi is normally spelled with two i's
1503 -a --apple This “description” has some characters that could
1504 confuse the line wrapping; an apple costs 0.51€ in
1505 some parts of Europe.
1508 let usage = usage("Usage: fruits", optgroups.as_slice());
1510 debug!("expected: <<{}>>", expected);
1511 debug!("generated: <<{}>>", usage);
1512 assert!(usage == expected)
1516 fn test_short_usage() {
1517 let optgroups = vec!(
1518 reqopt("b", "banana", "Desc", "VAL"),
1519 optopt("a", "012345678901234567890123456789",
1521 optflag("k", "kiwi", "Desc"),
1522 optflagopt("p", "", "Desc", "VAL"),
1523 optmulti("l", "", "Desc", "VAL"));
1525 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_owned();
1526 let generated_usage = short_usage("fruits", optgroups.as_slice());
1528 debug!("expected: <<{}>>", expected);
1529 debug!("generated: <<{}>>", generated_usage);
1530 assert_eq!(generated_usage, expected);