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, does not take an argument,
144 * and may occur multiple times.
146 pub fn optflagmulti(name: &str) -> Opt {
147 return Opt {name: mkname(name), hasarg: No, occur: Multi};
150 /// Create an option that is optional and takes an optional argument
151 pub fn optflagopt(name: &str) -> Opt {
152 return Opt {name: mkname(name), hasarg: Maybe, occur: Optional};
156 * Create an option that is optional, takes an argument, and may occur
159 pub fn optmulti(name: &str) -> Opt {
160 return Opt {name: mkname(name), hasarg: Yes, occur: Multi};
163 #[deriving(Clone, Eq)]
170 * The result of checking command line arguments. Contains a vector
171 * of matches and a vector of free strings.
173 #[deriving(Clone, Eq)]
180 fn is_arg(arg: &str) -> bool {
181 return arg.len() > 1 && arg[0] == '-' as u8;
184 fn name_str(nm: &Name) -> ~str {
186 Short(ch) => str::from_char(ch),
187 Long(ref s) => (*s).clone()
191 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
192 opts.iter().position(|opt| opt.name == nm)
196 * The type returned when the command line does not conform to the
197 * expected format. Pass this value to <fail_str> to get an error message.
199 #[deriving(Clone, Eq, ToStr)]
201 ArgumentMissing(~str),
202 UnrecognizedOption(~str),
204 OptionDuplicated(~str),
205 UnexpectedArgument(~str),
208 /// Convert a `fail_` enum into an error string
209 pub fn fail_str(f: Fail_) -> ~str {
211 ArgumentMissing(ref nm) => {
212 fmt!("Argument to option '%s' missing.", *nm)
214 UnrecognizedOption(ref nm) => {
215 fmt!("Unrecognized option: '%s'.", *nm)
217 OptionMissing(ref nm) => {
218 fmt!("Required option '%s' missing.", *nm)
220 OptionDuplicated(ref nm) => {
221 fmt!("Option '%s' given more than once.", *nm)
223 UnexpectedArgument(ref nm) => {
224 fmt!("Option '%s' does not take an argument.", *nm)
230 * The result of parsing a command line with a set of options
231 * (result::t<Matches, Fail_>)
233 pub type Result = result::Result<Matches, Fail_>;
236 * Parse command line arguments according to the provided options
238 * On success returns `ok(Opt)`. Use functions such as `opt_present`
239 * `opt_str`, etc. to interrogate results. Returns `err(Fail_)` on failure.
240 * Use <fail_str> to get an error message.
242 pub fn getopts(args: &[~str], opts: &[Opt]) -> Result {
243 let n_opts = opts.len();
244 fn f(_x: uint) -> ~[Optval] { return ~[]; }
245 let mut vals = vec::from_fn(n_opts, f);
246 let mut free: ~[~str] = ~[];
250 let cur = args[i].clone();
251 let curlen = cur.len();
254 } else if cur == ~"--" {
256 while j < l { free.push(args[j].clone()); j += 1; }
260 let mut i_arg = None;
261 if cur[1] == '-' as u8 {
262 let tail = cur.slice(2, curlen);
263 let tail_eq: ~[&str] = tail.split_iter('=').collect();
264 if tail_eq.len() <= 1 {
265 names = ~[Long(tail.to_owned())];
268 ~[Long(tail_eq[0].to_owned())];
269 i_arg = Some(tail_eq[1].to_owned());
273 let mut last_valid_opt_id = None;
276 let range = cur.char_range_at(j);
277 let opt = Short(range.ch);
279 /* In a series of potential options (eg. -aheJ), if we
280 see one which takes an argument, we assume all
281 subsequent characters make up the argument. This
282 allows options such as -L/usr/local/lib/foo to be
283 interpreted correctly
286 match find_opt(opts, opt.clone()) {
287 Some(id) => last_valid_opt_id = Some(id),
290 last_valid_opt_id.is_some() &&
291 match opts[last_valid_opt_id.unwrap()]
297 if arg_follows && j < curlen {
298 i_arg = Some(cur.slice(j, curlen).to_owned());
301 last_valid_opt_id = None;
309 let mut name_pos = 0;
310 for nm in names.iter() {
312 let optid = match find_opt(opts, (*nm).clone()) {
314 None => return Err(UnrecognizedOption(name_str(nm)))
316 match opts[optid].hasarg {
318 if !i_arg.is_none() {
319 return Err(UnexpectedArgument(name_str(nm)));
321 vals[optid].push(Given);
324 if !i_arg.is_none() {
325 vals[optid].push(Val((i_arg.clone()).unwrap()));
326 } else if name_pos < names.len() ||
327 i + 1 == l || is_arg(args[i + 1]) {
328 vals[optid].push(Given);
329 } else { i += 1; vals[optid].push(Val(args[i].clone())); }
332 if !i_arg.is_none() {
333 vals[optid].push(Val(i_arg.clone().unwrap()));
334 } else if i + 1 == l {
335 return Err(ArgumentMissing(name_str(nm)));
336 } else { i += 1; vals[optid].push(Val(args[i].clone())); }
345 let n = vals[i].len();
346 let occ = opts[i].occur;
349 return Err(OptionMissing(name_str(&(opts[i].name))));
354 return Err(OptionDuplicated(name_str(&(opts[i].name))));
359 return Ok(Matches {opts: opts.to_owned(),
364 fn opt_vals(mm: &Matches, nm: &str) -> ~[Optval] {
365 return match find_opt(mm.opts, mkname(nm)) {
366 Some(id) => mm.vals[id].clone(),
368 error!("No option '%s' defined", nm);
374 fn opt_val(mm: &Matches, nm: &str) -> Option<Optval> {
375 let vals = opt_vals(mm, nm);
376 if (vals.is_empty()) {
379 Some(opt_vals(mm, nm)[0].clone())
383 /// Returns true if an option was matched
384 pub fn opt_present(mm: &Matches, nm: &str) -> bool {
385 !opt_vals(mm, nm).is_empty()
388 /// Returns the number of times an option was matched
389 pub fn opt_count(mm: &Matches, nm: &str) -> uint {
390 opt_vals(mm, nm).len()
393 /// Returns true if any of several options were matched
394 pub fn opts_present(mm: &Matches, names: &[~str]) -> bool {
395 for nm in names.iter() {
396 match find_opt(mm.opts, mkname(*nm)) {
397 Some(id) if !mm.vals[id].is_empty() => return true,
406 * Returns the string argument supplied to a matching option
408 * Fails if the option was not matched or if the match did not take an
411 pub fn opt_str(mm: &Matches, nm: &str) -> ~str {
412 return match opt_val(mm, nm) {
419 * Returns the string argument supplied to one of several matching options
421 * Fails if the no option was provided from the given list, or if the no such
422 * option took an argument
424 pub fn opts_str(mm: &Matches, names: &[~str]) -> ~str {
425 for nm in names.iter() {
426 match opt_val(mm, *nm) {
427 Some(Val(ref s)) => return (*s).clone(),
436 * Returns a vector of the arguments provided to all matches of the given
439 * Used when an option accepts multiple values.
441 pub fn opt_strs(mm: &Matches, nm: &str) -> ~[~str] {
442 let mut acc: ~[~str] = ~[];
443 let r = opt_vals(mm, nm);
445 match *v { Val(ref s) => acc.push((*s).clone()), _ => () }
450 /// Returns the string argument supplied to a matching option or none
451 pub fn opt_maybe_str(mm: &Matches, nm: &str) -> Option<~str> {
452 let vals = opt_vals(mm, nm);
453 if vals.is_empty() { return None::<~str>; }
454 return match vals[0] {
455 Val(ref s) => Some((*s).clone()),
462 * Returns the matching string, a default, or none
464 * Returns none if the option was not present, `def` if the option was
465 * present but no argument was provided, and the argument if the option was
466 * present and an argument was provided.
468 pub fn opt_default(mm: &Matches, nm: &str, def: &str) -> Option<~str> {
469 let vals = opt_vals(mm, nm);
470 if vals.is_empty() { return None::<~str>; }
471 return match vals[0] { Val(ref s) => Some::<~str>((*s).clone()),
472 _ => Some::<~str>(def.to_owned()) }
484 /** A module which provides a way to specify descriptions and
485 * groups of short and long option names, together.
488 use getopts::{HasArg, Long, Maybe, Multi, No, Occur, Opt, Optional, Req};
489 use getopts::{Short, Yes};
493 /** one group of options, e.g., both -h and --help, along with
494 * their shared description and properties
496 #[deriving(Clone, Eq)]
497 pub struct OptGroup {
506 /// Create a long option that is required and takes an argument
507 pub fn reqopt(short_name: &str, long_name: &str,
508 desc: &str, hint: &str) -> OptGroup {
509 let len = short_name.len();
510 assert!(len == 1 || len == 0);
511 return OptGroup { short_name: short_name.to_owned(),
512 long_name: long_name.to_owned(),
513 hint: hint.to_owned(),
514 desc: desc.to_owned(),
519 /// Create a long option that is optional and takes an argument
520 pub fn optopt(short_name: &str, long_name: &str,
521 desc: &str, hint: &str) -> OptGroup {
522 let len = short_name.len();
523 assert!(len == 1 || len == 0);
524 return OptGroup {short_name: short_name.to_owned(),
525 long_name: long_name.to_owned(),
526 hint: hint.to_owned(),
527 desc: desc.to_owned(),
532 /// Create a long option that is optional and does not take an argument
533 pub fn optflag(short_name: &str, long_name: &str,
534 desc: &str) -> OptGroup {
535 let len = short_name.len();
536 assert!(len == 1 || len == 0);
537 return OptGroup {short_name: short_name.to_owned(),
538 long_name: long_name.to_owned(),
540 desc: desc.to_owned(),
545 /// Create a long option that is optional and takes an optional argument
546 pub fn optflagopt(short_name: &str, long_name: &str,
547 desc: &str, hint: &str) -> OptGroup {
548 let len = short_name.len();
549 assert!(len == 1 || len == 0);
550 return OptGroup {short_name: short_name.to_owned(),
551 long_name: long_name.to_owned(),
552 hint: hint.to_owned(),
553 desc: desc.to_owned(),
559 * Create a long option that is optional, takes an argument, and may occur
562 pub fn optmulti(short_name: &str, long_name: &str,
563 desc: &str, hint: &str) -> OptGroup {
564 let len = short_name.len();
565 assert!(len == 1 || len == 0);
566 return OptGroup {short_name: short_name.to_owned(),
567 long_name: long_name.to_owned(),
568 hint: hint.to_owned(),
569 desc: desc.to_owned(),
574 // translate OptGroup into Opt
575 // (both short and long names correspond to different Opts)
576 pub fn long_to_short(lopt: &OptGroup) -> ~[Opt] {
577 let OptGroup{short_name: short_name,
578 long_name: long_name,
581 _} = (*lopt).clone();
583 match (short_name.len(), long_name.len()) {
584 (0,0) => fail!("this long-format option was given no name"),
586 (0,_) => ~[Opt {name: Long((long_name)),
590 (1,0) => ~[Opt {name: Short(short_name.char_at(0)),
594 (1,_) => ~[Opt {name: Short(short_name.char_at(0)),
597 Opt {name: Long((long_name)),
601 (_,_) => fail!("something is wrong with the long-form opt")
606 * Parse command line args with the provided long format options
608 pub fn getopts(args: &[~str], opts: &[OptGroup]) -> ::getopts::Result {
609 ::getopts::getopts(args, vec::flat_map(opts, long_to_short))
613 * Derive a usage message from a set of long options
615 pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {
617 let desc_sep = "\n" + " ".repeat(24);
619 let mut rows = opts.iter().transform(|optref| {
620 let OptGroup{short_name: short_name,
621 long_name: long_name,
625 _} = (*optref).clone();
627 let mut row = " ".repeat(4);
630 match short_name.len() {
634 row.push_str(short_name);
637 _ => fail!("the short name should only be 1 ascii char long"),
641 match long_name.len() {
645 row.push_str(long_name);
653 Yes => row.push_str(hint),
662 // here we just need to indent the start of the description
663 let rowlen = row.len();
665 do (24 - rowlen).times {
669 row.push_str(desc_sep)
672 // Normalize desc to contain words separated by one space character
673 let mut desc_normalized_whitespace = ~"";
674 for word in desc.word_iter() {
675 desc_normalized_whitespace.push_str(word);
676 desc_normalized_whitespace.push_char(' ');
680 let mut desc_rows = ~[];
681 do each_split_within(desc_normalized_whitespace, 54) |substr| {
682 desc_rows.push(substr.to_owned());
687 // wrapped description
688 row.push_str(desc_rows.connect(desc_sep));
693 return brief.to_owned() +
695 rows.collect::<~[~str]>().connect("\n") +
699 /** Splits a string into substrings with possibly internal whitespace,
700 * each of them at most `lim` bytes long. The substrings have leading and trailing
701 * whitespace removed, and are only cut at whitespace boundaries.
703 * Note: Function was moved here from `std::str` because this module is the only place that
704 * uses it, and because it was to specific for a general string function.
708 * Fails during iteration if the string contains a non-whitespace
709 * sequence longer than the limit.
711 fn each_split_within<'a>(ss: &'a str,
713 it: &fn(&'a str) -> bool) -> bool {
714 // Just for fun, let's write this as an state machine:
716 enum SplitWithinState {
717 A, // leading whitespace, initial state
719 C, // internal and trailing whitespace
722 Ws, // current char is whitespace
723 Cr // current char is not whitespace
726 UnderLim, // current char makes current substring still fit in limit
727 OverLim // current char makes current substring no longer fit in limit
730 let mut slice_start = 0;
731 let mut last_start = 0;
732 let mut last_end = 0;
734 let mut fake_i = ss.len();
738 let slice: &fn() = || { cont = it(ss.slice(slice_start, last_end)) };
740 // if the limit is larger than the string, lower it to save cycles
745 let machine: &fn((uint, char)) -> bool = |(i, c)| {
746 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
747 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
749 state = match (state, whitespace, limit) {
751 (A, Cr, _) => { slice_start = i; last_start = i; B }
753 (B, Cr, UnderLim) => { B }
754 (B, Cr, OverLim) if (i - last_start + 1) > lim
755 => fail!("word starting with %? longer than limit!",
756 ss.slice(last_start, i + 1)),
757 (B, Cr, OverLim) => { slice(); slice_start = last_start; B }
758 (B, Ws, UnderLim) => { last_end = i; C }
759 (B, Ws, OverLim) => { last_end = i; slice(); A }
761 (C, Cr, UnderLim) => { last_start = i; B }
762 (C, Cr, OverLim) => { slice(); slice_start = i; last_start = i; last_end = i; B }
763 (C, Ws, OverLim) => { slice(); A }
764 (C, Ws, UnderLim) => { C }
770 ss.iter().enumerate().advance(|x| machine(x));
772 // Let the automaton 'run out' by supplying trailing whitespace
773 while cont && match state { B | C => true, A => false } {
774 machine((fake_i, ' '));
781 fn test_split_within() {
782 fn t(s: &str, i: uint, u: &[~str]) {
784 do each_split_within(s, i) |s| { v.push(s.to_owned()); true };
785 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
789 t("hello", 15, [~"hello"]);
790 t("\nMary had a little lamb\nLittle lamb\n", 15,
791 [~"Mary had a", ~"little lamb", ~"Little lamb"]);
792 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::max_value,
793 [~"Mary had a little lamb\nLittle lamb"]);
795 } // end groups module
800 use getopts::groups::OptGroup;
803 use std::result::{Err, Ok};
806 fn check_fail_type(f: Fail_, ft: FailType) {
808 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
809 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
810 OptionMissing(_) => assert!(ft == OptionMissing_),
811 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
812 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
819 fn test_reqopt_long() {
820 let args = ~[~"--test=20"];
821 let opts = ~[reqopt("test")];
822 let rs = getopts(args, opts);
825 assert!((opt_present(m, "test")));
826 assert_eq!(opt_str(m, "test"), ~"20");
828 _ => { fail!("test_reqopt_long failed"); }
833 fn test_reqopt_long_missing() {
834 let args = ~[~"blah"];
835 let opts = ~[reqopt("test")];
836 let rs = getopts(args, opts);
838 Err(f) => check_fail_type(f, OptionMissing_),
844 fn test_reqopt_long_no_arg() {
845 let args = ~[~"--test"];
846 let opts = ~[reqopt("test")];
847 let rs = getopts(args, opts);
849 Err(f) => check_fail_type(f, ArgumentMissing_),
855 fn test_reqopt_long_multi() {
856 let args = ~[~"--test=20", ~"--test=30"];
857 let opts = ~[reqopt("test")];
858 let rs = getopts(args, opts);
860 Err(f) => check_fail_type(f, OptionDuplicated_),
866 fn test_reqopt_short() {
867 let args = ~[~"-t", ~"20"];
868 let opts = ~[reqopt("t")];
869 let rs = getopts(args, opts);
872 assert!((opt_present(m, "t")));
873 assert_eq!(opt_str(m, "t"), ~"20");
880 fn test_reqopt_short_missing() {
881 let args = ~[~"blah"];
882 let opts = ~[reqopt("t")];
883 let rs = getopts(args, opts);
885 Err(f) => check_fail_type(f, OptionMissing_),
891 fn test_reqopt_short_no_arg() {
893 let opts = ~[reqopt("t")];
894 let rs = getopts(args, opts);
896 Err(f) => check_fail_type(f, ArgumentMissing_),
902 fn test_reqopt_short_multi() {
903 let args = ~[~"-t", ~"20", ~"-t", ~"30"];
904 let opts = ~[reqopt("t")];
905 let rs = getopts(args, opts);
907 Err(f) => check_fail_type(f, OptionDuplicated_),
915 fn test_optopt_long() {
916 let args = ~[~"--test=20"];
917 let opts = ~[optopt("test")];
918 let rs = getopts(args, opts);
921 assert!((opt_present(m, "test")));
922 assert_eq!(opt_str(m, "test"), ~"20");
929 fn test_optopt_long_missing() {
930 let args = ~[~"blah"];
931 let opts = ~[optopt("test")];
932 let rs = getopts(args, opts);
934 Ok(ref m) => assert!(!opt_present(m, "test")),
940 fn test_optopt_long_no_arg() {
941 let args = ~[~"--test"];
942 let opts = ~[optopt("test")];
943 let rs = getopts(args, opts);
945 Err(f) => check_fail_type(f, ArgumentMissing_),
951 fn test_optopt_long_multi() {
952 let args = ~[~"--test=20", ~"--test=30"];
953 let opts = ~[optopt("test")];
954 let rs = getopts(args, opts);
956 Err(f) => check_fail_type(f, OptionDuplicated_),
962 fn test_optopt_short() {
963 let args = ~[~"-t", ~"20"];
964 let opts = ~[optopt("t")];
965 let rs = getopts(args, opts);
968 assert!((opt_present(m, "t")));
969 assert_eq!(opt_str(m, "t"), ~"20");
976 fn test_optopt_short_missing() {
977 let args = ~[~"blah"];
978 let opts = ~[optopt("t")];
979 let rs = getopts(args, opts);
981 Ok(ref m) => assert!(!opt_present(m, "t")),
987 fn test_optopt_short_no_arg() {
989 let opts = ~[optopt("t")];
990 let rs = getopts(args, opts);
992 Err(f) => check_fail_type(f, ArgumentMissing_),
998 fn test_optopt_short_multi() {
999 let args = ~[~"-t", ~"20", ~"-t", ~"30"];
1000 let opts = ~[optopt("t")];
1001 let rs = getopts(args, opts);
1003 Err(f) => check_fail_type(f, OptionDuplicated_),
1009 // Tests for optflag
1011 fn test_optflag_long() {
1012 let args = ~[~"--test"];
1013 let opts = ~[optflag("test")];
1014 let rs = getopts(args, opts);
1016 Ok(ref m) => assert!(opt_present(m, "test")),
1022 fn test_optflag_long_missing() {
1023 let args = ~[~"blah"];
1024 let opts = ~[optflag("test")];
1025 let rs = getopts(args, opts);
1027 Ok(ref m) => assert!(!opt_present(m, "test")),
1033 fn test_optflag_long_arg() {
1034 let args = ~[~"--test=20"];
1035 let opts = ~[optflag("test")];
1036 let rs = getopts(args, opts);
1039 error!(fail_str(f.clone()));
1040 check_fail_type(f, UnexpectedArgument_);
1047 fn test_optflag_long_multi() {
1048 let args = ~[~"--test", ~"--test"];
1049 let opts = ~[optflag("test")];
1050 let rs = getopts(args, opts);
1052 Err(f) => check_fail_type(f, OptionDuplicated_),
1058 fn test_optflag_short() {
1059 let args = ~[~"-t"];
1060 let opts = ~[optflag("t")];
1061 let rs = getopts(args, opts);
1063 Ok(ref m) => assert!(opt_present(m, "t")),
1069 fn test_optflag_short_missing() {
1070 let args = ~[~"blah"];
1071 let opts = ~[optflag("t")];
1072 let rs = getopts(args, opts);
1074 Ok(ref m) => assert!(!opt_present(m, "t")),
1080 fn test_optflag_short_arg() {
1081 let args = ~[~"-t", ~"20"];
1082 let opts = ~[optflag("t")];
1083 let rs = getopts(args, opts);
1086 // The next variable after the flag is just a free argument
1088 assert!(m.free[0] == ~"20");
1095 fn test_optflag_short_multi() {
1096 let args = ~[~"-t", ~"-t"];
1097 let opts = ~[optflag("t")];
1098 let rs = getopts(args, opts);
1100 Err(f) => check_fail_type(f, OptionDuplicated_),
1105 // Tests for optflagmulti
1107 fn test_optflagmulti_short1() {
1108 let args = ~[~"-v"];
1109 let opts = ~[optflagmulti("v")];
1110 let rs = getopts(args, opts);
1113 assert_eq!(opt_count(m, "v"), 1);
1120 fn test_optflagmulti_short2a() {
1121 let args = ~[~"-v", ~"-v"];
1122 let opts = ~[optflagmulti("v")];
1123 let rs = getopts(args, opts);
1126 assert_eq!(opt_count(m, "v"), 2);
1133 fn test_optflagmulti_short2b() {
1134 let args = ~[~"-vv"];
1135 let opts = ~[optflagmulti("v")];
1136 let rs = getopts(args, opts);
1139 assert_eq!(opt_count(m, "v"), 2);
1146 fn test_optflagmulti_long1() {
1147 let args = ~[~"--verbose"];
1148 let opts = ~[optflagmulti("verbose")];
1149 let rs = getopts(args, opts);
1152 assert_eq!(opt_count(m, "verbose"), 1);
1159 fn test_optflagmulti_long2() {
1160 let args = ~[~"--verbose", ~"--verbose"];
1161 let opts = ~[optflagmulti("verbose")];
1162 let rs = getopts(args, opts);
1165 assert_eq!(opt_count(m, "verbose"), 2);
1171 // Tests for optmulti
1173 fn test_optmulti_long() {
1174 let args = ~[~"--test=20"];
1175 let opts = ~[optmulti("test")];
1176 let rs = getopts(args, opts);
1179 assert!((opt_present(m, "test")));
1180 assert_eq!(opt_str(m, "test"), ~"20");
1187 fn test_optmulti_long_missing() {
1188 let args = ~[~"blah"];
1189 let opts = ~[optmulti("test")];
1190 let rs = getopts(args, opts);
1192 Ok(ref m) => assert!(!opt_present(m, "test")),
1198 fn test_optmulti_long_no_arg() {
1199 let args = ~[~"--test"];
1200 let opts = ~[optmulti("test")];
1201 let rs = getopts(args, opts);
1203 Err(f) => check_fail_type(f, ArgumentMissing_),
1209 fn test_optmulti_long_multi() {
1210 let args = ~[~"--test=20", ~"--test=30"];
1211 let opts = ~[optmulti("test")];
1212 let rs = getopts(args, opts);
1215 assert!(opt_present(m, "test"));
1216 assert_eq!(opt_str(m, "test"), ~"20");
1217 let pair = opt_strs(m, "test");
1218 assert!(pair[0] == ~"20");
1219 assert!(pair[1] == ~"30");
1226 fn test_optmulti_short() {
1227 let args = ~[~"-t", ~"20"];
1228 let opts = ~[optmulti("t")];
1229 let rs = getopts(args, opts);
1232 assert!((opt_present(m, "t")));
1233 assert_eq!(opt_str(m, "t"), ~"20");
1240 fn test_optmulti_short_missing() {
1241 let args = ~[~"blah"];
1242 let opts = ~[optmulti("t")];
1243 let rs = getopts(args, opts);
1245 Ok(ref m) => assert!(!opt_present(m, "t")),
1251 fn test_optmulti_short_no_arg() {
1252 let args = ~[~"-t"];
1253 let opts = ~[optmulti("t")];
1254 let rs = getopts(args, opts);
1256 Err(f) => check_fail_type(f, ArgumentMissing_),
1262 fn test_optmulti_short_multi() {
1263 let args = ~[~"-t", ~"20", ~"-t", ~"30"];
1264 let opts = ~[optmulti("t")];
1265 let rs = getopts(args, opts);
1268 assert!((opt_present(m, "t")));
1269 assert_eq!(opt_str(m, "t"), ~"20");
1270 let pair = opt_strs(m, "t");
1271 assert!(pair[0] == ~"20");
1272 assert!(pair[1] == ~"30");
1279 fn test_unrecognized_option_long() {
1280 let args = ~[~"--untest"];
1281 let opts = ~[optmulti("t")];
1282 let rs = getopts(args, opts);
1284 Err(f) => check_fail_type(f, UnrecognizedOption_),
1290 fn test_unrecognized_option_short() {
1291 let args = ~[~"-t"];
1292 let opts = ~[optmulti("test")];
1293 let rs = getopts(args, opts);
1295 Err(f) => check_fail_type(f, UnrecognizedOption_),
1301 fn test_combined() {
1303 ~[~"prog", ~"free1", ~"-s", ~"20", ~"free2",
1304 ~"--flag", ~"--long=30", ~"-f", ~"-m", ~"40",
1305 ~"-m", ~"50", ~"-n", ~"-A B", ~"-n", ~"-60 70"];
1307 ~[optopt("s"), optflag("flag"), reqopt("long"),
1308 optflag("f"), optmulti("m"), optmulti("n"),
1309 optopt("notpresent")];
1310 let rs = getopts(args, opts);
1313 assert!(m.free[0] == ~"prog");
1314 assert!(m.free[1] == ~"free1");
1315 assert_eq!(opt_str(m, "s"), ~"20");
1316 assert!(m.free[2] == ~"free2");
1317 assert!((opt_present(m, "flag")));
1318 assert_eq!(opt_str(m, "long"), ~"30");
1319 assert!((opt_present(m, "f")));
1320 let pair = opt_strs(m, "m");
1321 assert!(pair[0] == ~"40");
1322 assert!(pair[1] == ~"50");
1323 let pair = opt_strs(m, "n");
1324 assert!(pair[0] == ~"-A B");
1325 assert!(pair[1] == ~"-60 70");
1326 assert!((!opt_present(m, "notpresent")));
1334 let opts = ~[optopt("e"), optopt("encrypt"), optopt("f")];
1336 let args_single = ~[~"-e", ~"foo"];
1337 let matches_single = &match getopts(args_single, opts) {
1339 result::Err(_) => fail!()
1341 assert!(opts_present(matches_single, [~"e"]));
1342 assert!(opts_present(matches_single, [~"encrypt", ~"e"]));
1343 assert!(opts_present(matches_single, [~"e", ~"encrypt"]));
1344 assert!(!opts_present(matches_single, [~"encrypt"]));
1345 assert!(!opts_present(matches_single, [~"thing"]));
1346 assert!(!opts_present(matches_single, []));
1348 assert_eq!(opts_str(matches_single, [~"e"]), ~"foo");
1349 assert_eq!(opts_str(matches_single, [~"e", ~"encrypt"]), ~"foo");
1350 assert_eq!(opts_str(matches_single, [~"encrypt", ~"e"]), ~"foo");
1352 let args_both = ~[~"-e", ~"foo", ~"--encrypt", ~"foo"];
1353 let matches_both = &match getopts(args_both, opts) {
1355 result::Err(_) => fail!()
1357 assert!(opts_present(matches_both, [~"e"]));
1358 assert!(opts_present(matches_both, [~"encrypt"]));
1359 assert!(opts_present(matches_both, [~"encrypt", ~"e"]));
1360 assert!(opts_present(matches_both, [~"e", ~"encrypt"]));
1361 assert!(!opts_present(matches_both, [~"f"]));
1362 assert!(!opts_present(matches_both, [~"thing"]));
1363 assert!(!opts_present(matches_both, []));
1365 assert_eq!(opts_str(matches_both, [~"e"]), ~"foo");
1366 assert_eq!(opts_str(matches_both, [~"encrypt"]), ~"foo");
1367 assert_eq!(opts_str(matches_both, [~"e", ~"encrypt"]), ~"foo");
1368 assert_eq!(opts_str(matches_both, [~"encrypt", ~"e"]), ~"foo");
1373 let args = ~[~"-Lfoo", ~"-M."];
1374 let opts = ~[optmulti("L"), optmulti("M")];
1375 let matches = &match getopts(args, opts) {
1377 result::Err(_) => fail!()
1379 assert!(opts_present(matches, [~"L"]));
1380 assert_eq!(opts_str(matches, [~"L"]), ~"foo");
1381 assert!(opts_present(matches, [~"M"]));
1382 assert_eq!(opts_str(matches, [~"M"]), ~".");
1387 fn test_groups_reqopt() {
1388 let opt = groups::reqopt("b", "banana", "some bananas", "VAL");
1389 assert!(opt == OptGroup { short_name: ~"b",
1390 long_name: ~"banana",
1392 desc: ~"some bananas",
1398 fn test_groups_optopt() {
1399 let opt = groups::optopt("a", "apple", "some apples", "VAL");
1400 assert!(opt == OptGroup { short_name: ~"a",
1401 long_name: ~"apple",
1403 desc: ~"some apples",
1409 fn test_groups_optflag() {
1410 let opt = groups::optflag("k", "kiwi", "some kiwis");
1411 assert!(opt == OptGroup { short_name: ~"k",
1414 desc: ~"some kiwis",
1420 fn test_groups_optflagopt() {
1421 let opt = groups::optflagopt("p", "pineapple", "some pineapples", "VAL");
1422 assert!(opt == OptGroup { short_name: ~"p",
1423 long_name: ~"pineapple",
1425 desc: ~"some pineapples",
1431 fn test_groups_optmulti() {
1432 let opt = groups::optmulti("l", "lime", "some limes", "VAL");
1433 assert!(opt == OptGroup { short_name: ~"l",
1436 desc: ~"some limes",
1442 fn test_groups_long_to_short() {
1443 let short = ~[reqopt("b"), reqopt("banana")];
1444 let verbose = groups::reqopt("b", "banana", "some bananas", "VAL");
1446 assert_eq!(groups::long_to_short(&verbose), short);
1450 fn test_groups_getopts() {
1452 reqopt("b"), reqopt("banana"),
1453 optopt("a"), optopt("apple"),
1454 optflag("k"), optflagopt("kiwi"),
1460 groups::reqopt("b", "banana", "Desc", "VAL"),
1461 groups::optopt("a", "apple", "Desc", "VAL"),
1462 groups::optflag("k", "kiwi", "Desc"),
1463 groups::optflagopt("p", "", "Desc", "VAL"),
1464 groups::optmulti("l", "", "Desc", "VAL"),
1467 let sample_args = ~[~"-k", ~"15", ~"--apple", ~"1", ~"k",
1468 ~"-p", ~"16", ~"l", ~"35"];
1470 // FIXME #4681: sort options here?
1471 assert!(getopts(sample_args, short)
1472 == groups::getopts(sample_args, verbose));
1476 fn test_groups_usage() {
1478 groups::reqopt("b", "banana", "Desc", "VAL"),
1479 groups::optopt("a", "012345678901234567890123456789",
1481 groups::optflag("k", "kiwi", "Desc"),
1482 groups::optflagopt("p", "", "Desc", "VAL"),
1483 groups::optmulti("l", "", "Desc", "VAL"),
1490 -b --banana VAL Desc
1491 -a --012345678901234567890123456789 VAL
1498 let generated_usage = groups::usage("Usage: fruits", optgroups);
1500 debug!("expected: <<%s>>", expected);
1501 debug!("generated: <<%s>>", generated_usage);
1502 assert_eq!(generated_usage, expected);
1506 fn test_groups_usage_description_wrapping() {
1507 // indentation should be 24 spaces
1508 // lines wrap after 78: or rather descriptions wrap after 54
1511 groups::optflag("k", "kiwi",
1512 "This is a long description which won't be wrapped..+.."), // 54
1513 groups::optflag("a", "apple",
1514 "This is a long description which _will_ be wrapped..+.."), // 55
1521 -k --kiwi This is a long description which won't be wrapped..+..
1522 -a --apple This is a long description which _will_ be
1526 let usage = groups::usage("Usage: fruits", optgroups);
1528 debug!("expected: <<%s>>", expected);
1529 debug!("generated: <<%s>>", usage);
1530 assert!(usage == expected)