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,usage};
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 //! let brief = format!("Usage: {} [options]", program);
49 //! print!("{}", usage(brief.as_slice(), opts));
53 //! let args: Vec<String> = os::args();
55 //! let program = args[0].clone();
58 //! optopt("o", "", "set output file name", "NAME"),
59 //! optflag("h", "help", "print this help menu")
61 //! let matches = match getopts(args.tail(), opts) {
63 //! Err(f) => { panic!(f.to_string()) }
65 //! if matches.opt_present("h") {
66 //! print_usage(program.as_slice(), opts);
69 //! let output = matches.opt_str("o");
70 //! let input = if !matches.free.is_empty() {
71 //! matches.free[0].clone()
73 //! print_usage(program.as_slice(), opts);
76 //! do_work(input.as_slice(), output);
80 #![crate_name = "getopts"]
81 #![experimental = "use the crates.io `getopts` library instead"]
82 #![crate_type = "rlib"]
83 #![crate_type = "dylib"]
84 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
85 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
86 html_root_url = "http://doc.rust-lang.org/nightly/",
87 html_playground_url = "http://play.rust-lang.org/")]
88 #![feature(globs, phase, slicing_syntax)]
89 #![feature(unboxed_closures)]
90 #![deny(missing_docs)]
92 #[cfg(test)] #[phase(plugin, link)] extern crate log;
99 use self::SplitWithinState::*;
100 use self::Whitespace::*;
101 use self::LengthLimit::*;
104 use std::iter::repeat;
107 /// Name of an option. Either a string or a single char.
108 #[deriving(Clone, PartialEq, Eq)]
110 /// A string representing the long name of an option.
111 /// For example: "help"
113 /// A char representing the short name of an option.
118 /// Describes whether an option has an argument.
119 #[deriving(Clone, Copy, PartialEq, Eq)]
121 /// The option requires an argument.
123 /// The option takes no argument.
125 /// The option argument is optional.
129 /// Describes how often an option may occur.
130 #[deriving(Clone, Copy, PartialEq, Eq)]
132 /// The option occurs once.
134 /// The option occurs at most once.
136 /// The option occurs zero or more times.
140 /// A description of a possible option.
141 #[deriving(Clone, PartialEq, Eq)]
143 /// Name of the option
145 /// Whether it has an argument
147 /// How often it can occur
149 /// Which options it aliases
150 pub aliases: Vec<Opt>,
153 /// One group of options, e.g., both `-h` and `--help`, along with
154 /// their shared description and properties.
155 #[deriving(Clone, PartialEq, Eq)]
156 pub struct OptGroup {
157 /// Short name of the option, e.g. `h` for a `-h` option
158 pub short_name: String,
159 /// Long name of the option, e.g. `help` for a `--help` option
160 pub long_name: String,
161 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
163 /// Description for usage help text
165 /// Whether option has an argument
167 /// How often it can occur
171 /// Describes whether an option is given at all or has a value.
172 #[deriving(Clone, PartialEq, Eq)]
178 /// The result of checking command line arguments. Contains a vector
179 /// of matches and a vector of free strings.
180 #[deriving(Clone, PartialEq, Eq)]
182 /// Options that matched
184 /// Values of the Options that matched
185 vals: Vec<Vec<Optval>>,
186 /// Free string fragments
187 pub free: Vec<String>,
190 /// The type returned when the command line does not conform to the
191 /// expected format. Use the `Show` implementation to output detailed
193 #[deriving(Clone, PartialEq, Eq)]
195 /// The option requires an argument but none was passed.
196 ArgumentMissing(String),
197 /// The passed option is not declared among the possible options.
198 UnrecognizedOption(String),
199 /// A required option is not present.
200 OptionMissing(String),
201 /// A single occurrence option is being used multiple times.
202 OptionDuplicated(String),
203 /// There's an argument being passed to a non-argument option.
204 UnexpectedArgument(String),
207 /// The type of failure that occurred.
208 #[deriving(Copy, PartialEq, Eq)]
209 #[allow(missing_docs)]
218 /// The result of parsing a command line with a set of options.
219 pub type Result = result::Result<Matches, Fail>;
222 fn from_str(nm: &str) -> Name {
224 Short(nm.char_at(0u))
230 fn to_string(&self) -> String {
232 Short(ch) => ch.to_string(),
233 Long(ref s) => s.to_string()
239 /// Translate OptGroup into Opt.
240 /// (Both short and long names correspond to different Opts).
241 pub fn long_to_short(&self) -> Opt {
250 match (short_name.len(), long_name.len()) {
251 (0,0) => panic!("this long-format option was given no name"),
253 name: Long((long_name)),
259 name: Short(short_name.char_at(0)),
265 name: Long((long_name)),
270 name: Short(short_name.char_at(0)),
277 (_,_) => panic!("something is wrong with the long-form opt")
283 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
284 match find_opt(self.opts[], Name::from_str(nm)) {
285 Some(id) => self.vals[id].clone(),
286 None => panic!("No option '{}' defined", nm)
290 fn opt_val(&self, nm: &str) -> Option<Optval> {
291 let vals = self.opt_vals(nm);
295 Some(vals[0].clone())
299 /// Returns true if an option was matched.
300 pub fn opt_present(&self, nm: &str) -> bool {
301 !self.opt_vals(nm).is_empty()
304 /// Returns the number of times an option was matched.
305 pub fn opt_count(&self, nm: &str) -> uint {
306 self.opt_vals(nm).len()
309 /// Returns true if any of several options were matched.
310 pub fn opts_present(&self, names: &[String]) -> bool {
311 for nm in names.iter() {
312 match find_opt(self.opts.as_slice(), Name::from_str(nm[])) {
313 Some(id) if !self.vals[id].is_empty() => return true,
320 /// Returns the string argument supplied to one of several matching options or `None`.
321 pub fn opts_str(&self, names: &[String]) -> Option<String> {
322 for nm in names.iter() {
323 match self.opt_val(nm[]) {
324 Some(Val(ref s)) => return Some(s.clone()),
331 /// Returns a vector of the arguments provided to all matches of the given
334 /// Used when an option accepts multiple values.
335 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
336 let mut acc: Vec<String> = Vec::new();
337 let r = self.opt_vals(nm);
340 Val(ref s) => acc.push((*s).clone()),
347 /// Returns the string argument supplied to a matching option or `None`.
348 pub fn opt_str(&self, nm: &str) -> Option<String> {
349 let vals = self.opt_vals(nm);
351 return None::<String>;
354 Val(ref s) => Some((*s).clone()),
360 /// Returns the matching string, a default, or none.
362 /// Returns none if the option was not present, `def` if the option was
363 /// present but no argument was provided, and the argument if the option was
364 /// present and an argument was provided.
365 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
366 let vals = self.opt_vals(nm);
371 Val(ref s) => Some((*s).clone()),
372 _ => Some(def.to_string())
379 fn is_arg(arg: &str) -> bool {
380 arg.len() > 1 && arg.as_bytes()[0] == b'-'
383 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
384 // Search main options.
385 let pos = opts.iter().position(|opt| opt.name == nm);
390 // Search in aliases.
391 for candidate in opts.iter() {
392 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
393 return opts.iter().position(|opt| opt.name == candidate.name);
400 /// Create a long option that is required and takes an argument.
402 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
403 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
404 /// * `desc` - Description for usage help
405 /// * `hint` - Hint that is used in place of the argument in the usage help,
406 /// e.g. `"FILE"` for a `-o FILE` option
407 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
408 let len = short_name.len();
409 assert!(len == 1 || len == 0);
411 short_name: short_name.to_string(),
412 long_name: long_name.to_string(),
413 hint: hint.to_string(),
414 desc: desc.to_string(),
420 /// Create a long option that is optional and takes an argument.
422 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
423 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
424 /// * `desc` - Description for usage help
425 /// * `hint` - Hint that is used in place of the argument in the usage help,
426 /// e.g. `"FILE"` for a `-o FILE` option
427 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
428 let len = short_name.len();
429 assert!(len == 1 || len == 0);
431 short_name: short_name.to_string(),
432 long_name: long_name.to_string(),
433 hint: hint.to_string(),
434 desc: desc.to_string(),
440 /// Create a long option that is optional and does not take an argument.
442 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
443 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
444 /// * `desc` - Description for usage help
445 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
446 let len = short_name.len();
447 assert!(len == 1 || len == 0);
449 short_name: short_name.to_string(),
450 long_name: long_name.to_string(),
451 hint: "".to_string(),
452 desc: desc.to_string(),
458 /// Create a long option that can occur more than once and does not
459 /// take an argument.
461 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
462 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
463 /// * `desc` - Description for usage help
464 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
465 let len = short_name.len();
466 assert!(len == 1 || len == 0);
468 short_name: short_name.to_string(),
469 long_name: long_name.to_string(),
470 hint: "".to_string(),
471 desc: desc.to_string(),
477 /// Create a long option that is optional and takes an optional argument.
479 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
480 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
481 /// * `desc` - Description for usage help
482 /// * `hint` - Hint that is used in place of the argument in the usage help,
483 /// e.g. `"FILE"` for a `-o FILE` option
484 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
485 let len = short_name.len();
486 assert!(len == 1 || len == 0);
488 short_name: short_name.to_string(),
489 long_name: long_name.to_string(),
490 hint: hint.to_string(),
491 desc: desc.to_string(),
497 /// Create a long option that is optional, takes an argument, and may occur
500 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
501 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
502 /// * `desc` - Description for usage help
503 /// * `hint` - Hint that is used in place of the argument in the usage help,
504 /// e.g. `"FILE"` for a `-o FILE` option
505 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
506 let len = short_name.len();
507 assert!(len == 1 || len == 0);
509 short_name: short_name.to_string(),
510 long_name: long_name.to_string(),
511 hint: hint.to_string(),
512 desc: desc.to_string(),
518 /// Create a generic option group, stating all parameters explicitly
519 pub fn opt(short_name: &str,
524 occur: Occur) -> OptGroup {
525 let len = short_name.len();
526 assert!(len == 1 || len == 0);
528 short_name: short_name.to_string(),
529 long_name: long_name.to_string(),
530 hint: hint.to_string(),
531 desc: desc.to_string(),
538 /// Convert a `Fail` enum into an error string.
539 #[deprecated="use `Show` (`{}` format specifier)"]
540 pub fn to_err_msg(self) -> String {
545 impl fmt::Show for Fail {
546 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
548 ArgumentMissing(ref nm) => {
549 write!(f, "Argument to option '{}' missing.", *nm)
551 UnrecognizedOption(ref nm) => {
552 write!(f, "Unrecognized option: '{}'.", *nm)
554 OptionMissing(ref nm) => {
555 write!(f, "Required option '{}' missing.", *nm)
557 OptionDuplicated(ref nm) => {
558 write!(f, "Option '{}' given more than once.", *nm)
560 UnexpectedArgument(ref nm) => {
561 write!(f, "Option '{}' does not take an argument.", *nm)
567 /// Parse command line arguments according to the provided options.
569 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
570 /// `opt_str`, etc. to interrogate results.
573 /// Returns `Err(Fail)` on failure: use the `Show` implementation of `Fail` to display
574 /// information about it.
575 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
576 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
577 let n_opts = opts.len();
579 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
581 let mut vals: Vec<_> = range(0, n_opts).map(f).collect();
582 let mut free: Vec<String> = Vec::new();
586 let cur = args[i].clone();
587 let curlen = cur.len();
590 } else if cur == "--" {
592 while j < l { free.push(args[j].clone()); j += 1; }
596 let mut i_arg = None;
597 if cur.as_bytes()[1] == b'-' {
598 let tail = cur[2..curlen];
599 let tail_eq: Vec<&str> = tail.split('=').collect();
600 if tail_eq.len() <= 1 {
601 names = vec!(Long(tail.to_string()));
604 vec!(Long(tail_eq[0].to_string()));
605 i_arg = Some(tail_eq[1].to_string());
611 let range = cur.char_range_at(j);
612 let opt = Short(range.ch);
614 /* In a series of potential options (eg. -aheJ), if we
615 see one which takes an argument, we assume all
616 subsequent characters make up the argument. This
617 allows options such as -L/usr/local/lib/foo to be
618 interpreted correctly
621 let opt_id = match find_opt(opts.as_slice(), opt.clone()) {
623 None => return Err(UnrecognizedOption(opt.to_string()))
628 let arg_follows = match opts[opt_id].hasarg {
633 if arg_follows && range.next < curlen {
634 i_arg = Some(cur[range.next..curlen].to_string());
641 let mut name_pos = 0;
642 for nm in names.iter() {
644 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
646 None => return Err(UnrecognizedOption(nm.to_string()))
648 match opts[optid].hasarg {
650 if name_pos == names.len() && !i_arg.is_none() {
651 return Err(UnexpectedArgument(nm.to_string()));
653 vals[optid].push(Given);
656 if !i_arg.is_none() {
658 .push(Val((i_arg.clone())
660 } else if name_pos < names.len() || i + 1 == l ||
661 is_arg(args[i + 1][]) {
662 vals[optid].push(Given);
665 vals[optid].push(Val(args[i].clone()));
669 if !i_arg.is_none() {
670 vals[optid].push(Val(i_arg.clone().unwrap()));
671 } else if i + 1 == l {
672 return Err(ArgumentMissing(nm.to_string()));
675 vals[optid].push(Val(args[i].clone()));
683 for i in range(0u, n_opts) {
684 let n = vals[i].len();
685 let occ = opts[i].occur;
686 if occ == Req && n == 0 {
687 return Err(OptionMissing(opts[i].name.to_string()));
689 if occ != Multi && n > 1 {
690 return Err(OptionDuplicated(opts[i].name.to_string()));
700 /// Derive a usage message from a set of long options.
701 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
703 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
705 let rows = opts.iter().map(|optref| {
706 let OptGroup{short_name,
711 ..} = (*optref).clone();
713 let mut row = repeat(" ").take(4).collect::<String>();
716 match short_name.len() {
720 row.push_str(short_name[]);
723 _ => panic!("the short name should only be 1 ascii char long"),
727 match long_name.len() {
731 row.push_str(long_name[]);
739 Yes => row.push_str(hint[]),
742 row.push_str(hint[]);
747 // FIXME: #5516 should be graphemes not codepoints
748 // here we just need to indent the start of the description
749 let rowlen = row.chars().count();
751 for _ in range(0, 24 - rowlen) {
755 row.push_str(desc_sep[]);
758 // Normalize desc to contain words separated by one space character
759 let mut desc_normalized_whitespace = String::new();
760 for word in desc.words() {
761 desc_normalized_whitespace.push_str(word);
762 desc_normalized_whitespace.push(' ');
765 // FIXME: #5516 should be graphemes not codepoints
766 let mut desc_rows = Vec::new();
767 each_split_within(desc_normalized_whitespace[], 54, |substr| {
768 desc_rows.push(substr.to_string());
772 // FIXME: #5516 should be graphemes not codepoints
773 // wrapped description
774 row.push_str(desc_rows.connect(desc_sep[])[]);
779 format!("{}\n\nOptions:\n{}\n", brief,
780 rows.collect::<Vec<String>>().connect("\n"))
783 fn format_option(opt: &OptGroup) -> String {
784 let mut line = String::new();
786 if opt.occur != Req {
790 // Use short_name is possible, but fallback to long_name.
791 if opt.short_name.len() > 0 {
793 line.push_str(opt.short_name[]);
796 line.push_str(opt.long_name[]);
799 if opt.hasarg != No {
801 if opt.hasarg == Maybe {
804 line.push_str(opt.hint[]);
805 if opt.hasarg == Maybe {
810 if opt.occur != Req {
813 if opt.occur == Multi {
820 /// Derive a short one-line usage summary from a set of long options.
821 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
822 let mut line = format!("Usage: {} ", program_name);
823 line.push_str(opts.iter()
825 .collect::<Vec<String>>()
831 enum SplitWithinState {
832 A, // leading whitespace, initial state
834 C, // internal and trailing whitespace
838 Ws, // current char is whitespace
839 Cr // current char is not whitespace
843 UnderLim, // current char makes current substring still fit in limit
844 OverLim // current char makes current substring no longer fit in limit
848 /// Splits a string into substrings with possibly internal whitespace,
849 /// each of them at most `lim` bytes long. The substrings have leading and trailing
850 /// whitespace removed, and are only cut at whitespace boundaries.
852 /// Note: Function was moved here from `std::str` because this module is the only place that
853 /// uses it, and because it was too specific for a general string function.
857 /// Panics during iteration if the string contains a non-whitespace
858 /// sequence longer than the limit.
859 fn each_split_within<F>(ss: &str, lim: uint, mut it: F) -> bool where
860 F: FnMut(&str) -> bool
862 // Just for fun, let's write this as a state machine:
864 let mut slice_start = 0;
865 let mut last_start = 0;
866 let mut last_end = 0;
868 let mut fake_i = ss.len();
873 // if the limit is larger than the string, lower it to save cycles
878 let mut machine = |&mut: cont: &mut bool, (i, c): (uint, char)| -> bool {
879 let whitespace = if c.is_whitespace() { Ws } else { Cr };
880 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
882 state = match (state, whitespace, limit) {
884 (A, Cr, _) => { slice_start = i; last_start = i; B }
886 (B, Cr, UnderLim) => { B }
887 (B, Cr, OverLim) if (i - last_start + 1) > lim
888 => panic!("word starting with {} longer than limit!",
889 ss[last_start..i + 1]),
890 (B, Cr, OverLim) => {
891 *cont = it(ss[slice_start..last_end]);
892 slice_start = last_start;
895 (B, Ws, UnderLim) => {
899 (B, Ws, OverLim) => {
901 *cont = it(ss[slice_start..last_end]);
905 (C, Cr, UnderLim) => {
909 (C, Cr, OverLim) => {
910 *cont = it(ss[slice_start..last_end]);
916 (C, Ws, OverLim) => {
917 *cont = it(ss[slice_start..last_end]);
920 (C, Ws, UnderLim) => {
928 ss.char_indices().all(|x| machine(&mut cont, x));
930 // Let the automaton 'run out' by supplying trailing whitespace
931 while cont && match state { B | C => true, A => false } {
932 machine(&mut cont, (fake_i, ' '));
939 fn test_split_within() {
940 fn t(s: &str, i: uint, u: &[String]) {
941 let mut v = Vec::new();
942 each_split_within(s, i, |s| { v.push(s.to_string()); true });
943 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
947 t("hello", 15, &["hello".to_string()]);
948 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
949 "Mary had a".to_string(),
950 "little lamb".to_string(),
951 "Little lamb".to_string()
953 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
954 &["Mary had a little lamb\nLittle lamb".to_string()]);
962 use std::result::Result::{Err, Ok};
968 let long_args = vec!("--test=20".to_string());
969 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
970 let rs = getopts(long_args.as_slice(), opts.as_slice());
973 assert!(m.opt_present("test"));
974 assert_eq!(m.opt_str("test").unwrap(), "20");
975 assert!(m.opt_present("t"));
976 assert_eq!(m.opt_str("t").unwrap(), "20");
978 _ => { panic!("test_reqopt failed (long arg)"); }
980 let short_args = vec!("-t".to_string(), "20".to_string());
981 match getopts(short_args.as_slice(), opts.as_slice()) {
983 assert!((m.opt_present("test")));
984 assert_eq!(m.opt_str("test").unwrap(), "20");
985 assert!((m.opt_present("t")));
986 assert_eq!(m.opt_str("t").unwrap(), "20");
988 _ => { panic!("test_reqopt failed (short arg)"); }
993 fn test_reqopt_missing() {
994 let args = vec!("blah".to_string());
995 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
996 let rs = getopts(args.as_slice(), opts.as_slice());
998 Err(OptionMissing(_)) => {},
1004 fn test_reqopt_no_arg() {
1005 let long_args = vec!("--test".to_string());
1006 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1007 let rs = getopts(long_args.as_slice(), opts.as_slice());
1009 Err(ArgumentMissing(_)) => {},
1012 let short_args = vec!("-t".to_string());
1013 match getopts(short_args.as_slice(), opts.as_slice()) {
1014 Err(ArgumentMissing(_)) => {},
1020 fn test_reqopt_multi() {
1021 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1022 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1023 let rs = getopts(args.as_slice(), opts.as_slice());
1025 Err(OptionDuplicated(_)) => {},
1033 let long_args = vec!("--test=20".to_string());
1034 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1035 let rs = getopts(long_args.as_slice(), opts.as_slice());
1038 assert!(m.opt_present("test"));
1039 assert_eq!(m.opt_str("test").unwrap(), "20");
1040 assert!((m.opt_present("t")));
1041 assert_eq!(m.opt_str("t").unwrap(), "20");
1045 let short_args = vec!("-t".to_string(), "20".to_string());
1046 match getopts(short_args.as_slice(), opts.as_slice()) {
1048 assert!((m.opt_present("test")));
1049 assert_eq!(m.opt_str("test").unwrap(), "20");
1050 assert!((m.opt_present("t")));
1051 assert_eq!(m.opt_str("t").unwrap(), "20");
1058 fn test_optopt_missing() {
1059 let args = vec!("blah".to_string());
1060 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1061 let rs = getopts(args.as_slice(), opts.as_slice());
1064 assert!(!m.opt_present("test"));
1065 assert!(!m.opt_present("t"));
1072 fn test_optopt_no_arg() {
1073 let long_args = vec!("--test".to_string());
1074 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1075 let rs = getopts(long_args.as_slice(), opts.as_slice());
1077 Err(ArgumentMissing(_)) => {},
1080 let short_args = vec!("-t".to_string());
1081 match getopts(short_args.as_slice(), opts.as_slice()) {
1082 Err(ArgumentMissing(_)) => {},
1088 fn test_optopt_multi() {
1089 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1090 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1091 let rs = getopts(args.as_slice(), opts.as_slice());
1093 Err(OptionDuplicated(_)) => {},
1098 // Tests for optflag
1101 let long_args = vec!("--test".to_string());
1102 let opts = vec!(optflag("t", "test", "testing"));
1103 let rs = getopts(long_args.as_slice(), opts.as_slice());
1106 assert!(m.opt_present("test"));
1107 assert!(m.opt_present("t"));
1111 let short_args = vec!("-t".to_string());
1112 match getopts(short_args.as_slice(), opts.as_slice()) {
1114 assert!(m.opt_present("test"));
1115 assert!(m.opt_present("t"));
1122 fn test_optflag_missing() {
1123 let args = vec!("blah".to_string());
1124 let opts = vec!(optflag("t", "test", "testing"));
1125 let rs = getopts(args.as_slice(), opts.as_slice());
1128 assert!(!m.opt_present("test"));
1129 assert!(!m.opt_present("t"));
1136 fn test_optflag_long_arg() {
1137 let args = vec!("--test=20".to_string());
1138 let opts = vec!(optflag("t", "test", "testing"));
1139 let rs = getopts(args.as_slice(), opts.as_slice());
1141 Err(UnexpectedArgument(_)) => {},
1147 fn test_optflag_multi() {
1148 let args = vec!("--test".to_string(), "-t".to_string());
1149 let opts = vec!(optflag("t", "test", "testing"));
1150 let rs = getopts(args.as_slice(), opts.as_slice());
1152 Err(OptionDuplicated(_)) => {},
1158 fn test_optflag_short_arg() {
1159 let args = vec!("-t".to_string(), "20".to_string());
1160 let opts = vec!(optflag("t", "test", "testing"));
1161 let rs = getopts(args.as_slice(), opts.as_slice());
1164 // The next variable after the flag is just a free argument
1166 assert!(m.free[0] == "20");
1172 // Tests for optflagmulti
1174 fn test_optflagmulti_short1() {
1175 let args = vec!("-v".to_string());
1176 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1177 let rs = getopts(args.as_slice(), opts.as_slice());
1180 assert_eq!(m.opt_count("v"), 1);
1187 fn test_optflagmulti_short2a() {
1188 let args = vec!("-v".to_string(), "-v".to_string());
1189 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1190 let rs = getopts(args.as_slice(), opts.as_slice());
1193 assert_eq!(m.opt_count("v"), 2);
1200 fn test_optflagmulti_short2b() {
1201 let args = vec!("-vv".to_string());
1202 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1203 let rs = getopts(args.as_slice(), opts.as_slice());
1206 assert_eq!(m.opt_count("v"), 2);
1213 fn test_optflagmulti_long1() {
1214 let args = vec!("--verbose".to_string());
1215 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1216 let rs = getopts(args.as_slice(), opts.as_slice());
1219 assert_eq!(m.opt_count("verbose"), 1);
1226 fn test_optflagmulti_long2() {
1227 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1228 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1229 let rs = getopts(args.as_slice(), opts.as_slice());
1232 assert_eq!(m.opt_count("verbose"), 2);
1239 fn test_optflagmulti_mix() {
1240 let args = vec!("--verbose".to_string(), "-v".to_string(),
1241 "-vv".to_string(), "verbose".to_string());
1242 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1243 let rs = getopts(args.as_slice(), opts.as_slice());
1246 assert_eq!(m.opt_count("verbose"), 4);
1247 assert_eq!(m.opt_count("v"), 4);
1253 // Tests for optmulti
1255 fn test_optmulti() {
1256 let long_args = vec!("--test=20".to_string());
1257 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1258 let rs = getopts(long_args.as_slice(), opts.as_slice());
1261 assert!((m.opt_present("test")));
1262 assert_eq!(m.opt_str("test").unwrap(), "20");
1263 assert!((m.opt_present("t")));
1264 assert_eq!(m.opt_str("t").unwrap(), "20");
1268 let short_args = vec!("-t".to_string(), "20".to_string());
1269 match getopts(short_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");
1281 fn test_optmulti_missing() {
1282 let args = vec!("blah".to_string());
1283 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1284 let rs = getopts(args.as_slice(), opts.as_slice());
1287 assert!(!m.opt_present("test"));
1288 assert!(!m.opt_present("t"));
1295 fn test_optmulti_no_arg() {
1296 let long_args = vec!("--test".to_string());
1297 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1298 let rs = getopts(long_args.as_slice(), opts.as_slice());
1300 Err(ArgumentMissing(_)) => {},
1303 let short_args = vec!("-t".to_string());
1304 match getopts(short_args.as_slice(), opts.as_slice()) {
1305 Err(ArgumentMissing(_)) => {},
1311 fn test_optmulti_multi() {
1312 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1313 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1314 let rs = getopts(args.as_slice(), opts.as_slice());
1317 assert!(m.opt_present("test"));
1318 assert_eq!(m.opt_str("test").unwrap(), "20");
1319 assert!(m.opt_present("t"));
1320 assert_eq!(m.opt_str("t").unwrap(), "20");
1321 let pair = m.opt_strs("test");
1322 assert!(pair[0] == "20");
1323 assert!(pair[1] == "30");
1330 fn test_unrecognized_option() {
1331 let long_args = vec!("--untest".to_string());
1332 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1333 let rs = getopts(long_args.as_slice(), opts.as_slice());
1335 Err(UnrecognizedOption(_)) => {},
1338 let short_args = vec!("-u".to_string());
1339 match getopts(short_args.as_slice(), opts.as_slice()) {
1340 Err(UnrecognizedOption(_)) => {},
1346 fn test_combined() {
1348 vec!("prog".to_string(),
1349 "free1".to_string(),
1352 "free2".to_string(),
1353 "--flag".to_string(),
1354 "--long=30".to_string(),
1363 "-60 70".to_string());
1365 vec!(optopt("s", "something", "something", "SOMETHING"),
1366 optflag("", "flag", "a flag"),
1367 reqopt("", "long", "hi", "LONG"),
1368 optflag("f", "", "another flag"),
1369 optmulti("m", "", "mmmmmm", "YUM"),
1370 optmulti("n", "", "nothing", "NOTHING"),
1371 optopt("", "notpresent", "nothing to see here", "NOPE"));
1372 let rs = getopts(args.as_slice(), opts.as_slice());
1375 assert!(m.free[0] == "prog");
1376 assert!(m.free[1] == "free1");
1377 assert_eq!(m.opt_str("s").unwrap(), "20");
1378 assert!(m.free[2] == "free2");
1379 assert!((m.opt_present("flag")));
1380 assert_eq!(m.opt_str("long").unwrap(), "30");
1381 assert!((m.opt_present("f")));
1382 let pair = m.opt_strs("m");
1383 assert!(pair[0] == "40");
1384 assert!(pair[1] == "50");
1385 let pair = m.opt_strs("n");
1386 assert!(pair[0] == "-A B");
1387 assert!(pair[1] == "-60 70");
1388 assert!((!m.opt_present("notpresent")));
1396 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1397 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1398 optopt("f", "", "flag", "FLAG"));
1400 let args_single = vec!("-e".to_string(), "foo".to_string());
1401 let matches_single = &match getopts(args_single.as_slice(),
1403 result::Result::Ok(m) => m,
1404 result::Result::Err(_) => panic!()
1406 assert!(matches_single.opts_present(&["e".to_string()]));
1407 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1408 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1409 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1410 assert!(!matches_single.opts_present(&["thing".to_string()]));
1411 assert!(!matches_single.opts_present(&[]));
1413 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1414 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1416 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1419 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1421 let matches_both = &match getopts(args_both.as_slice(),
1423 result::Result::Ok(m) => m,
1424 result::Result::Err(_) => panic!()
1426 assert!(matches_both.opts_present(&["e".to_string()]));
1427 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1428 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1429 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1430 assert!(!matches_both.opts_present(&["f".to_string()]));
1431 assert!(!matches_both.opts_present(&["thing".to_string()]));
1432 assert!(!matches_both.opts_present(&[]));
1434 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1435 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1436 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1438 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1444 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1445 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1446 optmulti("M", "", "something", "MMMM"));
1447 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1448 result::Result::Ok(m) => m,
1449 result::Result::Err(_) => panic!()
1451 assert!(matches.opts_present(&["L".to_string()]));
1452 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1453 assert!(matches.opts_present(&["M".to_string()]));
1454 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1459 fn test_nospace_conflict() {
1460 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1461 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1462 optflagmulti("v", "verbose", "Verbose"));
1463 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1464 result::Result::Ok(m) => m,
1465 result::Result::Err(e) => panic!( "{}", e )
1467 assert!(matches.opts_present(&["L".to_string()]));
1468 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1469 assert!(matches.opts_present(&["v".to_string()]));
1470 assert_eq!(3, matches.opt_count("v"));
1474 fn test_long_to_short() {
1475 let mut short = Opt {
1476 name: Name::Long("banana".to_string()),
1477 hasarg: HasArg::Yes,
1479 aliases: Vec::new(),
1481 short.aliases = vec!(Opt { name: Name::Short('b'),
1482 hasarg: HasArg::Yes,
1484 aliases: Vec::new() });
1485 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1487 assert!(verbose.long_to_short() == short);
1491 fn test_aliases_long_and_short() {
1493 optflagmulti("a", "apple", "Desc"));
1495 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1497 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1498 assert_eq!(3, matches.opt_count("a"));
1499 assert_eq!(3, matches.opt_count("apple"));
1504 let optgroups = vec!(
1505 reqopt("b", "banana", "Desc", "VAL"),
1506 optopt("a", "012345678901234567890123456789",
1508 optflag("k", "kiwi", "Desc"),
1509 optflagopt("p", "", "Desc", "VAL"),
1510 optmulti("l", "", "Desc", "VAL"));
1516 -b --banana VAL Desc
1517 -a --012345678901234567890123456789 VAL
1524 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1526 debug!("expected: <<{}>>", expected);
1527 debug!("generated: <<{}>>", generated_usage);
1528 assert_eq!(generated_usage, expected);
1532 fn test_usage_description_wrapping() {
1533 // indentation should be 24 spaces
1534 // lines wrap after 78: or rather descriptions wrap after 54
1536 let optgroups = vec!(
1537 optflag("k", "kiwi",
1538 "This is a long description which won't be wrapped..+.."), // 54
1539 optflag("a", "apple",
1540 "This is a long description which _will_ be wrapped..+.."));
1546 -k --kiwi This is a long description which won't be wrapped..+..
1547 -a --apple This is a long description which _will_ be
1551 let usage = usage("Usage: fruits", optgroups.as_slice());
1553 debug!("expected: <<{}>>", expected);
1554 debug!("generated: <<{}>>", usage);
1555 assert!(usage == expected)
1559 fn test_usage_description_multibyte_handling() {
1560 let optgroups = vec!(
1561 optflag("k", "k\u{2013}w\u{2013}",
1562 "The word kiwi is normally spelled with two i's"),
1563 optflag("a", "apple",
1564 "This \u{201C}description\u{201D} has some characters that could \
1565 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1571 -k --k–w– The word kiwi is normally spelled with two i's
1572 -a --apple This “description” has some characters that could
1573 confuse the line wrapping; an apple costs 0.51€ in
1574 some parts of Europe.
1577 let usage = usage("Usage: fruits", optgroups.as_slice());
1579 debug!("expected: <<{}>>", expected);
1580 debug!("generated: <<{}>>", usage);
1581 assert!(usage == expected)
1585 fn test_short_usage() {
1586 let optgroups = vec!(
1587 reqopt("b", "banana", "Desc", "VAL"),
1588 optopt("a", "012345678901234567890123456789",
1590 optflag("k", "kiwi", "Desc"),
1591 optflagopt("p", "", "Desc", "VAL"),
1592 optmulti("l", "", "Desc", "VAL"));
1594 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1595 let generated_usage = short_usage("fruits", optgroups.as_slice());
1597 debug!("expected: <<{}>>", expected);
1598 debug!("generated: <<{}>>", generated_usage);
1599 assert_eq!(generated_usage, expected);