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 // ignore-lexer-test FIXME #15677
13 //! Simple getopt alternative.
15 //! Construct a vector of options, either by using `reqopt`, `optopt`, and `optflag`
16 //! or by building them from components yourself, and pass them to `getopts`,
17 //! along with a vector of actual arguments (not including `argv[0]`). You'll
18 //! either get a failure code back, or a match. You'll have to verify whether
19 //! the amount of 'free' arguments in the match is what you expect. Use `opt_*`
20 //! accessors to get argument values out of the matches object.
22 //! Single-character options are expected to appear on the command line with a
23 //! single preceding dash; multiple-character options are expected to be
24 //! proceeded by two dashes. Options that expect an argument accept their
25 //! argument following either a space or an equals sign. Single-character
26 //! options don't require the space.
30 //! The following example shows simple command line parsing for an application
31 //! that requires an input file to be specified, accepts an optional output
32 //! file name following `-o`, and accepts both `-h` and `--help` as optional flags.
35 //! extern crate getopts;
36 //! use getopts::{optopt,optflag,getopts,OptGroup};
39 //! fn do_work(inp: &str, out: Option<String>) {
40 //! println!("{}", inp);
42 //! Some(x) => println!("{}", x),
43 //! None => println!("No Output"),
47 //! fn print_usage(program: &str, _opts: &[OptGroup]) {
48 //! println!("Usage: {} [options]", program);
49 //! println!("-o\t\tOutput");
50 //! println!("-h --help\tUsage");
54 //! let args: Vec<String> = os::args();
56 //! let program = args[0].clone();
59 //! optopt("o", "", "set output file name", "NAME"),
60 //! optflag("h", "help", "print this help menu")
62 //! let matches = match getopts(args.tail(), opts) {
64 //! Err(f) => { fail!(f.to_string()) }
66 //! if matches.opt_present("h") {
67 //! print_usage(program.as_slice(), opts);
70 //! let output = matches.opt_str("o");
71 //! let input = if !matches.free.is_empty() {
72 //! matches.free[0].clone()
74 //! print_usage(program.as_slice(), opts);
77 //! do_work(input.as_slice(), output);
81 #![crate_name = "getopts"]
83 #![crate_type = "rlib"]
84 #![crate_type = "dylib"]
85 #![license = "MIT/ASL2"]
86 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
87 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
88 html_root_url = "http://doc.rust-lang.org/nightly/",
89 html_playground_url = "http://play.rust-lang.org/")]
90 #![feature(globs, phase)]
91 #![feature(import_shadowing)]
94 #[cfg(test)] #[phase(plugin, link)] extern crate log;
96 use std::cmp::PartialEq;
98 use std::result::{Err, Ok};
100 use std::string::String;
102 /// Name of an option. Either a string or a single char.
103 #[deriving(Clone, PartialEq, Eq)]
105 /// A string representing the long name of an option.
106 /// For example: "help"
108 /// A char representing the short name of an option.
113 /// Describes whether an option has an argument.
114 #[deriving(Clone, PartialEq, Eq)]
116 /// The option requires an argument.
118 /// The option takes no argument.
120 /// The option argument is optional.
124 /// Describes how often an option may occur.
125 #[deriving(Clone, PartialEq, Eq)]
127 /// The option occurs once.
129 /// The option occurs at most once.
131 /// The option occurs zero or more times.
135 /// A description of a possible option.
136 #[deriving(Clone, PartialEq, Eq)]
138 /// Name of the option
140 /// Whether it has an argument
142 /// How often it can occur
144 /// Which options it aliases
145 pub aliases: Vec<Opt>,
148 /// One group of options, e.g., both -h and --help, along with
149 /// their shared description and properties.
150 #[deriving(Clone, PartialEq, Eq)]
151 pub struct OptGroup {
152 /// Short Name of the `OptGroup`
153 pub short_name: String,
154 /// Long Name of the `OptGroup`
155 pub long_name: String,
160 /// Whether it has an argument
162 /// How often it can occur
166 /// Describes whether an option is given at all or has a value.
167 #[deriving(Clone, PartialEq, Eq)]
173 /// The result of checking command line arguments. Contains a vector
174 /// of matches and a vector of free strings.
175 #[deriving(Clone, PartialEq, Eq)]
177 /// Options that matched
179 /// Values of the Options that matched
180 vals: Vec<Vec<Optval>>,
181 /// Free string fragments
182 pub free: Vec<String>,
185 /// The type returned when the command line does not conform to the
186 /// expected format. Use the `Show` implementation to output detailed
188 #[deriving(Clone, PartialEq, Eq)]
190 /// The option requires an argument but none was passed.
191 ArgumentMissing(String),
192 /// The passed option is not declared among the possible options.
193 UnrecognizedOption(String),
194 /// A required option is not present.
195 OptionMissing(String),
196 /// A single occurrence option is being used multiple times.
197 OptionDuplicated(String),
198 /// There's an argument being passed to a non-argument option.
199 UnexpectedArgument(String),
202 /// The type of failure that occurred.
203 #[deriving(PartialEq, Eq)]
204 #[allow(missing_doc)]
213 /// The result of parsing a command line with a set of options.
214 pub type Result = result::Result<Matches, Fail_>;
217 fn from_str(nm: &str) -> Name {
219 Short(nm.char_at(0u))
225 fn to_string(&self) -> String {
227 Short(ch) => ch.to_string(),
228 Long(ref s) => s.to_string()
234 /// Translate OptGroup into Opt.
235 /// (Both short and long names correspond to different Opts).
236 pub fn long_to_short(&self) -> Opt {
245 match (short_name.len(), long_name.len()) {
246 (0,0) => fail!("this long-format option was given no name"),
248 name: Long((long_name)),
254 name: Short(short_name.as_slice().char_at(0)),
260 name: Long((long_name)),
265 name: Short(short_name.as_slice().char_at(0)),
272 (_,_) => fail!("something is wrong with the long-form opt")
278 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
279 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
280 Some(id) => self.vals[id].clone(),
281 None => fail!("No option '{}' defined", nm)
285 fn opt_val(&self, nm: &str) -> Option<Optval> {
286 let vals = self.opt_vals(nm);
290 Some(vals[0].clone())
294 /// Returns true if an option was matched.
295 pub fn opt_present(&self, nm: &str) -> bool {
296 !self.opt_vals(nm).is_empty()
299 /// Returns the number of times an option was matched.
300 pub fn opt_count(&self, nm: &str) -> uint {
301 self.opt_vals(nm).len()
304 /// Returns true if any of several options were matched.
305 pub fn opts_present(&self, names: &[String]) -> bool {
306 for nm in names.iter() {
307 match find_opt(self.opts.as_slice(),
308 Name::from_str(nm.as_slice())) {
309 Some(id) if !self.vals[id].is_empty() => return true,
316 /// Returns the string argument supplied to one of several matching options or `None`.
317 pub fn opts_str(&self, names: &[String]) -> Option<String> {
318 for nm in names.iter() {
319 match self.opt_val(nm.as_slice()) {
320 Some(Val(ref s)) => return Some(s.clone()),
327 /// Returns a vector of the arguments provided to all matches of the given
330 /// Used when an option accepts multiple values.
331 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
332 let mut acc: Vec<String> = Vec::new();
333 let r = self.opt_vals(nm);
336 Val(ref s) => acc.push((*s).clone()),
343 /// Returns the string argument supplied to a matching option or `None`.
344 pub fn opt_str(&self, nm: &str) -> Option<String> {
345 let vals = self.opt_vals(nm);
347 return None::<String>;
350 Val(ref s) => Some((*s).clone()),
356 /// Returns the matching string, a default, or none.
358 /// Returns none if the option was not present, `def` if the option was
359 /// present but no argument was provided, and the argument if the option was
360 /// present and an argument was provided.
361 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
362 let vals = self.opt_vals(nm);
367 Val(ref s) => Some((*s).clone()),
368 _ => Some(def.to_string())
374 fn is_arg(arg: &str) -> bool {
375 arg.len() > 1 && arg.as_bytes()[0] == b'-'
378 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
379 // Search main options.
380 let pos = opts.iter().position(|opt| opt.name == nm);
385 // Search in aliases.
386 for candidate in opts.iter() {
387 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
388 return opts.iter().position(|opt| opt.name == candidate.name);
395 /// Create a long option that is required and takes an argument.
396 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
397 let len = short_name.len();
398 assert!(len == 1 || len == 0);
400 short_name: short_name.to_string(),
401 long_name: long_name.to_string(),
402 hint: hint.to_string(),
403 desc: desc.to_string(),
409 /// Create a long option that is optional and takes an argument.
410 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
411 let len = short_name.len();
412 assert!(len == 1 || len == 0);
414 short_name: short_name.to_string(),
415 long_name: long_name.to_string(),
416 hint: hint.to_string(),
417 desc: desc.to_string(),
423 /// Create a long option that is optional and does not take an argument.
424 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
425 let len = short_name.len();
426 assert!(len == 1 || len == 0);
428 short_name: short_name.to_string(),
429 long_name: long_name.to_string(),
430 hint: "".to_string(),
431 desc: desc.to_string(),
437 /// Create a long option that can occur more than once and does not
438 /// take an argument.
439 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
440 let len = short_name.len();
441 assert!(len == 1 || len == 0);
443 short_name: short_name.to_string(),
444 long_name: long_name.to_string(),
445 hint: "".to_string(),
446 desc: desc.to_string(),
452 /// Create a long option that is optional and takes an optional argument.
453 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
454 let len = short_name.len();
455 assert!(len == 1 || len == 0);
457 short_name: short_name.to_string(),
458 long_name: long_name.to_string(),
459 hint: hint.to_string(),
460 desc: desc.to_string(),
466 /// Create a long option that is optional, takes an argument, and may occur
468 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
469 let len = short_name.len();
470 assert!(len == 1 || len == 0);
472 short_name: short_name.to_string(),
473 long_name: long_name.to_string(),
474 hint: hint.to_string(),
475 desc: desc.to_string(),
481 /// Create a generic option group, stating all parameters explicitly
482 pub fn opt(short_name: &str,
487 occur: Occur) -> OptGroup {
488 let len = short_name.len();
489 assert!(len == 1 || len == 0);
491 short_name: short_name.to_string(),
492 long_name: long_name.to_string(),
493 hint: hint.to_string(),
494 desc: desc.to_string(),
501 /// Convert a `Fail_` enum into an error string.
502 #[deprecated="use `Show` (`{}` format specifier)"]
503 pub fn to_err_msg(self) -> String {
508 impl fmt::Show for Fail_ {
509 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
511 ArgumentMissing(ref nm) => {
512 write!(f, "Argument to option '{}' missing.", *nm)
514 UnrecognizedOption(ref nm) => {
515 write!(f, "Unrecognized option: '{}'.", *nm)
517 OptionMissing(ref nm) => {
518 write!(f, "Required option '{}' missing.", *nm)
520 OptionDuplicated(ref nm) => {
521 write!(f, "Option '{}' given more than once.", *nm)
523 UnexpectedArgument(ref nm) => {
524 write!(f, "Option '{}' does not take an argument.", *nm)
530 /// Parse command line arguments according to the provided options.
532 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
533 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on
534 /// failure: use the `Show` implementation of `Fail_` to display
535 /// information about it.
536 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
537 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
538 let n_opts = opts.len();
540 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
542 let mut vals = Vec::from_fn(n_opts, f);
543 let mut free: Vec<String> = Vec::new();
547 let cur = args[i].clone();
548 let curlen = cur.len();
549 if !is_arg(cur.as_slice()) {
551 } else if cur.as_slice() == "--" {
553 while j < l { free.push(args[j].clone()); j += 1; }
557 let mut i_arg = None;
558 if cur.as_bytes()[1] == b'-' {
559 let tail = cur.as_slice().slice(2, curlen);
560 let tail_eq: Vec<&str> = tail.split('=').collect();
561 if tail_eq.len() <= 1 {
562 names = vec!(Long(tail.to_string()));
565 vec!(Long(tail_eq[0].to_string()));
566 i_arg = Some(tail_eq[1].to_string());
572 let range = cur.as_slice().char_range_at(j);
573 let opt = Short(range.ch);
575 /* In a series of potential options (eg. -aheJ), if we
576 see one which takes an argument, we assume all
577 subsequent characters make up the argument. This
578 allows options such as -L/usr/local/lib/foo to be
579 interpreted correctly
582 let opt_id = match find_opt(opts.as_slice(), opt.clone()) {
584 None => return Err(UnrecognizedOption(opt.to_string()))
589 let arg_follows = match opts[opt_id].hasarg {
594 if arg_follows && range.next < curlen {
595 i_arg = Some(cur.as_slice()
596 .slice(range.next, curlen).to_string());
603 let mut name_pos = 0;
604 for nm in names.iter() {
606 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
608 None => return Err(UnrecognizedOption(nm.to_string()))
610 match opts[optid].hasarg {
612 if name_pos == names.len() && !i_arg.is_none() {
613 return Err(UnexpectedArgument(nm.to_string()));
615 vals.get_mut(optid).push(Given);
618 if !i_arg.is_none() {
620 .push(Val((i_arg.clone())
622 } else if name_pos < names.len() || i + 1 == l ||
623 is_arg(args[i + 1].as_slice()) {
624 vals.get_mut(optid).push(Given);
627 vals.get_mut(optid).push(Val(args[i].clone()));
631 if !i_arg.is_none() {
632 vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
633 } else if i + 1 == l {
634 return Err(ArgumentMissing(nm.to_string()));
637 vals.get_mut(optid).push(Val(args[i].clone()));
647 let n = vals[i].len();
648 let occ = opts[i].occur;
651 return Err(OptionMissing(opts[i].name.to_string()));
656 return Err(OptionDuplicated(opts[i].name.to_string()));
668 /// Derive a usage message from a set of long options.
669 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
671 let desc_sep = format!("\n{}", " ".repeat(24));
673 let mut rows = opts.iter().map(|optref| {
674 let OptGroup{short_name,
679 ..} = (*optref).clone();
681 let mut row = " ".repeat(4);
684 match short_name.len() {
688 row.push_str(short_name.as_slice());
691 _ => fail!("the short name should only be 1 ascii char long"),
695 match long_name.len() {
699 row.push_str(long_name.as_slice());
707 Yes => row.push_str(hint.as_slice()),
710 row.push_str(hint.as_slice());
715 // FIXME: #5516 should be graphemes not codepoints
716 // here we just need to indent the start of the description
717 let rowlen = row.as_slice().char_len();
719 for _ in range(0, 24 - rowlen) {
723 row.push_str(desc_sep.as_slice())
726 // Normalize desc to contain words separated by one space character
727 let mut desc_normalized_whitespace = String::new();
728 for word in desc.as_slice().words() {
729 desc_normalized_whitespace.push_str(word);
730 desc_normalized_whitespace.push(' ');
733 // FIXME: #5516 should be graphemes not codepoints
734 let mut desc_rows = Vec::new();
735 each_split_within(desc_normalized_whitespace.as_slice(),
738 desc_rows.push(substr.to_string());
742 // FIXME: #5516 should be graphemes not codepoints
743 // wrapped description
744 row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
749 format!("{}\n\nOptions:\n{}\n", brief,
750 rows.collect::<Vec<String>>().connect("\n"))
753 fn format_option(opt: &OptGroup) -> String {
754 let mut line = String::new();
756 if opt.occur != Req {
760 // Use short_name is possible, but fallback to long_name.
761 if opt.short_name.len() > 0 {
763 line.push_str(opt.short_name.as_slice());
766 line.push_str(opt.long_name.as_slice());
769 if opt.hasarg != No {
771 if opt.hasarg == Maybe {
774 line.push_str(opt.hint.as_slice());
775 if opt.hasarg == Maybe {
780 if opt.occur != Req {
783 if opt.occur == Multi {
790 /// Derive a short one-line usage summary from a set of long options.
791 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
792 let mut line = format!("Usage: {} ", program_name);
793 line.push_str(opts.iter()
795 .collect::<Vec<String>>()
802 /// Splits a string into substrings with possibly internal whitespace,
803 /// each of them at most `lim` bytes long. The substrings have leading and trailing
804 /// whitespace removed, and are only cut at whitespace boundaries.
806 /// Note: Function was moved here from `std::str` because this module is the only place that
807 /// uses it, and because it was too specific for a general string function.
811 /// Fails during iteration if the string contains a non-whitespace
812 /// sequence longer than the limit.
813 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
815 // Just for fun, let's write this as a state machine:
817 enum SplitWithinState {
818 A, // leading whitespace, initial state
820 C, // internal and trailing whitespace
823 Ws, // current char is whitespace
824 Cr // current char is not whitespace
827 UnderLim, // current char makes current substring still fit in limit
828 OverLim // current char makes current substring no longer fit in limit
831 let mut slice_start = 0;
832 let mut last_start = 0;
833 let mut last_end = 0;
835 let mut fake_i = ss.len();
840 // if the limit is larger than the string, lower it to save cycles
845 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
846 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
847 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
849 state = match (state, whitespace, limit) {
851 (A, Cr, _) => { slice_start = i; last_start = i; B }
853 (B, Cr, UnderLim) => { B }
854 (B, Cr, OverLim) if (i - last_start + 1) > lim
855 => fail!("word starting with {} longer than limit!",
856 ss.slice(last_start, i + 1)),
857 (B, Cr, OverLim) => {
858 *cont = it(ss.slice(slice_start, last_end));
859 slice_start = last_start;
862 (B, Ws, UnderLim) => {
866 (B, Ws, OverLim) => {
868 *cont = it(ss.slice(slice_start, last_end));
872 (C, Cr, UnderLim) => {
876 (C, Cr, OverLim) => {
877 *cont = it(ss.slice(slice_start, last_end));
883 (C, Ws, OverLim) => {
884 *cont = it(ss.slice(slice_start, last_end));
887 (C, Ws, UnderLim) => {
895 ss.char_indices().all(|x| machine(&mut cont, x));
897 // Let the automaton 'run out' by supplying trailing whitespace
898 while cont && match state { B | C => true, A => false } {
899 machine(&mut cont, (fake_i, ' '));
906 fn test_split_within() {
907 fn t(s: &str, i: uint, u: &[String]) {
908 let mut v = Vec::new();
909 each_split_within(s, i, |s| { v.push(s.to_string()); true });
910 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
914 t("hello", 15, ["hello".to_string()]);
915 t("\nMary had a little lamb\nLittle lamb\n", 15, [
916 "Mary had a".to_string(),
917 "little lamb".to_string(),
918 "Little lamb".to_string()
920 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
921 ["Mary had a little lamb\nLittle lamb".to_string()]);
928 use std::result::{Err, Ok};
931 fn check_fail_type(f: Fail_, ft: FailType) {
933 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
934 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
935 OptionMissing(_) => assert!(ft == OptionMissing_),
936 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
937 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
944 let long_args = vec!("--test=20".to_string());
945 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
946 let rs = getopts(long_args.as_slice(), opts.as_slice());
949 assert!(m.opt_present("test"));
950 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
951 assert!(m.opt_present("t"));
952 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
954 _ => { fail!("test_reqopt failed (long arg)"); }
956 let short_args = vec!("-t".to_string(), "20".to_string());
957 match getopts(short_args.as_slice(), opts.as_slice()) {
959 assert!((m.opt_present("test")));
960 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
961 assert!((m.opt_present("t")));
962 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
964 _ => { fail!("test_reqopt failed (short arg)"); }
969 fn test_reqopt_missing() {
970 let args = vec!("blah".to_string());
971 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
972 let rs = getopts(args.as_slice(), opts.as_slice());
974 Err(f) => check_fail_type(f, OptionMissing_),
980 fn test_reqopt_no_arg() {
981 let long_args = vec!("--test".to_string());
982 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
983 let rs = getopts(long_args.as_slice(), opts.as_slice());
985 Err(f) => check_fail_type(f, ArgumentMissing_),
988 let short_args = vec!("-t".to_string());
989 match getopts(short_args.as_slice(), opts.as_slice()) {
990 Err(f) => check_fail_type(f, ArgumentMissing_),
996 fn test_reqopt_multi() {
997 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
998 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
999 let rs = getopts(args.as_slice(), opts.as_slice());
1001 Err(f) => check_fail_type(f, OptionDuplicated_),
1009 let long_args = vec!("--test=20".to_string());
1010 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1011 let rs = getopts(long_args.as_slice(), opts.as_slice());
1014 assert!(m.opt_present("test"));
1015 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1016 assert!((m.opt_present("t")));
1017 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1021 let short_args = vec!("-t".to_string(), "20".to_string());
1022 match getopts(short_args.as_slice(), opts.as_slice()) {
1024 assert!((m.opt_present("test")));
1025 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1026 assert!((m.opt_present("t")));
1027 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1034 fn test_optopt_missing() {
1035 let args = vec!("blah".to_string());
1036 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1037 let rs = getopts(args.as_slice(), opts.as_slice());
1040 assert!(!m.opt_present("test"));
1041 assert!(!m.opt_present("t"));
1048 fn test_optopt_no_arg() {
1049 let long_args = vec!("--test".to_string());
1050 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1051 let rs = getopts(long_args.as_slice(), opts.as_slice());
1053 Err(f) => check_fail_type(f, ArgumentMissing_),
1056 let short_args = vec!("-t".to_string());
1057 match getopts(short_args.as_slice(), opts.as_slice()) {
1058 Err(f) => check_fail_type(f, ArgumentMissing_),
1064 fn test_optopt_multi() {
1065 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1066 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1067 let rs = getopts(args.as_slice(), opts.as_slice());
1069 Err(f) => check_fail_type(f, OptionDuplicated_),
1074 // Tests for optflag
1077 let long_args = vec!("--test".to_string());
1078 let opts = vec!(optflag("t", "test", "testing"));
1079 let rs = getopts(long_args.as_slice(), opts.as_slice());
1082 assert!(m.opt_present("test"));
1083 assert!(m.opt_present("t"));
1087 let short_args = vec!("-t".to_string());
1088 match getopts(short_args.as_slice(), opts.as_slice()) {
1090 assert!(m.opt_present("test"));
1091 assert!(m.opt_present("t"));
1098 fn test_optflag_missing() {
1099 let args = vec!("blah".to_string());
1100 let opts = vec!(optflag("t", "test", "testing"));
1101 let rs = getopts(args.as_slice(), opts.as_slice());
1104 assert!(!m.opt_present("test"));
1105 assert!(!m.opt_present("t"));
1112 fn test_optflag_long_arg() {
1113 let args = vec!("--test=20".to_string());
1114 let opts = vec!(optflag("t", "test", "testing"));
1115 let rs = getopts(args.as_slice(), opts.as_slice());
1118 check_fail_type(f, UnexpectedArgument_);
1125 fn test_optflag_multi() {
1126 let args = vec!("--test".to_string(), "-t".to_string());
1127 let opts = vec!(optflag("t", "test", "testing"));
1128 let rs = getopts(args.as_slice(), opts.as_slice());
1130 Err(f) => check_fail_type(f, OptionDuplicated_),
1136 fn test_optflag_short_arg() {
1137 let args = vec!("-t".to_string(), "20".to_string());
1138 let opts = vec!(optflag("t", "test", "testing"));
1139 let rs = getopts(args.as_slice(), opts.as_slice());
1142 // The next variable after the flag is just a free argument
1144 assert!(m.free[0] == "20".to_string());
1150 // Tests for optflagmulti
1152 fn test_optflagmulti_short1() {
1153 let args = vec!("-v".to_string());
1154 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1155 let rs = getopts(args.as_slice(), opts.as_slice());
1158 assert_eq!(m.opt_count("v"), 1);
1165 fn test_optflagmulti_short2a() {
1166 let args = vec!("-v".to_string(), "-v".to_string());
1167 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1168 let rs = getopts(args.as_slice(), opts.as_slice());
1171 assert_eq!(m.opt_count("v"), 2);
1178 fn test_optflagmulti_short2b() {
1179 let args = vec!("-vv".to_string());
1180 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1181 let rs = getopts(args.as_slice(), opts.as_slice());
1184 assert_eq!(m.opt_count("v"), 2);
1191 fn test_optflagmulti_long1() {
1192 let args = vec!("--verbose".to_string());
1193 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1194 let rs = getopts(args.as_slice(), opts.as_slice());
1197 assert_eq!(m.opt_count("verbose"), 1);
1204 fn test_optflagmulti_long2() {
1205 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1206 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1207 let rs = getopts(args.as_slice(), opts.as_slice());
1210 assert_eq!(m.opt_count("verbose"), 2);
1217 fn test_optflagmulti_mix() {
1218 let args = vec!("--verbose".to_string(), "-v".to_string(),
1219 "-vv".to_string(), "verbose".to_string());
1220 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1221 let rs = getopts(args.as_slice(), opts.as_slice());
1224 assert_eq!(m.opt_count("verbose"), 4);
1225 assert_eq!(m.opt_count("v"), 4);
1231 // Tests for optmulti
1233 fn test_optmulti() {
1234 let long_args = vec!("--test=20".to_string());
1235 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1236 let rs = getopts(long_args.as_slice(), opts.as_slice());
1239 assert!((m.opt_present("test")));
1240 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1241 assert!((m.opt_present("t")));
1242 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1246 let short_args = vec!("-t".to_string(), "20".to_string());
1247 match getopts(short_args.as_slice(), opts.as_slice()) {
1249 assert!((m.opt_present("test")));
1250 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1251 assert!((m.opt_present("t")));
1252 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1259 fn test_optmulti_missing() {
1260 let args = vec!("blah".to_string());
1261 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1262 let rs = getopts(args.as_slice(), opts.as_slice());
1265 assert!(!m.opt_present("test"));
1266 assert!(!m.opt_present("t"));
1273 fn test_optmulti_no_arg() {
1274 let long_args = vec!("--test".to_string());
1275 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1276 let rs = getopts(long_args.as_slice(), opts.as_slice());
1278 Err(f) => check_fail_type(f, ArgumentMissing_),
1281 let short_args = vec!("-t".to_string());
1282 match getopts(short_args.as_slice(), opts.as_slice()) {
1283 Err(f) => check_fail_type(f, ArgumentMissing_),
1289 fn test_optmulti_multi() {
1290 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1291 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1292 let rs = getopts(args.as_slice(), opts.as_slice());
1295 assert!(m.opt_present("test"));
1296 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1297 assert!(m.opt_present("t"));
1298 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1299 let pair = m.opt_strs("test");
1300 assert!(pair[0] == "20".to_string());
1301 assert!(pair[1] == "30".to_string());
1308 fn test_unrecognized_option() {
1309 let long_args = vec!("--untest".to_string());
1310 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1311 let rs = getopts(long_args.as_slice(), opts.as_slice());
1313 Err(f) => check_fail_type(f, UnrecognizedOption_),
1316 let short_args = vec!("-u".to_string());
1317 match getopts(short_args.as_slice(), opts.as_slice()) {
1318 Err(f) => check_fail_type(f, UnrecognizedOption_),
1324 fn test_combined() {
1326 vec!("prog".to_string(),
1327 "free1".to_string(),
1330 "free2".to_string(),
1331 "--flag".to_string(),
1332 "--long=30".to_string(),
1341 "-60 70".to_string());
1343 vec!(optopt("s", "something", "something", "SOMETHING"),
1344 optflag("", "flag", "a flag"),
1345 reqopt("", "long", "hi", "LONG"),
1346 optflag("f", "", "another flag"),
1347 optmulti("m", "", "mmmmmm", "YUM"),
1348 optmulti("n", "", "nothing", "NOTHING"),
1349 optopt("", "notpresent", "nothing to see here", "NOPE"));
1350 let rs = getopts(args.as_slice(), opts.as_slice());
1353 assert!(m.free[0] == "prog".to_string());
1354 assert!(m.free[1] == "free1".to_string());
1355 assert_eq!(m.opt_str("s").unwrap(), "20".to_string());
1356 assert!(m.free[2] == "free2".to_string());
1357 assert!((m.opt_present("flag")));
1358 assert_eq!(m.opt_str("long").unwrap(), "30".to_string());
1359 assert!((m.opt_present("f")));
1360 let pair = m.opt_strs("m");
1361 assert!(pair[0] == "40".to_string());
1362 assert!(pair[1] == "50".to_string());
1363 let pair = m.opt_strs("n");
1364 assert!(pair[0] == "-A B".to_string());
1365 assert!(pair[1] == "-60 70".to_string());
1366 assert!((!m.opt_present("notpresent")));
1374 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1375 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1376 optopt("f", "", "flag", "FLAG"));
1378 let args_single = vec!("-e".to_string(), "foo".to_string());
1379 let matches_single = &match getopts(args_single.as_slice(),
1382 result::Err(_) => fail!()
1384 assert!(matches_single.opts_present(["e".to_string()]));
1385 assert!(matches_single.opts_present(["encrypt".to_string(), "e".to_string()]));
1386 assert!(matches_single.opts_present(["e".to_string(), "encrypt".to_string()]));
1387 assert!(!matches_single.opts_present(["encrypt".to_string()]));
1388 assert!(!matches_single.opts_present(["thing".to_string()]));
1389 assert!(!matches_single.opts_present([]));
1391 assert_eq!(matches_single.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1392 assert_eq!(matches_single.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1394 assert_eq!(matches_single.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1397 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1399 let matches_both = &match getopts(args_both.as_slice(),
1402 result::Err(_) => fail!()
1404 assert!(matches_both.opts_present(["e".to_string()]));
1405 assert!(matches_both.opts_present(["encrypt".to_string()]));
1406 assert!(matches_both.opts_present(["encrypt".to_string(), "e".to_string()]));
1407 assert!(matches_both.opts_present(["e".to_string(), "encrypt".to_string()]));
1408 assert!(!matches_both.opts_present(["f".to_string()]));
1409 assert!(!matches_both.opts_present(["thing".to_string()]));
1410 assert!(!matches_both.opts_present([]));
1412 assert_eq!(matches_both.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1413 assert_eq!(matches_both.opts_str(["encrypt".to_string()]).unwrap(), "foo".to_string());
1414 assert_eq!(matches_both.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1416 assert_eq!(matches_both.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1422 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1423 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1424 optmulti("M", "", "something", "MMMM"));
1425 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1427 result::Err(_) => fail!()
1429 assert!(matches.opts_present(["L".to_string()]));
1430 assert_eq!(matches.opts_str(["L".to_string()]).unwrap(), "foo".to_string());
1431 assert!(matches.opts_present(["M".to_string()]));
1432 assert_eq!(matches.opts_str(["M".to_string()]).unwrap(), ".".to_string());
1437 fn test_nospace_conflict() {
1438 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1439 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1440 optflagmulti("v", "verbose", "Verbose"));
1441 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1443 result::Err(e) => fail!( "{}", e )
1445 assert!(matches.opts_present(["L".to_string()]));
1446 assert_eq!(matches.opts_str(["L".to_string()]).unwrap(), "verbose".to_string());
1447 assert!(matches.opts_present(["v".to_string()]));
1448 assert_eq!(3, matches.opt_count("v"));
1452 fn test_long_to_short() {
1453 let mut short = Opt {
1454 name: Long("banana".to_string()),
1457 aliases: Vec::new(),
1459 short.aliases = vec!(Opt { name: Short('b'),
1462 aliases: Vec::new() });
1463 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1465 assert!(verbose.long_to_short() == short);
1469 fn test_aliases_long_and_short() {
1471 optflagmulti("a", "apple", "Desc"));
1473 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1475 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1476 assert_eq!(3, matches.opt_count("a"));
1477 assert_eq!(3, matches.opt_count("apple"));
1482 let optgroups = vec!(
1483 reqopt("b", "banana", "Desc", "VAL"),
1484 optopt("a", "012345678901234567890123456789",
1486 optflag("k", "kiwi", "Desc"),
1487 optflagopt("p", "", "Desc", "VAL"),
1488 optmulti("l", "", "Desc", "VAL"));
1494 -b --banana VAL Desc
1495 -a --012345678901234567890123456789 VAL
1502 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1504 debug!("expected: <<{}>>", expected);
1505 debug!("generated: <<{}>>", generated_usage);
1506 assert_eq!(generated_usage, expected);
1510 fn test_usage_description_wrapping() {
1511 // indentation should be 24 spaces
1512 // lines wrap after 78: or rather descriptions wrap after 54
1514 let optgroups = vec!(
1515 optflag("k", "kiwi",
1516 "This is a long description which won't be wrapped..+.."), // 54
1517 optflag("a", "apple",
1518 "This is a long description which _will_ be wrapped..+.."));
1524 -k --kiwi This is a long description which won't be wrapped..+..
1525 -a --apple This is a long description which _will_ be
1529 let usage = usage("Usage: fruits", optgroups.as_slice());
1531 debug!("expected: <<{}>>", expected);
1532 debug!("generated: <<{}>>", usage);
1533 assert!(usage == expected)
1537 fn test_usage_description_multibyte_handling() {
1538 let optgroups = vec!(
1539 optflag("k", "k\u2013w\u2013",
1540 "The word kiwi is normally spelled with two i's"),
1541 optflag("a", "apple",
1542 "This \u201Cdescription\u201D has some characters that could \
1543 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1549 -k --k–w– The word kiwi is normally spelled with two i's
1550 -a --apple This “description” has some characters that could
1551 confuse the line wrapping; an apple costs 0.51€ in
1552 some parts of Europe.
1555 let usage = usage("Usage: fruits", optgroups.as_slice());
1557 debug!("expected: <<{}>>", expected);
1558 debug!("generated: <<{}>>", usage);
1559 assert!(usage == expected)
1563 fn test_short_usage() {
1564 let optgroups = vec!(
1565 reqopt("b", "banana", "Desc", "VAL"),
1566 optopt("a", "012345678901234567890123456789",
1568 optflag("k", "kiwi", "Desc"),
1569 optflagopt("p", "", "Desc", "VAL"),
1570 optmulti("l", "", "Desc", "VAL"));
1572 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1573 let generated_usage = short_usage("fruits", optgroups.as_slice());
1575 debug!("expected: <<{}>>", expected);
1576 debug!("generated: <<{}>>", generated_usage);
1577 assert_eq!(generated_usage, expected);