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[0].clone()
72 //! print_usage(program, opts);
75 //! do_work(input, output);
79 #[crate_id = "getopts#0.10-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 #[allow(deprecated_owned_vector)]; // NOTE: remove after stage0
90 #[cfg(test)] #[phase(syntax, link)] extern crate log;
93 use std::result::{Err, Ok};
95 use std::option::{Some, None};
98 /// Name of an option. Either a string or a single char.
99 #[deriving(Clone, Eq)]
101 /// A string representing the long name of an option.
102 /// For example: "help"
104 /// A char representing the short name of an option.
109 /// Describes whether an option has an argument.
110 #[deriving(Clone, Eq)]
112 /// The option requires an argument.
114 /// The option is just a flag, therefore no argument.
116 /// The option argument is optional and it could or not exist.
120 /// Describes how often an option may occur.
121 #[deriving(Clone, Eq)]
123 /// The option occurs once.
125 /// The option could or not occur.
127 /// The option occurs once or multiple times.
131 /// A description of a possible option.
132 #[deriving(Clone, Eq)]
134 /// Name of the option
136 /// Whether it has an argument
138 /// How often it can occur
140 /// Which options it aliases
141 priv aliases: ~[Opt],
144 /// One group of options, e.g., both -h and --help, along with
145 /// their shared description and properties.
146 #[deriving(Clone, Eq)]
147 pub struct OptGroup {
148 /// Short Name of the `OptGroup`
150 /// Long Name of the `OptGroup`
156 /// Whether it has an argument
158 /// How often it can occur
162 /// Describes wether an option is given at all or has a value.
163 #[deriving(Clone, Eq)]
169 /// The result of checking command line arguments. Contains a vector
170 /// of matches and a vector of free strings.
171 #[deriving(Clone, Eq)]
173 /// Options that matched
175 /// Values of the Options that matched
176 priv vals: ~[~[Optval]],
177 /// Free string fragments
181 /// The type returned when the command line does not conform to the
182 /// expected format. Call the `to_err_msg` method to retrieve the
183 /// error as a string.
184 #[deriving(Clone, Eq, Show)]
186 /// The option requires an argument but none was passed.
187 ArgumentMissing(~str),
188 /// The passed option is not declared among the possible options.
189 UnrecognizedOption(~str),
190 /// A required option is not present.
192 /// A single occurence option is being used multiple times.
193 OptionDuplicated(~str),
194 /// There's an argument being passed to a non-argument option.
195 UnexpectedArgument(~str),
198 /// The type of failure that occurred.
200 #[allow(missing_doc)]
209 /// The result of parsing a command line with a set of options.
210 pub type Result = result::Result<Matches, Fail_>;
213 fn from_str(nm: &str) -> Name {
215 Short(nm.char_at(0u))
221 fn to_str(&self) -> ~str {
223 Short(ch) => ch.to_str(),
224 Long(ref s) => s.to_owned()
230 /// Translate OptGroup into Opt.
231 /// (Both short and long names correspond to different Opts).
232 pub fn long_to_short(&self) -> Opt {
234 short_name: short_name,
235 long_name: long_name,
241 match (short_name.len(), long_name.len()) {
242 (0,0) => fail!("this long-format option was given no name"),
244 name: Long((long_name)),
250 name: Short(short_name.char_at(0)),
256 name: Long((long_name)),
261 name: Short(short_name.char_at(0)),
268 (_,_) => fail!("something is wrong with the long-form opt")
274 fn opt_vals(&self, nm: &str) -> ~[Optval] {
275 match find_opt(self.opts, Name::from_str(nm)) {
276 Some(id) => self.vals[id].clone(),
277 None => fail!("No option '{}' defined", nm)
281 fn opt_val(&self, nm: &str) -> Option<Optval> {
282 let vals = self.opt_vals(nm);
286 Some(vals[0].clone())
290 /// Returns true if an option was matched.
291 pub fn opt_present(&self, nm: &str) -> bool {
292 !self.opt_vals(nm).is_empty()
295 /// Returns the number of times an option was matched.
296 pub fn opt_count(&self, nm: &str) -> uint {
297 self.opt_vals(nm).len()
300 /// Returns true if any of several options were matched.
301 pub fn opts_present(&self, names: &[~str]) -> bool {
302 for nm in names.iter() {
303 match find_opt(self.opts, Name::from_str(*nm)) {
304 Some(id) if !self.vals[id].is_empty() => return true,
311 /// Returns the string argument supplied to one of several matching options or `None`.
312 pub fn opts_str(&self, names: &[~str]) -> Option<~str> {
313 for nm in names.iter() {
314 match self.opt_val(*nm) {
315 Some(Val(ref s)) => return Some(s.clone()),
322 /// Returns a vector of the arguments provided to all matches of the given
325 /// Used when an option accepts multiple values.
326 pub fn opt_strs(&self, nm: &str) -> ~[~str] {
327 let mut acc: ~[~str] = ~[];
328 let r = self.opt_vals(nm);
331 Val(ref s) => acc.push((*s).clone()),
338 /// Returns the string argument supplied to a matching option or `None`.
339 pub fn opt_str(&self, nm: &str) -> Option<~str> {
340 let vals = self.opt_vals(nm);
345 Val(ref s) => Some((*s).clone()),
351 /// Returns the matching string, a default, or none.
353 /// Returns none if the option was not present, `def` if the option was
354 /// present but no argument was provided, and the argument if the option was
355 /// present and an argument was provided.
356 pub fn opt_default(&self, nm: &str, def: &str) -> Option<~str> {
357 let vals = self.opt_vals(nm);
358 if vals.is_empty() { return None; }
360 Val(ref s) => Some((*s).clone()),
361 _ => Some(def.to_owned())
367 fn is_arg(arg: &str) -> bool {
368 arg.len() > 1 && arg[0] == '-' as u8
371 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
372 // Search main options.
373 let pos = opts.iter().position(|opt| opt.name == nm);
378 // Search in aliases.
379 for candidate in opts.iter() {
380 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
381 return opts.iter().position(|opt| opt.name == candidate.name);
388 /// Create a long option that is required and takes an argument.
389 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
390 let len = short_name.len();
391 assert!(len == 1 || len == 0);
393 short_name: short_name.to_owned(),
394 long_name: long_name.to_owned(),
395 hint: hint.to_owned(),
396 desc: desc.to_owned(),
402 /// Create a long option that is optional and takes an argument.
403 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
404 let len = short_name.len();
405 assert!(len == 1 || len == 0);
407 short_name: short_name.to_owned(),
408 long_name: long_name.to_owned(),
409 hint: hint.to_owned(),
410 desc: desc.to_owned(),
416 /// Create a long option that is optional and does not take an argument.
417 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
418 let len = short_name.len();
419 assert!(len == 1 || len == 0);
421 short_name: short_name.to_owned(),
422 long_name: long_name.to_owned(),
424 desc: desc.to_owned(),
430 /// Create a long option that can occur more than once and does not
431 /// take an argument.
432 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
433 let len = short_name.len();
434 assert!(len == 1 || len == 0);
436 short_name: short_name.to_owned(),
437 long_name: long_name.to_owned(),
439 desc: desc.to_owned(),
445 /// Create a long option that is optional and takes an optional argument.
446 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
447 let len = short_name.len();
448 assert!(len == 1 || len == 0);
450 short_name: short_name.to_owned(),
451 long_name: long_name.to_owned(),
452 hint: hint.to_owned(),
453 desc: desc.to_owned(),
459 /// Create a long option that is optional, takes an argument, and may occur
461 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
462 let len = short_name.len();
463 assert!(len == 1 || len == 0);
465 short_name: short_name.to_owned(),
466 long_name: long_name.to_owned(),
467 hint: hint.to_owned(),
468 desc: desc.to_owned(),
474 /// Create a generic option group, stating all parameters explicitly
475 pub fn opt(short_name: &str,
480 occur: Occur) -> OptGroup {
481 let len = short_name.len();
482 assert!(len == 1 || len == 0);
484 short_name: short_name.to_owned(),
485 long_name: long_name.to_owned(),
486 hint: hint.to_owned(),
487 desc: desc.to_owned(),
494 /// Convert a `Fail_` enum into an error string.
495 pub fn to_err_msg(self) -> ~str {
497 ArgumentMissing(ref nm) => {
498 format!("Argument to option '{}' missing.", *nm)
500 UnrecognizedOption(ref nm) => {
501 format!("Unrecognized option: '{}'.", *nm)
503 OptionMissing(ref nm) => {
504 format!("Required option '{}' missing.", *nm)
506 OptionDuplicated(ref nm) => {
507 format!("Option '{}' given more than once.", *nm)
509 UnexpectedArgument(ref nm) => {
510 format!("Option '{}' does not take an argument.", *nm)
516 /// Parse command line arguments according to the provided options.
518 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
519 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on failure.
520 /// Use `to_err_msg` to get an error message.
521 pub fn getopts(args: &[~str], optgrps: &[OptGroup]) -> Result {
522 let opts = optgrps.map(|x| x.long_to_short());
523 let n_opts = opts.len();
525 fn f(_x: uint) -> ~[Optval] { return ~[]; }
527 let mut vals = slice::from_fn(n_opts, f);
528 let mut free: ~[~str] = ~[];
532 let cur = args[i].clone();
533 let curlen = cur.len();
536 } else if cur == ~"--" {
538 while j < l { free.push(args[j].clone()); j += 1; }
542 let mut i_arg = None;
543 if cur[1] == '-' as u8 {
544 let tail = cur.slice(2, curlen);
545 let tail_eq: ~[&str] = tail.split('=').collect();
546 if tail_eq.len() <= 1 {
547 names = ~[Long(tail.to_owned())];
550 ~[Long(tail_eq[0].to_owned())];
551 i_arg = Some(tail_eq[1].to_owned());
555 let mut last_valid_opt_id = None;
558 let range = cur.char_range_at(j);
559 let opt = Short(range.ch);
561 /* In a series of potential options (eg. -aheJ), if we
562 see one which takes an argument, we assume all
563 subsequent characters make up the argument. This
564 allows options such as -L/usr/local/lib/foo to be
565 interpreted correctly
568 match find_opt(opts, opt.clone()) {
569 Some(id) => last_valid_opt_id = Some(id),
572 last_valid_opt_id.is_some() &&
573 match opts[last_valid_opt_id.unwrap()]
579 if arg_follows && j < curlen {
580 i_arg = Some(cur.slice(j, curlen).to_owned());
583 last_valid_opt_id = None;
591 let mut name_pos = 0;
592 for nm in names.iter() {
594 let optid = match find_opt(opts, (*nm).clone()) {
596 None => return Err(UnrecognizedOption(nm.to_str()))
598 match opts[optid].hasarg {
600 if !i_arg.is_none() {
601 return Err(UnexpectedArgument(nm.to_str()));
603 vals[optid].push(Given);
606 if !i_arg.is_none() {
607 vals[optid].push(Val((i_arg.clone()).unwrap()));
608 } else if name_pos < names.len() ||
609 i + 1 == l || is_arg(args[i + 1]) {
610 vals[optid].push(Given);
611 } else { i += 1; vals[optid].push(Val(args[i].clone())); }
614 if !i_arg.is_none() {
615 vals[optid].push(Val(i_arg.clone().unwrap()));
616 } else if i + 1 == l {
617 return Err(ArgumentMissing(nm.to_str()));
618 } else { i += 1; vals[optid].push(Val(args[i].clone())); }
627 let n = vals[i].len();
628 let occ = opts[i].occur;
631 return Err(OptionMissing(opts[i].name.to_str()));
636 return Err(OptionDuplicated(opts[i].name.to_str()));
642 opts: opts.to_owned(),
648 /// Derive a usage message from a set of long options.
649 pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {
651 let desc_sep = "\n" + " ".repeat(24);
653 let mut rows = opts.iter().map(|optref| {
654 let OptGroup{short_name: short_name,
655 long_name: long_name,
659 ..} = (*optref).clone();
661 let mut row = " ".repeat(4);
664 match short_name.len() {
668 row.push_str(short_name);
671 _ => fail!("the short name should only be 1 ascii char long"),
675 match long_name.len() {
679 row.push_str(long_name);
687 Yes => row.push_str(hint),
695 // FIXME: #5516 should be graphemes not codepoints
696 // here we just need to indent the start of the description
697 let rowlen = row.char_len();
699 for _ in range(0, 24 - rowlen) {
703 row.push_str(desc_sep)
706 // Normalize desc to contain words separated by one space character
707 let mut desc_normalized_whitespace = ~"";
708 for word in desc.words() {
709 desc_normalized_whitespace.push_str(word);
710 desc_normalized_whitespace.push_char(' ');
713 // FIXME: #5516 should be graphemes not codepoints
714 let mut desc_rows = ~[];
715 each_split_within(desc_normalized_whitespace, 54, |substr| {
716 desc_rows.push(substr.to_owned());
720 // FIXME: #5516 should be graphemes not codepoints
721 // wrapped description
722 row.push_str(desc_rows.connect(desc_sep));
727 format!("{}\n\nOptions:\n{}\n", brief, rows.collect::<~[~str]>().connect("\n"))
730 fn format_option(opt: &OptGroup) -> ~str {
733 if opt.occur != Req {
737 // Use short_name is possible, but fallback to long_name.
738 if opt.short_name.len() > 0 {
740 line.push_str(opt.short_name);
743 line.push_str(opt.long_name);
746 if opt.hasarg != No {
748 if opt.hasarg == Maybe {
751 line.push_str(opt.hint);
752 if opt.hasarg == Maybe {
757 if opt.occur != Req {
760 if opt.occur == Multi {
767 /// Derive a short one-line usage summary from a set of long options.
768 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> ~str {
769 let mut line = ~"Usage: " + program_name + " ";
770 line.push_str(opts.iter().map(format_option).to_owned_vec().connect(" "));
776 /// Splits a string into substrings with possibly internal whitespace,
777 /// each of them at most `lim` bytes long. The substrings have leading and trailing
778 /// whitespace removed, and are only cut at whitespace boundaries.
780 /// Note: Function was moved here from `std::str` because this module is the only place that
781 /// uses it, and because it was to specific for a general string function.
785 /// Fails during iteration if the string contains a non-whitespace
786 /// sequence longer than the limit.
787 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
789 // Just for fun, let's write this as a state machine:
791 enum SplitWithinState {
792 A, // leading whitespace, initial state
794 C, // internal and trailing whitespace
797 Ws, // current char is whitespace
798 Cr // current char is not whitespace
801 UnderLim, // current char makes current substring still fit in limit
802 OverLim // current char makes current substring no longer fit in limit
805 let mut slice_start = 0;
806 let mut last_start = 0;
807 let mut last_end = 0;
809 let mut fake_i = ss.len();
814 // if the limit is larger than the string, lower it to save cycles
819 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
820 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
821 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
823 state = match (state, whitespace, limit) {
825 (A, Cr, _) => { slice_start = i; last_start = i; B }
827 (B, Cr, UnderLim) => { B }
828 (B, Cr, OverLim) if (i - last_start + 1) > lim
829 => fail!("word starting with {} longer than limit!",
830 ss.slice(last_start, i + 1)),
831 (B, Cr, OverLim) => {
832 *cont = it(ss.slice(slice_start, last_end));
833 slice_start = last_start;
836 (B, Ws, UnderLim) => {
840 (B, Ws, OverLim) => {
842 *cont = it(ss.slice(slice_start, last_end));
846 (C, Cr, UnderLim) => {
850 (C, Cr, OverLim) => {
851 *cont = it(ss.slice(slice_start, last_end));
857 (C, Ws, OverLim) => {
858 *cont = it(ss.slice(slice_start, last_end));
861 (C, Ws, UnderLim) => {
869 ss.char_indices().advance(|x| machine(&mut cont, x));
871 // Let the automaton 'run out' by supplying trailing whitespace
872 while cont && match state { B | C => true, A => false } {
873 machine(&mut cont, (fake_i, ' '));
880 fn test_split_within() {
881 fn t(s: &str, i: uint, u: &[~str]) {
883 each_split_within(s, i, |s| { v.push(s.to_owned()); true });
884 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
888 t("hello", 15, [~"hello"]);
889 t("\nMary had a little lamb\nLittle lamb\n", 15,
890 [~"Mary had a", ~"little lamb", ~"Little lamb"]);
891 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
892 [~"Mary had a little lamb\nLittle lamb"]);
899 use std::result::{Err, Ok};
902 fn check_fail_type(f: Fail_, ft: FailType) {
904 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
905 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
906 OptionMissing(_) => assert!(ft == OptionMissing_),
907 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
908 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
915 let long_args = ~[~"--test=20"];
916 let opts = ~[reqopt("t", "test", "testing", "TEST")];
917 let rs = getopts(long_args, opts);
920 assert!(m.opt_present("test"));
921 assert_eq!(m.opt_str("test").unwrap(), ~"20");
922 assert!(m.opt_present("t"));
923 assert_eq!(m.opt_str("t").unwrap(), ~"20");
925 _ => { fail!("test_reqopt failed (long arg)"); }
927 let short_args = ~[~"-t", ~"20"];
928 match getopts(short_args, opts) {
930 assert!((m.opt_present("test")));
931 assert_eq!(m.opt_str("test").unwrap(), ~"20");
932 assert!((m.opt_present("t")));
933 assert_eq!(m.opt_str("t").unwrap(), ~"20");
935 _ => { fail!("test_reqopt failed (short arg)"); }
940 fn test_reqopt_missing() {
941 let args = ~[~"blah"];
942 let opts = ~[reqopt("t", "test", "testing", "TEST")];
943 let rs = getopts(args, opts);
945 Err(f) => check_fail_type(f, OptionMissing_),
951 fn test_reqopt_no_arg() {
952 let long_args = ~[~"--test"];
953 let opts = ~[reqopt("t", "test", "testing", "TEST")];
954 let rs = getopts(long_args, opts);
956 Err(f) => check_fail_type(f, ArgumentMissing_),
959 let short_args = ~[~"-t"];
960 match getopts(short_args, opts) {
961 Err(f) => check_fail_type(f, ArgumentMissing_),
967 fn test_reqopt_multi() {
968 let args = ~[~"--test=20", ~"-t", ~"30"];
969 let opts = ~[reqopt("t", "test", "testing", "TEST")];
970 let rs = getopts(args, opts);
972 Err(f) => check_fail_type(f, OptionDuplicated_),
980 let long_args = ~[~"--test=20"];
981 let opts = ~[optopt("t", "test", "testing", "TEST")];
982 let rs = getopts(long_args, opts);
985 assert!(m.opt_present("test"));
986 assert_eq!(m.opt_str("test").unwrap(), ~"20");
987 assert!((m.opt_present("t")));
988 assert_eq!(m.opt_str("t").unwrap(), ~"20");
992 let short_args = ~[~"-t", ~"20"];
993 match getopts(short_args, opts) {
995 assert!((m.opt_present("test")));
996 assert_eq!(m.opt_str("test").unwrap(), ~"20");
997 assert!((m.opt_present("t")));
998 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1005 fn test_optopt_missing() {
1006 let args = ~[~"blah"];
1007 let opts = ~[optopt("t", "test", "testing", "TEST")];
1008 let rs = getopts(args, opts);
1011 assert!(!m.opt_present("test"));
1012 assert!(!m.opt_present("t"));
1019 fn test_optopt_no_arg() {
1020 let long_args = ~[~"--test"];
1021 let opts = ~[optopt("t", "test", "testing", "TEST")];
1022 let rs = getopts(long_args, opts);
1024 Err(f) => check_fail_type(f, ArgumentMissing_),
1027 let short_args = ~[~"-t"];
1028 match getopts(short_args, opts) {
1029 Err(f) => check_fail_type(f, ArgumentMissing_),
1035 fn test_optopt_multi() {
1036 let args = ~[~"--test=20", ~"-t", ~"30"];
1037 let opts = ~[optopt("t", "test", "testing", "TEST")];
1038 let rs = getopts(args, opts);
1040 Err(f) => check_fail_type(f, OptionDuplicated_),
1045 // Tests for optflag
1048 let long_args = ~[~"--test"];
1049 let opts = ~[optflag("t", "test", "testing")];
1050 let rs = getopts(long_args, opts);
1053 assert!(m.opt_present("test"));
1054 assert!(m.opt_present("t"));
1058 let short_args = ~[~"-t"];
1059 match getopts(short_args, opts) {
1061 assert!(m.opt_present("test"));
1062 assert!(m.opt_present("t"));
1069 fn test_optflag_missing() {
1070 let args = ~[~"blah"];
1071 let opts = ~[optflag("t", "test", "testing")];
1072 let rs = getopts(args, opts);
1075 assert!(!m.opt_present("test"));
1076 assert!(!m.opt_present("t"));
1083 fn test_optflag_long_arg() {
1084 let args = ~[~"--test=20"];
1085 let opts = ~[optflag("t", "test", "testing")];
1086 let rs = getopts(args, opts);
1089 error!("{:?}", f.clone().to_err_msg());
1090 check_fail_type(f, UnexpectedArgument_);
1097 fn test_optflag_multi() {
1098 let args = ~[~"--test", ~"-t"];
1099 let opts = ~[optflag("t", "test", "testing")];
1100 let rs = getopts(args, opts);
1102 Err(f) => check_fail_type(f, OptionDuplicated_),
1108 fn test_optflag_short_arg() {
1109 let args = ~[~"-t", ~"20"];
1110 let opts = ~[optflag("t", "test", "testing")];
1111 let rs = getopts(args, opts);
1114 // The next variable after the flag is just a free argument
1116 assert!(m.free[0] == ~"20");
1122 // Tests for optflagmulti
1124 fn test_optflagmulti_short1() {
1125 let args = ~[~"-v"];
1126 let opts = ~[optflagmulti("v", "verbose", "verbosity")];
1127 let rs = getopts(args, opts);
1130 assert_eq!(m.opt_count("v"), 1);
1137 fn test_optflagmulti_short2a() {
1138 let args = ~[~"-v", ~"-v"];
1139 let opts = ~[optflagmulti("v", "verbose", "verbosity")];
1140 let rs = getopts(args, opts);
1143 assert_eq!(m.opt_count("v"), 2);
1150 fn test_optflagmulti_short2b() {
1151 let args = ~[~"-vv"];
1152 let opts = ~[optflagmulti("v", "verbose", "verbosity")];
1153 let rs = getopts(args, opts);
1156 assert_eq!(m.opt_count("v"), 2);
1163 fn test_optflagmulti_long1() {
1164 let args = ~[~"--verbose"];
1165 let opts = ~[optflagmulti("v", "verbose", "verbosity")];
1166 let rs = getopts(args, opts);
1169 assert_eq!(m.opt_count("verbose"), 1);
1176 fn test_optflagmulti_long2() {
1177 let args = ~[~"--verbose", ~"--verbose"];
1178 let opts = ~[optflagmulti("v", "verbose", "verbosity")];
1179 let rs = getopts(args, opts);
1182 assert_eq!(m.opt_count("verbose"), 2);
1189 fn test_optflagmulti_mix() {
1190 let args = ~[~"--verbose", ~"-v", ~"-vv", ~"verbose"];
1191 let opts = ~[optflagmulti("v", "verbose", "verbosity")];
1192 let rs = getopts(args, opts);
1195 assert_eq!(m.opt_count("verbose"), 4);
1196 assert_eq!(m.opt_count("v"), 4);
1202 // Tests for optmulti
1204 fn test_optmulti() {
1205 let long_args = ~[~"--test=20"];
1206 let opts = ~[optmulti("t", "test", "testing", "TEST")];
1207 let rs = getopts(long_args, opts);
1210 assert!((m.opt_present("test")));
1211 assert_eq!(m.opt_str("test").unwrap(), ~"20");
1212 assert!((m.opt_present("t")));
1213 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1217 let short_args = ~[~"-t", ~"20"];
1218 match getopts(short_args, opts) {
1220 assert!((m.opt_present("test")));
1221 assert_eq!(m.opt_str("test").unwrap(), ~"20");
1222 assert!((m.opt_present("t")));
1223 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1230 fn test_optmulti_missing() {
1231 let args = ~[~"blah"];
1232 let opts = ~[optmulti("t", "test", "testing", "TEST")];
1233 let rs = getopts(args, opts);
1236 assert!(!m.opt_present("test"));
1237 assert!(!m.opt_present("t"));
1244 fn test_optmulti_no_arg() {
1245 let long_args = ~[~"--test"];
1246 let opts = ~[optmulti("t", "test", "testing", "TEST")];
1247 let rs = getopts(long_args, opts);
1249 Err(f) => check_fail_type(f, ArgumentMissing_),
1252 let short_args = ~[~"-t"];
1253 match getopts(short_args, opts) {
1254 Err(f) => check_fail_type(f, ArgumentMissing_),
1260 fn test_optmulti_multi() {
1261 let args = ~[~"--test=20", ~"-t", ~"30"];
1262 let opts = ~[optmulti("t", "test", "testing", "TEST")];
1263 let rs = getopts(args, opts);
1266 assert!(m.opt_present("test"));
1267 assert_eq!(m.opt_str("test").unwrap(), ~"20");
1268 assert!(m.opt_present("t"));
1269 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1270 let pair = m.opt_strs("test");
1271 assert!(pair[0] == ~"20");
1272 assert!(pair[1] == ~"30");
1279 fn test_unrecognized_option() {
1280 let long_args = ~[~"--untest"];
1281 let opts = ~[optmulti("t", "test", "testing", "TEST")];
1282 let rs = getopts(long_args, opts);
1284 Err(f) => check_fail_type(f, UnrecognizedOption_),
1287 let short_args = ~[~"-u"];
1288 match getopts(short_args, opts) {
1289 Err(f) => check_fail_type(f, UnrecognizedOption_),
1295 fn test_combined() {
1297 ~[~"prog", ~"free1", ~"-s", ~"20", ~"free2",
1298 ~"--flag", ~"--long=30", ~"-f", ~"-m", ~"40",
1299 ~"-m", ~"50", ~"-n", ~"-A B", ~"-n", ~"-60 70"];
1301 ~[optopt("s", "something", "something", "SOMETHING"),
1302 optflag("", "flag", "a flag"),
1303 reqopt("", "long", "hi", "LONG"),
1304 optflag("f", "", "another flag"),
1305 optmulti("m", "", "mmmmmm", "YUM"),
1306 optmulti("n", "", "nothing", "NOTHING"),
1307 optopt("", "notpresent", "nothing to see here", "NOPE")];
1308 let rs = getopts(args, opts);
1311 assert!(m.free[0] == ~"prog");
1312 assert!(m.free[1] == ~"free1");
1313 assert_eq!(m.opt_str("s").unwrap(), ~"20");
1314 assert!(m.free[2] == ~"free2");
1315 assert!((m.opt_present("flag")));
1316 assert_eq!(m.opt_str("long").unwrap(), ~"30");
1317 assert!((m.opt_present("f")));
1318 let pair = m.opt_strs("m");
1319 assert!(pair[0] == ~"40");
1320 assert!(pair[1] == ~"50");
1321 let pair = m.opt_strs("n");
1322 assert!(pair[0] == ~"-A B");
1323 assert!(pair[1] == ~"-60 70");
1324 assert!((!m.opt_present("notpresent")));
1332 let opts = ~[optopt("e", "", "encrypt", "ENCRYPT"),
1333 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1334 optopt("f", "", "flag", "FLAG")];
1336 let args_single = ~[~"-e", ~"foo"];
1337 let matches_single = &match getopts(args_single, opts) {
1339 result::Err(_) => fail!()
1341 assert!(matches_single.opts_present([~"e"]));
1342 assert!(matches_single.opts_present([~"encrypt", ~"e"]));
1343 assert!(matches_single.opts_present([~"e", ~"encrypt"]));
1344 assert!(!matches_single.opts_present([~"encrypt"]));
1345 assert!(!matches_single.opts_present([~"thing"]));
1346 assert!(!matches_single.opts_present([]));
1348 assert_eq!(matches_single.opts_str([~"e"]).unwrap(), ~"foo");
1349 assert_eq!(matches_single.opts_str([~"e", ~"encrypt"]).unwrap(), ~"foo");
1350 assert_eq!(matches_single.opts_str([~"encrypt", ~"e"]).unwrap(), ~"foo");
1352 let args_both = ~[~"-e", ~"foo", ~"--encrypt", ~"foo"];
1353 let matches_both = &match getopts(args_both, opts) {
1355 result::Err(_) => fail!()
1357 assert!(matches_both.opts_present([~"e"]));
1358 assert!(matches_both.opts_present([~"encrypt"]));
1359 assert!(matches_both.opts_present([~"encrypt", ~"e"]));
1360 assert!(matches_both.opts_present([~"e", ~"encrypt"]));
1361 assert!(!matches_both.opts_present([~"f"]));
1362 assert!(!matches_both.opts_present([~"thing"]));
1363 assert!(!matches_both.opts_present([]));
1365 assert_eq!(matches_both.opts_str([~"e"]).unwrap(), ~"foo");
1366 assert_eq!(matches_both.opts_str([~"encrypt"]).unwrap(), ~"foo");
1367 assert_eq!(matches_both.opts_str([~"e", ~"encrypt"]).unwrap(), ~"foo");
1368 assert_eq!(matches_both.opts_str([~"encrypt", ~"e"]).unwrap(), ~"foo");
1373 let args = ~[~"-Lfoo", ~"-M."];
1374 let opts = ~[optmulti("L", "", "library directory", "LIB"),
1375 optmulti("M", "", "something", "MMMM")];
1376 let matches = &match getopts(args, opts) {
1378 result::Err(_) => fail!()
1380 assert!(matches.opts_present([~"L"]));
1381 assert_eq!(matches.opts_str([~"L"]).unwrap(), ~"foo");
1382 assert!(matches.opts_present([~"M"]));
1383 assert_eq!(matches.opts_str([~"M"]).unwrap(), ~".");
1388 fn test_long_to_short() {
1389 let mut short = Opt { name: Long(~"banana"),
1393 short.aliases = ~[Opt { name: Short('b'),
1397 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1399 assert!(verbose.long_to_short() == short);
1403 fn test_aliases_long_and_short() {
1405 optflagmulti("a", "apple", "Desc"),
1408 let args = ~[~"-a", ~"--apple", ~"-a"];
1410 let matches = getopts(args, opts).unwrap();
1411 assert_eq!(3, matches.opt_count("a"));
1412 assert_eq!(3, matches.opt_count("apple"));
1418 reqopt("b", "banana", "Desc", "VAL"),
1419 optopt("a", "012345678901234567890123456789",
1421 optflag("k", "kiwi", "Desc"),
1422 optflagopt("p", "", "Desc", "VAL"),
1423 optmulti("l", "", "Desc", "VAL"),
1430 -b --banana VAL Desc
1431 -a --012345678901234567890123456789 VAL
1438 let generated_usage = usage("Usage: fruits", optgroups);
1440 debug!("expected: <<{}>>", expected);
1441 debug!("generated: <<{}>>", generated_usage);
1442 assert_eq!(generated_usage, expected);
1446 fn test_usage_description_wrapping() {
1447 // indentation should be 24 spaces
1448 // lines wrap after 78: or rather descriptions wrap after 54
1451 optflag("k", "kiwi",
1452 "This is a long description which won't be wrapped..+.."), // 54
1453 optflag("a", "apple",
1454 "This is a long description which _will_ be wrapped..+.."), // 55
1461 -k --kiwi This is a long description which won't be wrapped..+..
1462 -a --apple This is a long description which _will_ be
1466 let usage = usage("Usage: fruits", optgroups);
1468 debug!("expected: <<{}>>", expected);
1469 debug!("generated: <<{}>>", usage);
1470 assert!(usage == expected)
1474 fn test_usage_description_multibyte_handling() {
1476 optflag("k", "k\u2013w\u2013",
1477 "The word kiwi is normally spelled with two i's"),
1478 optflag("a", "apple",
1479 "This \u201Cdescription\u201D has some characters that could \
1480 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."),
1487 -k --k–w– The word kiwi is normally spelled with two i's
1488 -a --apple This “description” has some characters that could
1489 confuse the line wrapping; an apple costs 0.51€ in
1490 some parts of Europe.
1493 let usage = usage("Usage: fruits", optgroups);
1495 debug!("expected: <<{}>>", expected);
1496 debug!("generated: <<{}>>", usage);
1497 assert!(usage == expected)
1501 fn test_short_usage() {
1503 reqopt("b", "banana", "Desc", "VAL"),
1504 optopt("a", "012345678901234567890123456789",
1506 optflag("k", "kiwi", "Desc"),
1507 optflagopt("p", "", "Desc", "VAL"),
1508 optmulti("l", "", "Desc", "VAL"),
1511 let expected = ~"Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..";
1512 let generated_usage = short_usage("fruits", optgroups);
1514 debug!("expected: <<{}>>", expected);
1515 debug!("generated: <<{}>>", generated_usage);
1516 assert_eq!(generated_usage, expected);