1 // Copyright 2012-2013 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.
12 * Simple getopt alternative.
14 * Construct a vector of options, either by using reqopt, optopt, and optflag
15 * or by building them from components yourself, and pass them to getopts,
16 * along with a vector of actual arguments (not including argv[0]). You'll
17 * either get a failure code back, or a match. You'll have to verify whether
18 * the amount of 'free' arguments in the match is what you expect. Use opt_*
19 * accessors to get argument values out of the matches object.
21 * Single-character options are expected to appear on the command line with a
22 * single preceding dash; multiple-character options are expected to be
23 * proceeded by two dashes. Options that expect an argument accept their
24 * argument following either a space or an equals sign. Single-character
25 * options don't require the space.
29 * The following example shows simple command line parsing for an application
30 * that requires an input file to be specified, accepts an optional output
31 * file name following -o, and accepts both -h and --help as optional flags.
35 * use extra::getopts::*;
38 * fn do_work(in: &str, out: Option<~str>) {
42 * None => ~"No Output"
46 * fn print_usage(program: &str, _opts: &[Opt]) {
47 * printfln!("Usage: %s [options]", program);
48 * println("-o\t\tOutput");
49 * println("-h --help\tUsage");
53 * let args = os::args();
55 * let program = args[0].clone();
62 * let matches = match getopts(args.tail(), opts) {
64 * Err(f) => { fail!(fail_str(f)) }
66 * if opt_present(&matches, "h") || opt_present(&matches, "help") {
67 * print_usage(program, opts);
70 * let output = opt_maybe_str(&matches, "o");
71 * let input: &str = if !matches.free.is_empty() {
72 * matches.free[0].clone()
74 * print_usage(program, opts);
77 * do_work(input, output);
82 #[allow(missing_doc)];
86 use std::result::{Err, Ok};
88 use std::option::{Some, None};
92 #[deriving(Clone, Eq)]
98 #[deriving(Clone, Eq)]
105 #[deriving(Clone, Eq)]
112 /// A description of a possible option
113 #[deriving(Clone, Eq)]
120 fn mkname(nm: &str) -> Name {
122 Short(nm.char_at(0u))
128 /// Create an option that is required and takes an argument
129 pub fn reqopt(name: &str) -> Opt {
130 return Opt {name: mkname(name), hasarg: Yes, occur: Req};
133 /// Create an option that is optional and takes an argument
134 pub fn optopt(name: &str) -> Opt {
135 return Opt {name: mkname(name), hasarg: Yes, occur: Optional};
138 /// Create an option that is optional and does not take an argument
139 pub fn optflag(name: &str) -> Opt {
140 return Opt {name: mkname(name), hasarg: No, occur: Optional};
143 /// Create an option that is optional and does not take an argument
144 pub fn optflagmulti(name: &str) -> Opt {
145 return Opt {name: mkname(name), hasarg: No, occur: Multi};
148 /// Create an option that is optional and takes an optional argument
149 pub fn optflagopt(name: &str) -> Opt {
150 return Opt {name: mkname(name), hasarg: Maybe, occur: Optional};
154 * Create an option that is optional, takes an argument, and may occur
157 pub fn optmulti(name: &str) -> Opt {
158 return Opt {name: mkname(name), hasarg: Yes, occur: Multi};
161 #[deriving(Clone, Eq)]
168 * The result of checking command line arguments. Contains a vector
169 * of matches and a vector of free strings.
171 #[deriving(Clone, Eq)]
178 fn is_arg(arg: &str) -> bool {
179 return arg.len() > 1 && arg[0] == '-' as u8;
182 fn name_str(nm: &Name) -> ~str {
184 Short(ch) => str::from_char(ch),
185 Long(ref s) => (*s).clone()
189 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
190 opts.iter().position(|opt| opt.name == nm)
194 * The type returned when the command line does not conform to the
195 * expected format. Pass this value to <fail_str> to get an error message.
197 #[deriving(Clone, Eq)]
199 ArgumentMissing(~str),
200 UnrecognizedOption(~str),
202 OptionDuplicated(~str),
203 UnexpectedArgument(~str),
206 /// Convert a `fail_` enum into an error string
207 pub fn fail_str(f: Fail_) -> ~str {
209 ArgumentMissing(ref nm) => {
210 fmt!("Argument to option '%s' missing.", *nm)
212 UnrecognizedOption(ref nm) => {
213 fmt!("Unrecognized option: '%s'.", *nm)
215 OptionMissing(ref nm) => {
216 fmt!("Required option '%s' missing.", *nm)
218 OptionDuplicated(ref nm) => {
219 fmt!("Option '%s' given more than once.", *nm)
221 UnexpectedArgument(ref nm) => {
222 fmt!("Option '%s' does not take an argument.", *nm)
228 * The result of parsing a command line with a set of options
229 * (result::t<Matches, Fail_>)
231 pub type Result = result::Result<Matches, Fail_>;
234 * Parse command line arguments according to the provided options
236 * On success returns `ok(Opt)`. Use functions such as `opt_present`
237 * `opt_str`, etc. to interrogate results. Returns `err(Fail_)` on failure.
238 * Use <fail_str> to get an error message.
240 pub fn getopts(args: &[~str], opts: &[Opt]) -> Result {
241 let n_opts = opts.len();
242 fn f(_x: uint) -> ~[Optval] { return ~[]; }
243 let mut vals = vec::from_fn(n_opts, f);
244 let mut free: ~[~str] = ~[];
248 let cur = args[i].clone();
249 let curlen = cur.len();
252 } else if cur == ~"--" {
254 while j < l { free.push(args[j].clone()); j += 1; }
258 let mut i_arg = None;
259 if cur[1] == '-' as u8 {
260 let tail = cur.slice(2, curlen);
261 let tail_eq: ~[&str] = tail.split_iter('=').collect();
262 if tail_eq.len() <= 1 {
263 names = ~[Long(tail.to_owned())];
266 ~[Long(tail_eq[0].to_owned())];
267 i_arg = Some(tail_eq[1].to_owned());
271 let mut last_valid_opt_id = None;
274 let range = cur.char_range_at(j);
275 let opt = Short(range.ch);
277 /* In a series of potential options (eg. -aheJ), if we
278 see one which takes an argument, we assume all
279 subsequent characters make up the argument. This
280 allows options such as -L/usr/local/lib/foo to be
281 interpreted correctly
284 match find_opt(opts, opt.clone()) {
285 Some(id) => last_valid_opt_id = Some(id),
288 last_valid_opt_id.is_some() &&
289 match opts[last_valid_opt_id.get()]
295 if arg_follows && j < curlen {
296 i_arg = Some(cur.slice(j, curlen).to_owned());
299 last_valid_opt_id = None;
307 let mut name_pos = 0;
308 for names.iter().advance() |nm| {
310 let optid = match find_opt(opts, (*nm).clone()) {
312 None => return Err(UnrecognizedOption(name_str(nm)))
314 match opts[optid].hasarg {
316 if !i_arg.is_none() {
317 return Err(UnexpectedArgument(name_str(nm)));
319 vals[optid].push(Given);
322 if !i_arg.is_none() {
323 vals[optid].push(Val((i_arg.clone()).get()));
324 } else if name_pos < names.len() ||
325 i + 1 == l || is_arg(args[i + 1]) {
326 vals[optid].push(Given);
327 } else { i += 1; vals[optid].push(Val(args[i].clone())); }
330 if !i_arg.is_none() {
331 vals[optid].push(Val(i_arg.clone().get()));
332 } else if i + 1 == l {
333 return Err(ArgumentMissing(name_str(nm)));
334 } else { i += 1; vals[optid].push(Val(args[i].clone())); }
343 let n = vals[i].len();
344 let occ = opts[i].occur;
347 return Err(OptionMissing(name_str(&(opts[i].name))));
352 return Err(OptionDuplicated(name_str(&(opts[i].name))));
357 return Ok(Matches {opts: opts.to_owned(),
362 fn opt_vals(mm: &Matches, nm: &str) -> ~[Optval] {
363 return match find_opt(mm.opts, mkname(nm)) {
364 Some(id) => mm.vals[id].clone(),
366 error!("No option '%s' defined", nm);
372 fn opt_val(mm: &Matches, nm: &str) -> Optval { opt_vals(mm, nm)[0].clone() }
374 /// Returns true if an option was matched
375 pub fn opt_present(mm: &Matches, nm: &str) -> bool {
376 !opt_vals(mm, nm).is_empty()
379 /// Returns the number of times an option was matched
380 pub fn opt_count(mm: &Matches, nm: &str) -> uint {
381 opt_vals(mm, nm).len()
384 /// Returns true if any of several options were matched
385 pub fn opts_present(mm: &Matches, names: &[~str]) -> bool {
386 for names.iter().advance |nm| {
387 match find_opt(mm.opts, mkname(*nm)) {
388 Some(id) if !mm.vals[id].is_empty() => return true,
397 * Returns the string argument supplied to a matching option
399 * Fails if the option was not matched or if the match did not take an
402 pub fn opt_str(mm: &Matches, nm: &str) -> ~str {
403 return match opt_val(mm, nm) { Val(s) => s, _ => fail!() };
407 * Returns the string argument supplied to one of several matching options
409 * Fails if the no option was provided from the given list, or if the no such
410 * option took an argument
412 pub fn opts_str(mm: &Matches, names: &[~str]) -> ~str {
413 for names.iter().advance |nm| {
414 match opt_val(mm, *nm) {
415 Val(ref s) => return (*s).clone(),
424 * Returns a vector of the arguments provided to all matches of the given
427 * Used when an option accepts multiple values.
429 pub fn opt_strs(mm: &Matches, nm: &str) -> ~[~str] {
430 let mut acc: ~[~str] = ~[];
431 let r = opt_vals(mm, nm);
432 for r.iter().advance |v| {
433 match *v { Val(ref s) => acc.push((*s).clone()), _ => () }
438 /// Returns the string argument supplied to a matching option or none
439 pub fn opt_maybe_str(mm: &Matches, nm: &str) -> Option<~str> {
440 let vals = opt_vals(mm, nm);
441 if vals.is_empty() { return None::<~str>; }
442 return match vals[0] {
443 Val(ref s) => Some((*s).clone()),
450 * Returns the matching string, a default, or none
452 * Returns none if the option was not present, `def` if the option was
453 * present but no argument was provided, and the argument if the option was
454 * present and an argument was provided.
456 pub fn opt_default(mm: &Matches, nm: &str, def: &str) -> Option<~str> {
457 let vals = opt_vals(mm, nm);
458 if vals.is_empty() { return None::<~str>; }
459 return match vals[0] { Val(ref s) => Some::<~str>((*s).clone()),
460 _ => Some::<~str>(def.to_owned()) }
472 /** A module which provides a way to specify descriptions and
473 * groups of short and long option names, together.
476 use getopts::{HasArg, Long, Maybe, Multi, No, Occur, Opt, Optional, Req};
477 use getopts::{Short, Yes};
482 /** one group of options, e.g., both -h and --help, along with
483 * their shared description and properties
485 #[deriving(Clone, Eq)]
486 pub struct OptGroup {
495 /// Create a long option that is required and takes an argument
496 pub fn reqopt(short_name: &str, long_name: &str,
497 desc: &str, hint: &str) -> OptGroup {
498 let len = short_name.len();
499 assert!(len == 1 || len == 0);
500 return OptGroup { short_name: short_name.to_owned(),
501 long_name: long_name.to_owned(),
502 hint: hint.to_owned(),
503 desc: desc.to_owned(),
508 /// Create a long option that is optional and takes an argument
509 pub fn optopt(short_name: &str, long_name: &str,
510 desc: &str, hint: &str) -> OptGroup {
511 let len = short_name.len();
512 assert!(len == 1 || len == 0);
513 return OptGroup {short_name: short_name.to_owned(),
514 long_name: long_name.to_owned(),
515 hint: hint.to_owned(),
516 desc: desc.to_owned(),
521 /// Create a long option that is optional and does not take an argument
522 pub fn optflag(short_name: &str, long_name: &str,
523 desc: &str) -> OptGroup {
524 let len = short_name.len();
525 assert!(len == 1 || len == 0);
526 return OptGroup {short_name: short_name.to_owned(),
527 long_name: long_name.to_owned(),
529 desc: desc.to_owned(),
534 /// Create a long option that is optional and takes an optional argument
535 pub fn optflagopt(short_name: &str, long_name: &str,
536 desc: &str, hint: &str) -> OptGroup {
537 let len = short_name.len();
538 assert!(len == 1 || len == 0);
539 return OptGroup {short_name: short_name.to_owned(),
540 long_name: long_name.to_owned(),
541 hint: hint.to_owned(),
542 desc: desc.to_owned(),
548 * Create a long option that is optional, takes an argument, and may occur
551 pub fn optmulti(short_name: &str, long_name: &str,
552 desc: &str, hint: &str) -> OptGroup {
553 let len = short_name.len();
554 assert!(len == 1 || len == 0);
555 return OptGroup {short_name: short_name.to_owned(),
556 long_name: long_name.to_owned(),
557 hint: hint.to_owned(),
558 desc: desc.to_owned(),
563 // translate OptGroup into Opt
564 // (both short and long names correspond to different Opts)
565 pub fn long_to_short(lopt: &OptGroup) -> ~[Opt] {
566 let OptGroup{short_name: short_name,
567 long_name: long_name,
570 _} = (*lopt).clone();
572 match (short_name.len(), long_name.len()) {
573 (0,0) => fail!("this long-format option was given no name"),
575 (0,_) => ~[Opt {name: Long((long_name)),
579 (1,0) => ~[Opt {name: Short(short_name.char_at(0)),
583 (1,_) => ~[Opt {name: Short(short_name.char_at(0)),
586 Opt {name: Long((long_name)),
590 (_,_) => fail!("something is wrong with the long-form opt")
595 * Parse command line args with the provided long format options
597 pub fn getopts(args: &[~str], opts: &[OptGroup]) -> ::getopts::Result {
598 ::getopts::getopts(args, vec::flat_map(opts, long_to_short))
602 * Derive a usage message from a set of long options
604 pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {
606 let desc_sep = "\n" + " ".repeat(24);
608 let mut rows = opts.iter().transform(|optref| {
609 let OptGroup{short_name: short_name,
610 long_name: long_name,
614 _} = (*optref).clone();
616 let mut row = " ".repeat(4);
619 match short_name.len() {
623 row.push_str(short_name);
626 _ => fail!("the short name should only be 1 ascii char long"),
630 match long_name.len() {
634 row.push_str(long_name);
642 Yes => row.push_str(hint),
651 // here we just need to indent the start of the description
652 let rowlen = row.len();
654 for (24 - rowlen).times {
658 row.push_str(desc_sep)
661 // Normalize desc to contain words separated by one space character
662 let mut desc_normalized_whitespace = ~"";
663 for desc.word_iter().advance |word| {
664 desc_normalized_whitespace.push_str(word);
665 desc_normalized_whitespace.push_char(' ');
669 let mut desc_rows = ~[];
670 for str::each_split_within(desc_normalized_whitespace, 54) |substr| {
671 desc_rows.push(substr.to_owned());
675 // wrapped description
676 row.push_str(desc_rows.connect(desc_sep));
681 return brief.to_owned() +
683 rows.collect::<~[~str]>().connect("\n") +
686 } // end groups module
691 use getopts::groups::OptGroup;
694 use std::result::{Err, Ok};
697 fn check_fail_type(f: Fail_, ft: FailType) {
699 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
700 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
701 OptionMissing(_) => assert!(ft == OptionMissing_),
702 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
703 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
710 fn test_reqopt_long() {
711 let args = ~[~"--test=20"];
712 let opts = ~[reqopt("test")];
713 let rs = getopts(args, opts);
716 assert!((opt_present(m, "test")));
717 assert_eq!(opt_str(m, "test"), ~"20");
719 _ => { fail!("test_reqopt_long failed"); }
724 fn test_reqopt_long_missing() {
725 let args = ~[~"blah"];
726 let opts = ~[reqopt("test")];
727 let rs = getopts(args, opts);
729 Err(f) => check_fail_type(f, OptionMissing_),
735 fn test_reqopt_long_no_arg() {
736 let args = ~[~"--test"];
737 let opts = ~[reqopt("test")];
738 let rs = getopts(args, opts);
740 Err(f) => check_fail_type(f, ArgumentMissing_),
746 fn test_reqopt_long_multi() {
747 let args = ~[~"--test=20", ~"--test=30"];
748 let opts = ~[reqopt("test")];
749 let rs = getopts(args, opts);
751 Err(f) => check_fail_type(f, OptionDuplicated_),
757 fn test_reqopt_short() {
758 let args = ~[~"-t", ~"20"];
759 let opts = ~[reqopt("t")];
760 let rs = getopts(args, opts);
763 assert!((opt_present(m, "t")));
764 assert_eq!(opt_str(m, "t"), ~"20");
771 fn test_reqopt_short_missing() {
772 let args = ~[~"blah"];
773 let opts = ~[reqopt("t")];
774 let rs = getopts(args, opts);
776 Err(f) => check_fail_type(f, OptionMissing_),
782 fn test_reqopt_short_no_arg() {
784 let opts = ~[reqopt("t")];
785 let rs = getopts(args, opts);
787 Err(f) => check_fail_type(f, ArgumentMissing_),
793 fn test_reqopt_short_multi() {
794 let args = ~[~"-t", ~"20", ~"-t", ~"30"];
795 let opts = ~[reqopt("t")];
796 let rs = getopts(args, opts);
798 Err(f) => check_fail_type(f, OptionDuplicated_),
806 fn test_optopt_long() {
807 let args = ~[~"--test=20"];
808 let opts = ~[optopt("test")];
809 let rs = getopts(args, opts);
812 assert!((opt_present(m, "test")));
813 assert_eq!(opt_str(m, "test"), ~"20");
820 fn test_optopt_long_missing() {
821 let args = ~[~"blah"];
822 let opts = ~[optopt("test")];
823 let rs = getopts(args, opts);
825 Ok(ref m) => assert!(!opt_present(m, "test")),
831 fn test_optopt_long_no_arg() {
832 let args = ~[~"--test"];
833 let opts = ~[optopt("test")];
834 let rs = getopts(args, opts);
836 Err(f) => check_fail_type(f, ArgumentMissing_),
842 fn test_optopt_long_multi() {
843 let args = ~[~"--test=20", ~"--test=30"];
844 let opts = ~[optopt("test")];
845 let rs = getopts(args, opts);
847 Err(f) => check_fail_type(f, OptionDuplicated_),
853 fn test_optopt_short() {
854 let args = ~[~"-t", ~"20"];
855 let opts = ~[optopt("t")];
856 let rs = getopts(args, opts);
859 assert!((opt_present(m, "t")));
860 assert_eq!(opt_str(m, "t"), ~"20");
867 fn test_optopt_short_missing() {
868 let args = ~[~"blah"];
869 let opts = ~[optopt("t")];
870 let rs = getopts(args, opts);
872 Ok(ref m) => assert!(!opt_present(m, "t")),
878 fn test_optopt_short_no_arg() {
880 let opts = ~[optopt("t")];
881 let rs = getopts(args, opts);
883 Err(f) => check_fail_type(f, ArgumentMissing_),
889 fn test_optopt_short_multi() {
890 let args = ~[~"-t", ~"20", ~"-t", ~"30"];
891 let opts = ~[optopt("t")];
892 let rs = getopts(args, opts);
894 Err(f) => check_fail_type(f, OptionDuplicated_),
902 fn test_optflag_long() {
903 let args = ~[~"--test"];
904 let opts = ~[optflag("test")];
905 let rs = getopts(args, opts);
907 Ok(ref m) => assert!(opt_present(m, "test")),
913 fn test_optflag_long_missing() {
914 let args = ~[~"blah"];
915 let opts = ~[optflag("test")];
916 let rs = getopts(args, opts);
918 Ok(ref m) => assert!(!opt_present(m, "test")),
924 fn test_optflag_long_arg() {
925 let args = ~[~"--test=20"];
926 let opts = ~[optflag("test")];
927 let rs = getopts(args, opts);
930 error!(fail_str(f.clone()));
931 check_fail_type(f, UnexpectedArgument_);
938 fn test_optflag_long_multi() {
939 let args = ~[~"--test", ~"--test"];
940 let opts = ~[optflag("test")];
941 let rs = getopts(args, opts);
943 Err(f) => check_fail_type(f, OptionDuplicated_),
949 fn test_optflag_short() {
951 let opts = ~[optflag("t")];
952 let rs = getopts(args, opts);
954 Ok(ref m) => assert!(opt_present(m, "t")),
960 fn test_optflag_short_missing() {
961 let args = ~[~"blah"];
962 let opts = ~[optflag("t")];
963 let rs = getopts(args, opts);
965 Ok(ref m) => assert!(!opt_present(m, "t")),
971 fn test_optflag_short_arg() {
972 let args = ~[~"-t", ~"20"];
973 let opts = ~[optflag("t")];
974 let rs = getopts(args, opts);
977 // The next variable after the flag is just a free argument
979 assert!(m.free[0] == ~"20");
986 fn test_optflag_short_multi() {
987 let args = ~[~"-t", ~"-t"];
988 let opts = ~[optflag("t")];
989 let rs = getopts(args, opts);
991 Err(f) => check_fail_type(f, OptionDuplicated_),
996 // Tests for optflagmulti
998 fn test_optflagmulti_short1() {
1000 let opts = ~[optflagmulti("v")];
1001 let rs = getopts(args, opts);
1004 assert_eq!(opt_count(m, "v"), 1);
1011 fn test_optflagmulti_short2a() {
1012 let args = ~[~"-v", ~"-v"];
1013 let opts = ~[optflagmulti("v")];
1014 let rs = getopts(args, opts);
1017 assert_eq!(opt_count(m, "v"), 2);
1024 fn test_optflagmulti_short2b() {
1025 let args = ~[~"-vv"];
1026 let opts = ~[optflagmulti("v")];
1027 let rs = getopts(args, opts);
1030 assert_eq!(opt_count(m, "v"), 2);
1037 fn test_optflagmulti_long1() {
1038 let args = ~[~"--verbose"];
1039 let opts = ~[optflagmulti("verbose")];
1040 let rs = getopts(args, opts);
1043 assert_eq!(opt_count(m, "verbose"), 1);
1050 fn test_optflagmulti_long2() {
1051 let args = ~[~"--verbose", ~"--verbose"];
1052 let opts = ~[optflagmulti("verbose")];
1053 let rs = getopts(args, opts);
1056 assert_eq!(opt_count(m, "verbose"), 2);
1062 // Tests for optmulti
1064 fn test_optmulti_long() {
1065 let args = ~[~"--test=20"];
1066 let opts = ~[optmulti("test")];
1067 let rs = getopts(args, opts);
1070 assert!((opt_present(m, "test")));
1071 assert_eq!(opt_str(m, "test"), ~"20");
1078 fn test_optmulti_long_missing() {
1079 let args = ~[~"blah"];
1080 let opts = ~[optmulti("test")];
1081 let rs = getopts(args, opts);
1083 Ok(ref m) => assert!(!opt_present(m, "test")),
1089 fn test_optmulti_long_no_arg() {
1090 let args = ~[~"--test"];
1091 let opts = ~[optmulti("test")];
1092 let rs = getopts(args, opts);
1094 Err(f) => check_fail_type(f, ArgumentMissing_),
1100 fn test_optmulti_long_multi() {
1101 let args = ~[~"--test=20", ~"--test=30"];
1102 let opts = ~[optmulti("test")];
1103 let rs = getopts(args, opts);
1106 assert!(opt_present(m, "test"));
1107 assert_eq!(opt_str(m, "test"), ~"20");
1108 let pair = opt_strs(m, "test");
1109 assert!(pair[0] == ~"20");
1110 assert!(pair[1] == ~"30");
1117 fn test_optmulti_short() {
1118 let args = ~[~"-t", ~"20"];
1119 let opts = ~[optmulti("t")];
1120 let rs = getopts(args, opts);
1123 assert!((opt_present(m, "t")));
1124 assert_eq!(opt_str(m, "t"), ~"20");
1131 fn test_optmulti_short_missing() {
1132 let args = ~[~"blah"];
1133 let opts = ~[optmulti("t")];
1134 let rs = getopts(args, opts);
1136 Ok(ref m) => assert!(!opt_present(m, "t")),
1142 fn test_optmulti_short_no_arg() {
1143 let args = ~[~"-t"];
1144 let opts = ~[optmulti("t")];
1145 let rs = getopts(args, opts);
1147 Err(f) => check_fail_type(f, ArgumentMissing_),
1153 fn test_optmulti_short_multi() {
1154 let args = ~[~"-t", ~"20", ~"-t", ~"30"];
1155 let opts = ~[optmulti("t")];
1156 let rs = getopts(args, opts);
1159 assert!((opt_present(m, "t")));
1160 assert_eq!(opt_str(m, "t"), ~"20");
1161 let pair = opt_strs(m, "t");
1162 assert!(pair[0] == ~"20");
1163 assert!(pair[1] == ~"30");
1170 fn test_unrecognized_option_long() {
1171 let args = ~[~"--untest"];
1172 let opts = ~[optmulti("t")];
1173 let rs = getopts(args, opts);
1175 Err(f) => check_fail_type(f, UnrecognizedOption_),
1181 fn test_unrecognized_option_short() {
1182 let args = ~[~"-t"];
1183 let opts = ~[optmulti("test")];
1184 let rs = getopts(args, opts);
1186 Err(f) => check_fail_type(f, UnrecognizedOption_),
1192 fn test_combined() {
1194 ~[~"prog", ~"free1", ~"-s", ~"20", ~"free2",
1195 ~"--flag", ~"--long=30", ~"-f", ~"-m", ~"40",
1196 ~"-m", ~"50", ~"-n", ~"-A B", ~"-n", ~"-60 70"];
1198 ~[optopt("s"), optflag("flag"), reqopt("long"),
1199 optflag("f"), optmulti("m"), optmulti("n"),
1200 optopt("notpresent")];
1201 let rs = getopts(args, opts);
1204 assert!(m.free[0] == ~"prog");
1205 assert!(m.free[1] == ~"free1");
1206 assert_eq!(opt_str(m, "s"), ~"20");
1207 assert!(m.free[2] == ~"free2");
1208 assert!((opt_present(m, "flag")));
1209 assert_eq!(opt_str(m, "long"), ~"30");
1210 assert!((opt_present(m, "f")));
1211 let pair = opt_strs(m, "m");
1212 assert!(pair[0] == ~"40");
1213 assert!(pair[1] == ~"50");
1214 let pair = opt_strs(m, "n");
1215 assert!(pair[0] == ~"-A B");
1216 assert!(pair[1] == ~"-60 70");
1217 assert!((!opt_present(m, "notpresent")));
1225 let args = ~[~"-e", ~"foo", ~"--encrypt", ~"foo"];
1226 let opts = ~[optopt("e"), optopt("encrypt"), optopt("f")];
1227 let matches = &match getopts(args, opts) {
1229 result::Err(_) => fail!()
1231 assert!(opts_present(matches, [~"e"]));
1232 assert!(opts_present(matches, [~"encrypt"]));
1233 assert!(opts_present(matches, [~"encrypt", ~"e"]));
1234 assert!(opts_present(matches, [~"e", ~"encrypt"]));
1235 assert!(!opts_present(matches, [~"f"]));
1236 assert!(!opts_present(matches, [~"thing"]));
1237 assert!(!opts_present(matches, []));
1239 assert_eq!(opts_str(matches, [~"e"]), ~"foo");
1240 assert_eq!(opts_str(matches, [~"encrypt"]), ~"foo");
1241 assert_eq!(opts_str(matches, [~"e", ~"encrypt"]), ~"foo");
1242 assert_eq!(opts_str(matches, [~"encrypt", ~"e"]), ~"foo");
1247 let args = ~[~"-Lfoo", ~"-M."];
1248 let opts = ~[optmulti("L"), optmulti("M")];
1249 let matches = &match getopts(args, opts) {
1251 result::Err(_) => fail!()
1253 assert!(opts_present(matches, [~"L"]));
1254 assert_eq!(opts_str(matches, [~"L"]), ~"foo");
1255 assert!(opts_present(matches, [~"M"]));
1256 assert_eq!(opts_str(matches, [~"M"]), ~".");
1261 fn test_groups_reqopt() {
1262 let opt = groups::reqopt("b", "banana", "some bananas", "VAL");
1263 assert!(opt == OptGroup { short_name: ~"b",
1264 long_name: ~"banana",
1266 desc: ~"some bananas",
1272 fn test_groups_optopt() {
1273 let opt = groups::optopt("a", "apple", "some apples", "VAL");
1274 assert!(opt == OptGroup { short_name: ~"a",
1275 long_name: ~"apple",
1277 desc: ~"some apples",
1283 fn test_groups_optflag() {
1284 let opt = groups::optflag("k", "kiwi", "some kiwis");
1285 assert!(opt == OptGroup { short_name: ~"k",
1288 desc: ~"some kiwis",
1294 fn test_groups_optflagopt() {
1295 let opt = groups::optflagopt("p", "pineapple", "some pineapples", "VAL");
1296 assert!(opt == OptGroup { short_name: ~"p",
1297 long_name: ~"pineapple",
1299 desc: ~"some pineapples",
1305 fn test_groups_optmulti() {
1306 let opt = groups::optmulti("l", "lime", "some limes", "VAL");
1307 assert!(opt == OptGroup { short_name: ~"l",
1310 desc: ~"some limes",
1316 fn test_groups_long_to_short() {
1317 let short = ~[reqopt("b"), reqopt("banana")];
1318 let verbose = groups::reqopt("b", "banana", "some bananas", "VAL");
1320 assert_eq!(groups::long_to_short(&verbose), short);
1324 fn test_groups_getopts() {
1326 reqopt("b"), reqopt("banana"),
1327 optopt("a"), optopt("apple"),
1328 optflag("k"), optflagopt("kiwi"),
1334 groups::reqopt("b", "banana", "Desc", "VAL"),
1335 groups::optopt("a", "apple", "Desc", "VAL"),
1336 groups::optflag("k", "kiwi", "Desc"),
1337 groups::optflagopt("p", "", "Desc", "VAL"),
1338 groups::optmulti("l", "", "Desc", "VAL"),
1341 let sample_args = ~[~"-k", ~"15", ~"--apple", ~"1", ~"k",
1342 ~"-p", ~"16", ~"l", ~"35"];
1344 // FIXME #4681: sort options here?
1345 assert!(getopts(sample_args, short)
1346 == groups::getopts(sample_args, verbose));
1350 fn test_groups_usage() {
1352 groups::reqopt("b", "banana", "Desc", "VAL"),
1353 groups::optopt("a", "012345678901234567890123456789",
1355 groups::optflag("k", "kiwi", "Desc"),
1356 groups::optflagopt("p", "", "Desc", "VAL"),
1357 groups::optmulti("l", "", "Desc", "VAL"),
1364 -b --banana VAL Desc
1365 -a --012345678901234567890123456789 VAL
1373 let generated_usage = groups::usage("Usage: fruits", optgroups);
1375 debug!("expected: <<%s>>", expected);
1376 debug!("generated: <<%s>>", generated_usage);
1377 assert_eq!(generated_usage, expected);
1381 fn test_groups_usage_description_wrapping() {
1382 // indentation should be 24 spaces
1383 // lines wrap after 78: or rather descriptions wrap after 54
1386 groups::optflag("k", "kiwi",
1387 "This is a long description which won't be wrapped..+.."), // 54
1388 groups::optflag("a", "apple",
1389 "This is a long description which _will_ be wrapped..+.."), // 55
1396 -k --kiwi This is a long description which won't be wrapped..+..
1397 -a --apple This is a long description which _will_ be
1402 let usage = groups::usage("Usage: fruits", optgroups);
1404 debug!("expected: <<%s>>", expected);
1405 debug!("generated: <<%s>>", usage);
1406 assert!(usage == expected)