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.10"]
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};
96 /// Name of an option. Either a string or a single char.
97 #[deriving(Clone, Eq)]
99 /// A string representing the long name of an option.
100 /// For example: "help"
102 /// A char representing the short name of an option.
107 /// Describes whether an option has an argument.
108 #[deriving(Clone, Eq)]
110 /// The option requires an argument.
112 /// The option is just a flag, therefore no argument.
114 /// The option argument is optional and it could or not exist.
118 /// Describes how often an option may occur.
119 #[deriving(Clone, Eq)]
121 /// The option occurs once.
123 /// The option could or not occur.
125 /// The option occurs once or multiple times.
129 /// A description of a possible option.
130 #[deriving(Clone, Eq)]
132 /// Name of the option
134 /// Whether it has an argument
136 /// How often it can occur
138 /// Which options it aliases
139 priv aliases: Vec<Opt> ,
142 /// One group of options, e.g., both -h and --help, along with
143 /// their shared description and properties.
144 #[deriving(Clone, Eq)]
145 pub struct OptGroup {
146 /// Short Name of the `OptGroup`
148 /// Long Name of the `OptGroup`
154 /// Whether it has an argument
156 /// How often it can occur
160 /// Describes wether an option is given at all or has a value.
161 #[deriving(Clone, Eq)]
167 /// The result of checking command line arguments. Contains a vector
168 /// of matches and a vector of free strings.
169 #[deriving(Clone, Eq)]
171 /// Options that matched
172 priv opts: Vec<Opt> ,
173 /// Values of the Options that matched
174 priv vals: Vec<Vec<Optval> > ,
175 /// Free string fragments
178 /// The type returned when the command line does not conform to the
179 /// expected format. Call the `to_err_msg` method to retrieve the
180 /// error as a string.
181 #[deriving(Clone, Eq, Show)]
183 /// The option requires an argument but none was passed.
184 ArgumentMissing(~str),
185 /// The passed option is not declared among the possible options.
186 UnrecognizedOption(~str),
187 /// A required option is not present.
189 /// A single occurence option is being used multiple times.
190 OptionDuplicated(~str),
191 /// There's an argument being passed to a non-argument option.
192 UnexpectedArgument(~str),
195 /// The type of failure that occurred.
197 #[allow(missing_doc)]
206 /// The result of parsing a command line with a set of options.
207 pub type Result = result::Result<Matches, Fail_>;
210 fn from_str(nm: &str) -> Name {
212 Short(nm.char_at(0u))
218 fn to_str(&self) -> ~str {
220 Short(ch) => ch.to_str(),
221 Long(ref s) => s.to_owned()
227 /// Translate OptGroup into Opt.
228 /// (Both short and long names correspond to different Opts).
229 pub fn long_to_short(&self) -> Opt {
231 short_name: short_name,
232 long_name: long_name,
238 match (short_name.len(), long_name.len()) {
239 (0,0) => fail!("this long-format option was given no name"),
241 name: Long((long_name)),
247 name: Short(short_name.char_at(0)),
253 name: Long((long_name)),
258 name: Short(short_name.char_at(0)),
265 (_,_) => fail!("something is wrong with the long-form opt")
271 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
272 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
273 Some(id) => (*self.vals.get(id)).clone(),
274 None => fail!("No option '{}' defined", nm)
278 fn opt_val(&self, nm: &str) -> Option<Optval> {
279 let vals = self.opt_vals(nm);
283 Some((*vals.get(0)).clone())
287 /// Returns true if an option was matched.
288 pub fn opt_present(&self, nm: &str) -> bool {
289 !self.opt_vals(nm).is_empty()
292 /// Returns the number of times an option was matched.
293 pub fn opt_count(&self, nm: &str) -> uint {
294 self.opt_vals(nm).len()
297 /// Returns true if any of several options were matched.
298 pub fn opts_present(&self, names: &[~str]) -> bool {
299 for nm in names.iter() {
300 match find_opt(self.opts.as_slice(), Name::from_str(*nm)) {
301 Some(id) if !self.vals.get(id).is_empty() => return true,
308 /// Returns the string argument supplied to one of several matching options or `None`.
309 pub fn opts_str(&self, names: &[~str]) -> Option<~str> {
310 for nm in names.iter() {
311 match self.opt_val(*nm) {
312 Some(Val(ref s)) => return Some(s.clone()),
319 /// Returns a vector of the arguments provided to all matches of the given
322 /// Used when an option accepts multiple values.
323 pub fn opt_strs(&self, nm: &str) -> Vec<~str> {
324 let mut acc: Vec<~str> = Vec::new();
325 let r = self.opt_vals(nm);
328 Val(ref s) => acc.push((*s).clone()),
335 /// Returns the string argument supplied to a matching option or `None`.
336 pub fn opt_str(&self, nm: &str) -> Option<~str> {
337 let vals = self.opt_vals(nm);
342 &Val(ref s) => Some((*s).clone()),
348 /// Returns the matching string, a default, or none.
350 /// Returns none if the option was not present, `def` if the option was
351 /// present but no argument was provided, and the argument if the option was
352 /// present and an argument was provided.
353 pub fn opt_default(&self, nm: &str, def: &str) -> Option<~str> {
354 let vals = self.opt_vals(nm);
355 if vals.is_empty() { return None; }
357 &Val(ref s) => Some((*s).clone()),
358 _ => Some(def.to_owned())
364 fn is_arg(arg: &str) -> bool {
365 arg.len() > 1 && arg[0] == '-' as u8
368 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
369 // Search main options.
370 let pos = opts.iter().position(|opt| opt.name == nm);
375 // Search in aliases.
376 for candidate in opts.iter() {
377 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
378 return opts.iter().position(|opt| opt.name == candidate.name);
385 /// Create a long option that is required and takes an argument.
386 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
387 let len = short_name.len();
388 assert!(len == 1 || len == 0);
390 short_name: short_name.to_owned(),
391 long_name: long_name.to_owned(),
392 hint: hint.to_owned(),
393 desc: desc.to_owned(),
399 /// Create a long option that is optional and takes an argument.
400 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
401 let len = short_name.len();
402 assert!(len == 1 || len == 0);
404 short_name: short_name.to_owned(),
405 long_name: long_name.to_owned(),
406 hint: hint.to_owned(),
407 desc: desc.to_owned(),
413 /// Create a long option that is optional and does not take an argument.
414 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
415 let len = short_name.len();
416 assert!(len == 1 || len == 0);
418 short_name: short_name.to_owned(),
419 long_name: long_name.to_owned(),
421 desc: desc.to_owned(),
427 /// Create a long option that can occur more than once and does not
428 /// take an argument.
429 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
430 let len = short_name.len();
431 assert!(len == 1 || len == 0);
433 short_name: short_name.to_owned(),
434 long_name: long_name.to_owned(),
436 desc: desc.to_owned(),
442 /// Create a long option that is optional and takes an optional argument.
443 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
444 let len = short_name.len();
445 assert!(len == 1 || len == 0);
447 short_name: short_name.to_owned(),
448 long_name: long_name.to_owned(),
449 hint: hint.to_owned(),
450 desc: desc.to_owned(),
456 /// Create a long option that is optional, takes an argument, and may occur
458 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
459 let len = short_name.len();
460 assert!(len == 1 || len == 0);
462 short_name: short_name.to_owned(),
463 long_name: long_name.to_owned(),
464 hint: hint.to_owned(),
465 desc: desc.to_owned(),
471 /// Create a generic option group, stating all parameters explicitly
472 pub fn opt(short_name: &str,
477 occur: Occur) -> OptGroup {
478 let len = short_name.len();
479 assert!(len == 1 || len == 0);
481 short_name: short_name.to_owned(),
482 long_name: long_name.to_owned(),
483 hint: hint.to_owned(),
484 desc: desc.to_owned(),
491 /// Convert a `Fail_` enum into an error string.
492 pub fn to_err_msg(self) -> ~str {
494 ArgumentMissing(ref nm) => {
495 format!("Argument to option '{}' missing.", *nm)
497 UnrecognizedOption(ref nm) => {
498 format!("Unrecognized option: '{}'.", *nm)
500 OptionMissing(ref nm) => {
501 format!("Required option '{}' missing.", *nm)
503 OptionDuplicated(ref nm) => {
504 format!("Option '{}' given more than once.", *nm)
506 UnexpectedArgument(ref nm) => {
507 format!("Option '{}' does not take an argument.", *nm)
513 /// Parse command line arguments according to the provided options.
515 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
516 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on failure.
517 /// Use `to_err_msg` to get an error message.
518 pub fn getopts(args: &[~str], optgrps: &[OptGroup]) -> Result {
519 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
520 let n_opts = opts.len();
522 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
524 let mut vals = Vec::from_fn(n_opts, f);
525 let mut free: Vec<~str> = Vec::new();
529 let cur = args[i].clone();
530 let curlen = cur.len();
533 } else if cur == ~"--" {
535 while j < l { free.push(args[j].clone()); j += 1; }
539 let mut i_arg = None;
540 if cur[1] == '-' as u8 {
541 let tail = cur.slice(2, curlen);
542 let tail_eq: Vec<&str> = tail.split('=').collect();
543 if tail_eq.len() <= 1 {
544 names = vec!(Long(tail.to_owned()));
547 vec!(Long((*tail_eq.get(0)).to_owned()));
548 i_arg = Some((*tail_eq.get(1)).to_owned());
552 let mut last_valid_opt_id = None;
555 let range = cur.char_range_at(j);
556 let opt = Short(range.ch);
558 /* In a series of potential options (eg. -aheJ), if we
559 see one which takes an argument, we assume all
560 subsequent characters make up the argument. This
561 allows options such as -L/usr/local/lib/foo to be
562 interpreted correctly
565 match find_opt(opts.as_slice(), opt.clone()) {
566 Some(id) => last_valid_opt_id = Some(id),
569 last_valid_opt_id.is_some() &&
570 match opts.get(last_valid_opt_id.unwrap())
576 if arg_follows && j < curlen {
577 i_arg = Some(cur.slice(j, curlen).to_owned());
580 last_valid_opt_id = None;
588 let mut name_pos = 0;
589 for nm in names.iter() {
591 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
593 None => return Err(UnrecognizedOption(nm.to_str()))
595 match opts.get(optid).hasarg {
597 if !i_arg.is_none() {
598 return Err(UnexpectedArgument(nm.to_str()));
600 vals.get_mut(optid).push(Given);
603 if !i_arg.is_none() {
605 .push(Val((i_arg.clone())
607 } else if name_pos < names.len() ||
608 i + 1 == l || is_arg(args[i + 1]) {
609 vals.get_mut(optid).push(Given);
612 vals.get_mut(optid).push(Val(args[i].clone()));
616 if !i_arg.is_none() {
617 vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
618 } else if i + 1 == l {
619 return Err(ArgumentMissing(nm.to_str()));
622 vals.get_mut(optid).push(Val(args[i].clone()));
632 let n = vals.get(i).len();
633 let occ = opts.get(i).occur;
636 return Err(OptionMissing(opts.get(i).name.to_str()));
641 return Err(OptionDuplicated(opts.get(i).name.to_str()));
653 /// Derive a usage message from a set of long options.
654 pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str {
656 let desc_sep = "\n" + " ".repeat(24);
658 let mut rows = opts.iter().map(|optref| {
659 let OptGroup{short_name: short_name,
660 long_name: long_name,
664 ..} = (*optref).clone();
666 let mut row = " ".repeat(4);
669 match short_name.len() {
673 row.push_str(short_name);
676 _ => fail!("the short name should only be 1 ascii char long"),
680 match long_name.len() {
684 row.push_str(long_name);
692 Yes => row.push_str(hint),
700 // FIXME: #5516 should be graphemes not codepoints
701 // here we just need to indent the start of the description
702 let rowlen = row.char_len();
704 for _ in range(0, 24 - rowlen) {
708 row.push_str(desc_sep)
711 // Normalize desc to contain words separated by one space character
712 let mut desc_normalized_whitespace = ~"";
713 for word in desc.words() {
714 desc_normalized_whitespace.push_str(word);
715 desc_normalized_whitespace.push_char(' ');
718 // FIXME: #5516 should be graphemes not codepoints
719 let mut desc_rows = Vec::new();
720 each_split_within(desc_normalized_whitespace, 54, |substr| {
721 desc_rows.push(substr.to_owned());
725 // FIXME: #5516 should be graphemes not codepoints
726 // wrapped description
727 row.push_str(desc_rows.connect(desc_sep));
732 format!("{}\n\nOptions:\n{}\n", brief, rows.collect::<Vec<~str> >().connect("\n"))
735 fn format_option(opt: &OptGroup) -> ~str {
738 if opt.occur != Req {
742 // Use short_name is possible, but fallback to long_name.
743 if opt.short_name.len() > 0 {
745 line.push_str(opt.short_name);
748 line.push_str(opt.long_name);
751 if opt.hasarg != No {
753 if opt.hasarg == Maybe {
756 line.push_str(opt.hint);
757 if opt.hasarg == Maybe {
762 if opt.occur != Req {
765 if opt.occur == Multi {
772 /// Derive a short one-line usage summary from a set of long options.
773 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> ~str {
774 let mut line = ~"Usage: " + program_name + " ";
775 line.push_str(opts.iter().map(format_option).collect::<Vec<~str>>().connect(" "));
781 /// Splits a string into substrings with possibly internal whitespace,
782 /// each of them at most `lim` bytes long. The substrings have leading and trailing
783 /// whitespace removed, and are only cut at whitespace boundaries.
785 /// Note: Function was moved here from `std::str` because this module is the only place that
786 /// uses it, and because it was to specific for a general string function.
790 /// Fails during iteration if the string contains a non-whitespace
791 /// sequence longer than the limit.
792 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
794 // Just for fun, let's write this as a state machine:
796 enum SplitWithinState {
797 A, // leading whitespace, initial state
799 C, // internal and trailing whitespace
802 Ws, // current char is whitespace
803 Cr // current char is not whitespace
806 UnderLim, // current char makes current substring still fit in limit
807 OverLim // current char makes current substring no longer fit in limit
810 let mut slice_start = 0;
811 let mut last_start = 0;
812 let mut last_end = 0;
814 let mut fake_i = ss.len();
819 // if the limit is larger than the string, lower it to save cycles
824 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
825 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
826 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
828 state = match (state, whitespace, limit) {
830 (A, Cr, _) => { slice_start = i; last_start = i; B }
832 (B, Cr, UnderLim) => { B }
833 (B, Cr, OverLim) if (i - last_start + 1) > lim
834 => fail!("word starting with {} longer than limit!",
835 ss.slice(last_start, i + 1)),
836 (B, Cr, OverLim) => {
837 *cont = it(ss.slice(slice_start, last_end));
838 slice_start = last_start;
841 (B, Ws, UnderLim) => {
845 (B, Ws, OverLim) => {
847 *cont = it(ss.slice(slice_start, last_end));
851 (C, Cr, UnderLim) => {
855 (C, Cr, OverLim) => {
856 *cont = it(ss.slice(slice_start, last_end));
862 (C, Ws, OverLim) => {
863 *cont = it(ss.slice(slice_start, last_end));
866 (C, Ws, UnderLim) => {
874 ss.char_indices().advance(|x| machine(&mut cont, x));
876 // Let the automaton 'run out' by supplying trailing whitespace
877 while cont && match state { B | C => true, A => false } {
878 machine(&mut cont, (fake_i, ' '));
885 fn test_split_within() {
886 fn t(s: &str, i: uint, u: &[~str]) {
887 let mut v = Vec::new();
888 each_split_within(s, i, |s| { v.push(s.to_owned()); true });
889 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
893 t("hello", 15, [~"hello"]);
894 t("\nMary had a little lamb\nLittle lamb\n", 15,
895 [~"Mary had a", ~"little lamb", ~"Little lamb"]);
896 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
897 [~"Mary had a little lamb\nLittle lamb"]);
904 use std::result::{Err, Ok};
907 fn check_fail_type(f: Fail_, ft: FailType) {
909 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
910 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
911 OptionMissing(_) => assert!(ft == OptionMissing_),
912 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
913 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
920 let long_args = vec!(~"--test=20");
921 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
922 let rs = getopts(long_args.as_slice(), opts.as_slice());
925 assert!(m.opt_present("test"));
926 assert_eq!(m.opt_str("test").unwrap(), ~"20");
927 assert!(m.opt_present("t"));
928 assert_eq!(m.opt_str("t").unwrap(), ~"20");
930 _ => { fail!("test_reqopt failed (long arg)"); }
932 let short_args = vec!(~"-t", ~"20");
933 match getopts(short_args.as_slice(), opts.as_slice()) {
935 assert!((m.opt_present("test")));
936 assert_eq!(m.opt_str("test").unwrap(), ~"20");
937 assert!((m.opt_present("t")));
938 assert_eq!(m.opt_str("t").unwrap(), ~"20");
940 _ => { fail!("test_reqopt failed (short arg)"); }
945 fn test_reqopt_missing() {
946 let args = vec!(~"blah");
947 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
948 let rs = getopts(args.as_slice(), opts.as_slice());
950 Err(f) => check_fail_type(f, OptionMissing_),
956 fn test_reqopt_no_arg() {
957 let long_args = vec!(~"--test");
958 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
959 let rs = getopts(long_args.as_slice(), opts.as_slice());
961 Err(f) => check_fail_type(f, ArgumentMissing_),
964 let short_args = vec!(~"-t");
965 match getopts(short_args.as_slice(), opts.as_slice()) {
966 Err(f) => check_fail_type(f, ArgumentMissing_),
972 fn test_reqopt_multi() {
973 let args = vec!(~"--test=20", ~"-t", ~"30");
974 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
975 let rs = getopts(args.as_slice(), opts.as_slice());
977 Err(f) => check_fail_type(f, OptionDuplicated_),
985 let long_args = vec!(~"--test=20");
986 let opts = vec!(optopt("t", "test", "testing", "TEST"));
987 let rs = getopts(long_args.as_slice(), opts.as_slice());
990 assert!(m.opt_present("test"));
991 assert_eq!(m.opt_str("test").unwrap(), ~"20");
992 assert!((m.opt_present("t")));
993 assert_eq!(m.opt_str("t").unwrap(), ~"20");
997 let short_args = vec!(~"-t", ~"20");
998 match getopts(short_args.as_slice(), opts.as_slice()) {
1000 assert!((m.opt_present("test")));
1001 assert_eq!(m.opt_str("test").unwrap(), ~"20");
1002 assert!((m.opt_present("t")));
1003 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1010 fn test_optopt_missing() {
1011 let args = vec!(~"blah");
1012 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1013 let rs = getopts(args.as_slice(), opts.as_slice());
1016 assert!(!m.opt_present("test"));
1017 assert!(!m.opt_present("t"));
1024 fn test_optopt_no_arg() {
1025 let long_args = vec!(~"--test");
1026 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1027 let rs = getopts(long_args.as_slice(), opts.as_slice());
1029 Err(f) => check_fail_type(f, ArgumentMissing_),
1032 let short_args = vec!(~"-t");
1033 match getopts(short_args.as_slice(), opts.as_slice()) {
1034 Err(f) => check_fail_type(f, ArgumentMissing_),
1040 fn test_optopt_multi() {
1041 let args = vec!(~"--test=20", ~"-t", ~"30");
1042 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1043 let rs = getopts(args.as_slice(), opts.as_slice());
1045 Err(f) => check_fail_type(f, OptionDuplicated_),
1050 // Tests for optflag
1053 let long_args = vec!(~"--test");
1054 let opts = vec!(optflag("t", "test", "testing"));
1055 let rs = getopts(long_args.as_slice(), opts.as_slice());
1058 assert!(m.opt_present("test"));
1059 assert!(m.opt_present("t"));
1063 let short_args = vec!(~"-t");
1064 match getopts(short_args.as_slice(), opts.as_slice()) {
1066 assert!(m.opt_present("test"));
1067 assert!(m.opt_present("t"));
1074 fn test_optflag_missing() {
1075 let args = vec!(~"blah");
1076 let opts = vec!(optflag("t", "test", "testing"));
1077 let rs = getopts(args.as_slice(), opts.as_slice());
1080 assert!(!m.opt_present("test"));
1081 assert!(!m.opt_present("t"));
1088 fn test_optflag_long_arg() {
1089 let args = vec!(~"--test=20");
1090 let opts = vec!(optflag("t", "test", "testing"));
1091 let rs = getopts(args.as_slice(), opts.as_slice());
1094 error!("{:?}", f.clone().to_err_msg());
1095 check_fail_type(f, UnexpectedArgument_);
1102 fn test_optflag_multi() {
1103 let args = vec!(~"--test", ~"-t");
1104 let opts = vec!(optflag("t", "test", "testing"));
1105 let rs = getopts(args.as_slice(), opts.as_slice());
1107 Err(f) => check_fail_type(f, OptionDuplicated_),
1113 fn test_optflag_short_arg() {
1114 let args = vec!(~"-t", ~"20");
1115 let opts = vec!(optflag("t", "test", "testing"));
1116 let rs = getopts(args.as_slice(), opts.as_slice());
1119 // The next variable after the flag is just a free argument
1121 assert!(*m.free.get(0) == ~"20");
1127 // Tests for optflagmulti
1129 fn test_optflagmulti_short1() {
1130 let args = vec!(~"-v");
1131 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1132 let rs = getopts(args.as_slice(), opts.as_slice());
1135 assert_eq!(m.opt_count("v"), 1);
1142 fn test_optflagmulti_short2a() {
1143 let args = vec!(~"-v", ~"-v");
1144 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1145 let rs = getopts(args.as_slice(), opts.as_slice());
1148 assert_eq!(m.opt_count("v"), 2);
1155 fn test_optflagmulti_short2b() {
1156 let args = vec!(~"-vv");
1157 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1158 let rs = getopts(args.as_slice(), opts.as_slice());
1161 assert_eq!(m.opt_count("v"), 2);
1168 fn test_optflagmulti_long1() {
1169 let args = vec!(~"--verbose");
1170 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1171 let rs = getopts(args.as_slice(), opts.as_slice());
1174 assert_eq!(m.opt_count("verbose"), 1);
1181 fn test_optflagmulti_long2() {
1182 let args = vec!(~"--verbose", ~"--verbose");
1183 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1184 let rs = getopts(args.as_slice(), opts.as_slice());
1187 assert_eq!(m.opt_count("verbose"), 2);
1194 fn test_optflagmulti_mix() {
1195 let args = vec!(~"--verbose", ~"-v", ~"-vv", ~"verbose");
1196 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1197 let rs = getopts(args.as_slice(), opts.as_slice());
1200 assert_eq!(m.opt_count("verbose"), 4);
1201 assert_eq!(m.opt_count("v"), 4);
1207 // Tests for optmulti
1209 fn test_optmulti() {
1210 let long_args = vec!(~"--test=20");
1211 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1212 let rs = getopts(long_args.as_slice(), opts.as_slice());
1215 assert!((m.opt_present("test")));
1216 assert_eq!(m.opt_str("test").unwrap(), ~"20");
1217 assert!((m.opt_present("t")));
1218 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1222 let short_args = vec!(~"-t", ~"20");
1223 match getopts(short_args.as_slice(), opts.as_slice()) {
1225 assert!((m.opt_present("test")));
1226 assert_eq!(m.opt_str("test").unwrap(), ~"20");
1227 assert!((m.opt_present("t")));
1228 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1235 fn test_optmulti_missing() {
1236 let args = vec!(~"blah");
1237 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1238 let rs = getopts(args.as_slice(), opts.as_slice());
1241 assert!(!m.opt_present("test"));
1242 assert!(!m.opt_present("t"));
1249 fn test_optmulti_no_arg() {
1250 let long_args = vec!(~"--test");
1251 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1252 let rs = getopts(long_args.as_slice(), opts.as_slice());
1254 Err(f) => check_fail_type(f, ArgumentMissing_),
1257 let short_args = vec!(~"-t");
1258 match getopts(short_args.as_slice(), opts.as_slice()) {
1259 Err(f) => check_fail_type(f, ArgumentMissing_),
1265 fn test_optmulti_multi() {
1266 let args = vec!(~"--test=20", ~"-t", ~"30");
1267 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1268 let rs = getopts(args.as_slice(), opts.as_slice());
1271 assert!(m.opt_present("test"));
1272 assert_eq!(m.opt_str("test").unwrap(), ~"20");
1273 assert!(m.opt_present("t"));
1274 assert_eq!(m.opt_str("t").unwrap(), ~"20");
1275 let pair = m.opt_strs("test");
1276 assert!(*pair.get(0) == ~"20");
1277 assert!(*pair.get(1) == ~"30");
1284 fn test_unrecognized_option() {
1285 let long_args = vec!(~"--untest");
1286 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1287 let rs = getopts(long_args.as_slice(), opts.as_slice());
1289 Err(f) => check_fail_type(f, UnrecognizedOption_),
1292 let short_args = vec!(~"-u");
1293 match getopts(short_args.as_slice(), opts.as_slice()) {
1294 Err(f) => check_fail_type(f, UnrecognizedOption_),
1300 fn test_combined() {
1302 vec!(~"prog", ~"free1", ~"-s", ~"20", ~"free2",
1303 ~"--flag", ~"--long=30", ~"-f", ~"-m", ~"40",
1304 ~"-m", ~"50", ~"-n", ~"-A B", ~"-n", ~"-60 70");
1306 vec!(optopt("s", "something", "something", "SOMETHING"),
1307 optflag("", "flag", "a flag"),
1308 reqopt("", "long", "hi", "LONG"),
1309 optflag("f", "", "another flag"),
1310 optmulti("m", "", "mmmmmm", "YUM"),
1311 optmulti("n", "", "nothing", "NOTHING"),
1312 optopt("", "notpresent", "nothing to see here", "NOPE"));
1313 let rs = getopts(args.as_slice(), opts.as_slice());
1316 assert!(*m.free.get(0) == ~"prog");
1317 assert!(*m.free.get(1) == ~"free1");
1318 assert_eq!(m.opt_str("s").unwrap(), ~"20");
1319 assert!(*m.free.get(2) == ~"free2");
1320 assert!((m.opt_present("flag")));
1321 assert_eq!(m.opt_str("long").unwrap(), ~"30");
1322 assert!((m.opt_present("f")));
1323 let pair = m.opt_strs("m");
1324 assert!(*pair.get(0) == ~"40");
1325 assert!(*pair.get(1) == ~"50");
1326 let pair = m.opt_strs("n");
1327 assert!(*pair.get(0) == ~"-A B");
1328 assert!(*pair.get(1) == ~"-60 70");
1329 assert!((!m.opt_present("notpresent")));
1337 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1338 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1339 optopt("f", "", "flag", "FLAG"));
1341 let args_single = vec!(~"-e", ~"foo");
1342 let matches_single = &match getopts(args_single.as_slice(),
1345 result::Err(_) => fail!()
1347 assert!(matches_single.opts_present([~"e"]));
1348 assert!(matches_single.opts_present([~"encrypt", ~"e"]));
1349 assert!(matches_single.opts_present([~"e", ~"encrypt"]));
1350 assert!(!matches_single.opts_present([~"encrypt"]));
1351 assert!(!matches_single.opts_present([~"thing"]));
1352 assert!(!matches_single.opts_present([]));
1354 assert_eq!(matches_single.opts_str([~"e"]).unwrap(), ~"foo");
1355 assert_eq!(matches_single.opts_str([~"e", ~"encrypt"]).unwrap(), ~"foo");
1356 assert_eq!(matches_single.opts_str([~"encrypt", ~"e"]).unwrap(), ~"foo");
1358 let args_both = vec!(~"-e", ~"foo", ~"--encrypt", ~"foo");
1359 let matches_both = &match getopts(args_both.as_slice(),
1362 result::Err(_) => fail!()
1364 assert!(matches_both.opts_present([~"e"]));
1365 assert!(matches_both.opts_present([~"encrypt"]));
1366 assert!(matches_both.opts_present([~"encrypt", ~"e"]));
1367 assert!(matches_both.opts_present([~"e", ~"encrypt"]));
1368 assert!(!matches_both.opts_present([~"f"]));
1369 assert!(!matches_both.opts_present([~"thing"]));
1370 assert!(!matches_both.opts_present([]));
1372 assert_eq!(matches_both.opts_str([~"e"]).unwrap(), ~"foo");
1373 assert_eq!(matches_both.opts_str([~"encrypt"]).unwrap(), ~"foo");
1374 assert_eq!(matches_both.opts_str([~"e", ~"encrypt"]).unwrap(), ~"foo");
1375 assert_eq!(matches_both.opts_str([~"encrypt", ~"e"]).unwrap(), ~"foo");
1380 let args = vec!(~"-Lfoo", ~"-M.");
1381 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1382 optmulti("M", "", "something", "MMMM"));
1383 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1385 result::Err(_) => fail!()
1387 assert!(matches.opts_present([~"L"]));
1388 assert_eq!(matches.opts_str([~"L"]).unwrap(), ~"foo");
1389 assert!(matches.opts_present([~"M"]));
1390 assert_eq!(matches.opts_str([~"M"]).unwrap(), ~".");
1395 fn test_long_to_short() {
1396 let mut short = Opt {
1397 name: Long(~"banana"),
1400 aliases: Vec::new(),
1402 short.aliases = vec!(Opt { name: Short('b'),
1405 aliases: Vec::new() });
1406 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1408 assert!(verbose.long_to_short() == short);
1412 fn test_aliases_long_and_short() {
1414 optflagmulti("a", "apple", "Desc"));
1416 let args = vec!(~"-a", ~"--apple", ~"-a");
1418 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1419 assert_eq!(3, matches.opt_count("a"));
1420 assert_eq!(3, matches.opt_count("apple"));
1425 let optgroups = vec!(
1426 reqopt("b", "banana", "Desc", "VAL"),
1427 optopt("a", "012345678901234567890123456789",
1429 optflag("k", "kiwi", "Desc"),
1430 optflagopt("p", "", "Desc", "VAL"),
1431 optmulti("l", "", "Desc", "VAL"));
1437 -b --banana VAL Desc
1438 -a --012345678901234567890123456789 VAL
1445 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1447 debug!("expected: <<{}>>", expected);
1448 debug!("generated: <<{}>>", generated_usage);
1449 assert_eq!(generated_usage, expected);
1453 fn test_usage_description_wrapping() {
1454 // indentation should be 24 spaces
1455 // lines wrap after 78: or rather descriptions wrap after 54
1457 let optgroups = vec!(
1458 optflag("k", "kiwi",
1459 "This is a long description which won't be wrapped..+.."), // 54
1460 optflag("a", "apple",
1461 "This is a long description which _will_ be wrapped..+.."));
1467 -k --kiwi This is a long description which won't be wrapped..+..
1468 -a --apple This is a long description which _will_ be
1472 let usage = usage("Usage: fruits", optgroups.as_slice());
1474 debug!("expected: <<{}>>", expected);
1475 debug!("generated: <<{}>>", usage);
1476 assert!(usage == expected)
1480 fn test_usage_description_multibyte_handling() {
1481 let optgroups = vec!(
1482 optflag("k", "k\u2013w\u2013",
1483 "The word kiwi is normally spelled with two i's"),
1484 optflag("a", "apple",
1485 "This \u201Cdescription\u201D has some characters that could \
1486 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1492 -k --k–w– The word kiwi is normally spelled with two i's
1493 -a --apple This “description” has some characters that could
1494 confuse the line wrapping; an apple costs 0.51€ in
1495 some parts of Europe.
1498 let usage = usage("Usage: fruits", optgroups.as_slice());
1500 debug!("expected: <<{}>>", expected);
1501 debug!("generated: <<{}>>", usage);
1502 assert!(usage == expected)
1506 fn test_short_usage() {
1507 let optgroups = vec!(
1508 reqopt("b", "banana", "Desc", "VAL"),
1509 optopt("a", "012345678901234567890123456789",
1511 optflag("k", "kiwi", "Desc"),
1512 optflagopt("p", "", "Desc", "VAL"),
1513 optmulti("l", "", "Desc", "VAL"));
1515 let expected = ~"Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..";
1516 let generated_usage = short_usage("fruits", optgroups.as_slice());
1518 debug!("expected: <<{}>>", expected);
1519 debug!("generated: <<{}>>", generated_usage);
1520 assert_eq!(generated_usage, expected);