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