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, slicing_syntax)]
89 #![feature(unboxed_closures)]
90 #![deny(missing_docs)]
92 #[cfg(test)] #[macro_use] 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 #[derive(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 #[derive(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 #[derive(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 #[derive(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 #[derive(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 #[derive(Clone, PartialEq, Eq)]
178 /// The result of checking command line arguments. Contains a vector
179 /// of matches and a vector of free strings.
180 #[derive(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 #[derive(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 #[derive(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.index(&FullRange), 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.index(&FullRange))) {
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.index(&FullRange)) {
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();
588 if !is_arg(cur.index(&FullRange)) {
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.index(&(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.index(&(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 let v = &mut vals[optid];
657 if !i_arg.is_none() {
658 let v = &mut vals[optid];
659 v.push(Val((i_arg.clone())
661 } else if name_pos < names.len() || i + 1 == l ||
662 is_arg(args[i + 1].index(&FullRange)) {
663 let v = &mut vals[optid];
667 let v = &mut vals[optid];
668 v.push(Val(args[i].clone()));
672 if !i_arg.is_none() {
673 let v = &mut vals[optid];
674 v.push(Val(i_arg.clone().unwrap()));
675 } else if i + 1 == l {
676 return Err(ArgumentMissing(nm.to_string()));
679 let v = &mut vals[optid];
680 v.push(Val(args[i].clone()));
688 for i in range(0u, n_opts) {
689 let n = vals[i].len();
690 let occ = opts[i].occur;
691 if occ == Req && n == 0 {
692 return Err(OptionMissing(opts[i].name.to_string()));
694 if occ != Multi && n > 1 {
695 return Err(OptionDuplicated(opts[i].name.to_string()));
705 /// Derive a usage message from a set of long options.
706 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
708 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
710 let rows = opts.iter().map(|optref| {
711 let OptGroup{short_name,
716 ..} = (*optref).clone();
718 let mut row = repeat(" ").take(4).collect::<String>();
721 match short_name.len() {
725 row.push_str(short_name.index(&FullRange));
728 _ => panic!("the short name should only be 1 ascii char long"),
732 match long_name.len() {
736 row.push_str(long_name.index(&FullRange));
744 Yes => row.push_str(hint.index(&FullRange)),
747 row.push_str(hint.index(&FullRange));
752 // FIXME: #5516 should be graphemes not codepoints
753 // here we just need to indent the start of the description
754 let rowlen = row.chars().count();
756 for _ in range(0, 24 - rowlen) {
760 row.push_str(desc_sep.index(&FullRange));
763 // Normalize desc to contain words separated by one space character
764 let mut desc_normalized_whitespace = String::new();
765 for word in desc.words() {
766 desc_normalized_whitespace.push_str(word);
767 desc_normalized_whitespace.push(' ');
770 // FIXME: #5516 should be graphemes not codepoints
771 let mut desc_rows = Vec::new();
772 each_split_within(desc_normalized_whitespace.index(&FullRange), 54, |substr| {
773 desc_rows.push(substr.to_string());
777 // FIXME: #5516 should be graphemes not codepoints
778 // wrapped description
779 row.push_str(desc_rows.connect(desc_sep.index(&FullRange)).index(&FullRange));
784 format!("{}\n\nOptions:\n{}\n", brief,
785 rows.collect::<Vec<String>>().connect("\n"))
788 fn format_option(opt: &OptGroup) -> String {
789 let mut line = String::new();
791 if opt.occur != Req {
795 // Use short_name is possible, but fallback to long_name.
796 if opt.short_name.len() > 0 {
798 line.push_str(opt.short_name.index(&FullRange));
801 line.push_str(opt.long_name.index(&FullRange));
804 if opt.hasarg != No {
806 if opt.hasarg == Maybe {
809 line.push_str(opt.hint.index(&FullRange));
810 if opt.hasarg == Maybe {
815 if opt.occur != Req {
818 if opt.occur == Multi {
825 /// Derive a short one-line usage summary from a set of long options.
826 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
827 let mut line = format!("Usage: {} ", program_name);
828 line.push_str(opts.iter()
830 .collect::<Vec<String>>()
831 .connect(" ").index(&FullRange));
836 enum SplitWithinState {
837 A, // leading whitespace, initial state
839 C, // internal and trailing whitespace
843 Ws, // current char is whitespace
844 Cr // current char is not whitespace
848 UnderLim, // current char makes current substring still fit in limit
849 OverLim // current char makes current substring no longer fit in limit
853 /// Splits a string into substrings with possibly internal whitespace,
854 /// each of them at most `lim` bytes long. The substrings have leading and trailing
855 /// whitespace removed, and are only cut at whitespace boundaries.
857 /// Note: Function was moved here from `std::str` because this module is the only place that
858 /// uses it, and because it was too specific for a general string function.
862 /// Panics during iteration if the string contains a non-whitespace
863 /// sequence longer than the limit.
864 fn each_split_within<F>(ss: &str, lim: uint, mut it: F) -> bool where
865 F: FnMut(&str) -> bool
867 // Just for fun, let's write this as a state machine:
869 let mut slice_start = 0;
870 let mut last_start = 0;
871 let mut last_end = 0;
873 let mut fake_i = ss.len();
878 // if the limit is larger than the string, lower it to save cycles
883 let mut machine = |&mut: cont: &mut bool, (i, c): (uint, char)| -> bool {
884 let whitespace = if c.is_whitespace() { Ws } else { Cr };
885 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
887 state = match (state, whitespace, limit) {
889 (A, Cr, _) => { slice_start = i; last_start = i; B }
891 (B, Cr, UnderLim) => { B }
892 (B, Cr, OverLim) if (i - last_start + 1) > lim
893 => panic!("word starting with {} longer than limit!",
894 ss.index(&(last_start..(i + 1)))),
895 (B, Cr, OverLim) => {
896 *cont = it(ss.index(&(slice_start..last_end)));
897 slice_start = last_start;
900 (B, Ws, UnderLim) => {
904 (B, Ws, OverLim) => {
906 *cont = it(ss.index(&(slice_start..last_end)));
910 (C, Cr, UnderLim) => {
914 (C, Cr, OverLim) => {
915 *cont = it(ss.index(&(slice_start..last_end)));
921 (C, Ws, OverLim) => {
922 *cont = it(ss.index(&(slice_start..last_end)));
925 (C, Ws, UnderLim) => {
933 ss.char_indices().all(|x| machine(&mut cont, x));
935 // Let the automaton 'run out' by supplying trailing whitespace
936 while cont && match state { B | C => true, A => false } {
937 machine(&mut cont, (fake_i, ' '));
944 fn test_split_within() {
945 fn t(s: &str, i: uint, u: &[String]) {
946 let mut v = Vec::new();
947 each_split_within(s, i, |s| { v.push(s.to_string()); true });
948 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
952 t("hello", 15, &["hello".to_string()]);
953 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
954 "Mary had a".to_string(),
955 "little lamb".to_string(),
956 "Little lamb".to_string()
958 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
959 &["Mary had a little lamb\nLittle lamb".to_string()]);
967 use std::result::Result::{Err, Ok};
973 let long_args = vec!("--test=20".to_string());
974 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
975 let rs = getopts(long_args.as_slice(), opts.as_slice());
978 assert!(m.opt_present("test"));
979 assert_eq!(m.opt_str("test").unwrap(), "20");
980 assert!(m.opt_present("t"));
981 assert_eq!(m.opt_str("t").unwrap(), "20");
983 _ => { panic!("test_reqopt failed (long arg)"); }
985 let short_args = vec!("-t".to_string(), "20".to_string());
986 match getopts(short_args.as_slice(), opts.as_slice()) {
988 assert!((m.opt_present("test")));
989 assert_eq!(m.opt_str("test").unwrap(), "20");
990 assert!((m.opt_present("t")));
991 assert_eq!(m.opt_str("t").unwrap(), "20");
993 _ => { panic!("test_reqopt failed (short arg)"); }
998 fn test_reqopt_missing() {
999 let args = vec!("blah".to_string());
1000 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1001 let rs = getopts(args.as_slice(), opts.as_slice());
1003 Err(OptionMissing(_)) => {},
1009 fn test_reqopt_no_arg() {
1010 let long_args = vec!("--test".to_string());
1011 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1012 let rs = getopts(long_args.as_slice(), opts.as_slice());
1014 Err(ArgumentMissing(_)) => {},
1017 let short_args = vec!("-t".to_string());
1018 match getopts(short_args.as_slice(), opts.as_slice()) {
1019 Err(ArgumentMissing(_)) => {},
1025 fn test_reqopt_multi() {
1026 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1027 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1028 let rs = getopts(args.as_slice(), opts.as_slice());
1030 Err(OptionDuplicated(_)) => {},
1038 let long_args = vec!("--test=20".to_string());
1039 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1040 let rs = getopts(long_args.as_slice(), opts.as_slice());
1043 assert!(m.opt_present("test"));
1044 assert_eq!(m.opt_str("test").unwrap(), "20");
1045 assert!((m.opt_present("t")));
1046 assert_eq!(m.opt_str("t").unwrap(), "20");
1050 let short_args = vec!("-t".to_string(), "20".to_string());
1051 match getopts(short_args.as_slice(), opts.as_slice()) {
1053 assert!((m.opt_present("test")));
1054 assert_eq!(m.opt_str("test").unwrap(), "20");
1055 assert!((m.opt_present("t")));
1056 assert_eq!(m.opt_str("t").unwrap(), "20");
1063 fn test_optopt_missing() {
1064 let args = vec!("blah".to_string());
1065 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1066 let rs = getopts(args.as_slice(), opts.as_slice());
1069 assert!(!m.opt_present("test"));
1070 assert!(!m.opt_present("t"));
1077 fn test_optopt_no_arg() {
1078 let long_args = vec!("--test".to_string());
1079 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1080 let rs = getopts(long_args.as_slice(), opts.as_slice());
1082 Err(ArgumentMissing(_)) => {},
1085 let short_args = vec!("-t".to_string());
1086 match getopts(short_args.as_slice(), opts.as_slice()) {
1087 Err(ArgumentMissing(_)) => {},
1093 fn test_optopt_multi() {
1094 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1095 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1096 let rs = getopts(args.as_slice(), opts.as_slice());
1098 Err(OptionDuplicated(_)) => {},
1103 // Tests for optflag
1106 let long_args = vec!("--test".to_string());
1107 let opts = vec!(optflag("t", "test", "testing"));
1108 let rs = getopts(long_args.as_slice(), opts.as_slice());
1111 assert!(m.opt_present("test"));
1112 assert!(m.opt_present("t"));
1116 let short_args = vec!("-t".to_string());
1117 match getopts(short_args.as_slice(), opts.as_slice()) {
1119 assert!(m.opt_present("test"));
1120 assert!(m.opt_present("t"));
1127 fn test_optflag_missing() {
1128 let args = vec!("blah".to_string());
1129 let opts = vec!(optflag("t", "test", "testing"));
1130 let rs = getopts(args.as_slice(), opts.as_slice());
1133 assert!(!m.opt_present("test"));
1134 assert!(!m.opt_present("t"));
1141 fn test_optflag_long_arg() {
1142 let args = vec!("--test=20".to_string());
1143 let opts = vec!(optflag("t", "test", "testing"));
1144 let rs = getopts(args.as_slice(), opts.as_slice());
1146 Err(UnexpectedArgument(_)) => {},
1152 fn test_optflag_multi() {
1153 let args = vec!("--test".to_string(), "-t".to_string());
1154 let opts = vec!(optflag("t", "test", "testing"));
1155 let rs = getopts(args.as_slice(), opts.as_slice());
1157 Err(OptionDuplicated(_)) => {},
1163 fn test_optflag_short_arg() {
1164 let args = vec!("-t".to_string(), "20".to_string());
1165 let opts = vec!(optflag("t", "test", "testing"));
1166 let rs = getopts(args.as_slice(), opts.as_slice());
1169 // The next variable after the flag is just a free argument
1171 assert!(m.free[0] == "20");
1177 // Tests for optflagmulti
1179 fn test_optflagmulti_short1() {
1180 let args = vec!("-v".to_string());
1181 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1182 let rs = getopts(args.as_slice(), opts.as_slice());
1185 assert_eq!(m.opt_count("v"), 1);
1192 fn test_optflagmulti_short2a() {
1193 let args = vec!("-v".to_string(), "-v".to_string());
1194 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1195 let rs = getopts(args.as_slice(), opts.as_slice());
1198 assert_eq!(m.opt_count("v"), 2);
1205 fn test_optflagmulti_short2b() {
1206 let args = vec!("-vv".to_string());
1207 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1208 let rs = getopts(args.as_slice(), opts.as_slice());
1211 assert_eq!(m.opt_count("v"), 2);
1218 fn test_optflagmulti_long1() {
1219 let args = vec!("--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"), 1);
1231 fn test_optflagmulti_long2() {
1232 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1233 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1234 let rs = getopts(args.as_slice(), opts.as_slice());
1237 assert_eq!(m.opt_count("verbose"), 2);
1244 fn test_optflagmulti_mix() {
1245 let args = vec!("--verbose".to_string(), "-v".to_string(),
1246 "-vv".to_string(), "verbose".to_string());
1247 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1248 let rs = getopts(args.as_slice(), opts.as_slice());
1251 assert_eq!(m.opt_count("verbose"), 4);
1252 assert_eq!(m.opt_count("v"), 4);
1258 // Tests for optmulti
1260 fn test_optmulti() {
1261 let long_args = vec!("--test=20".to_string());
1262 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1263 let rs = getopts(long_args.as_slice(), opts.as_slice());
1266 assert!((m.opt_present("test")));
1267 assert_eq!(m.opt_str("test").unwrap(), "20");
1268 assert!((m.opt_present("t")));
1269 assert_eq!(m.opt_str("t").unwrap(), "20");
1273 let short_args = vec!("-t".to_string(), "20".to_string());
1274 match getopts(short_args.as_slice(), opts.as_slice()) {
1276 assert!((m.opt_present("test")));
1277 assert_eq!(m.opt_str("test").unwrap(), "20");
1278 assert!((m.opt_present("t")));
1279 assert_eq!(m.opt_str("t").unwrap(), "20");
1286 fn test_optmulti_missing() {
1287 let args = vec!("blah".to_string());
1288 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1289 let rs = getopts(args.as_slice(), opts.as_slice());
1292 assert!(!m.opt_present("test"));
1293 assert!(!m.opt_present("t"));
1300 fn test_optmulti_no_arg() {
1301 let long_args = vec!("--test".to_string());
1302 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1303 let rs = getopts(long_args.as_slice(), opts.as_slice());
1305 Err(ArgumentMissing(_)) => {},
1308 let short_args = vec!("-t".to_string());
1309 match getopts(short_args.as_slice(), opts.as_slice()) {
1310 Err(ArgumentMissing(_)) => {},
1316 fn test_optmulti_multi() {
1317 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1318 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1319 let rs = getopts(args.as_slice(), opts.as_slice());
1322 assert!(m.opt_present("test"));
1323 assert_eq!(m.opt_str("test").unwrap(), "20");
1324 assert!(m.opt_present("t"));
1325 assert_eq!(m.opt_str("t").unwrap(), "20");
1326 let pair = m.opt_strs("test");
1327 assert!(pair[0] == "20");
1328 assert!(pair[1] == "30");
1335 fn test_unrecognized_option() {
1336 let long_args = vec!("--untest".to_string());
1337 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1338 let rs = getopts(long_args.as_slice(), opts.as_slice());
1340 Err(UnrecognizedOption(_)) => {},
1343 let short_args = vec!("-u".to_string());
1344 match getopts(short_args.as_slice(), opts.as_slice()) {
1345 Err(UnrecognizedOption(_)) => {},
1351 fn test_combined() {
1353 vec!("prog".to_string(),
1354 "free1".to_string(),
1357 "free2".to_string(),
1358 "--flag".to_string(),
1359 "--long=30".to_string(),
1368 "-60 70".to_string());
1370 vec!(optopt("s", "something", "something", "SOMETHING"),
1371 optflag("", "flag", "a flag"),
1372 reqopt("", "long", "hi", "LONG"),
1373 optflag("f", "", "another flag"),
1374 optmulti("m", "", "mmmmmm", "YUM"),
1375 optmulti("n", "", "nothing", "NOTHING"),
1376 optopt("", "notpresent", "nothing to see here", "NOPE"));
1377 let rs = getopts(args.as_slice(), opts.as_slice());
1380 assert!(m.free[0] == "prog");
1381 assert!(m.free[1] == "free1");
1382 assert_eq!(m.opt_str("s").unwrap(), "20");
1383 assert!(m.free[2] == "free2");
1384 assert!((m.opt_present("flag")));
1385 assert_eq!(m.opt_str("long").unwrap(), "30");
1386 assert!((m.opt_present("f")));
1387 let pair = m.opt_strs("m");
1388 assert!(pair[0] == "40");
1389 assert!(pair[1] == "50");
1390 let pair = m.opt_strs("n");
1391 assert!(pair[0] == "-A B");
1392 assert!(pair[1] == "-60 70");
1393 assert!((!m.opt_present("notpresent")));
1401 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1402 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1403 optopt("f", "", "flag", "FLAG"));
1405 let args_single = vec!("-e".to_string(), "foo".to_string());
1406 let matches_single = &match getopts(args_single.as_slice(),
1408 result::Result::Ok(m) => m,
1409 result::Result::Err(_) => panic!()
1411 assert!(matches_single.opts_present(&["e".to_string()]));
1412 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1413 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1414 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1415 assert!(!matches_single.opts_present(&["thing".to_string()]));
1416 assert!(!matches_single.opts_present(&[]));
1418 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1419 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1421 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1424 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1426 let matches_both = &match getopts(args_both.as_slice(),
1428 result::Result::Ok(m) => m,
1429 result::Result::Err(_) => panic!()
1431 assert!(matches_both.opts_present(&["e".to_string()]));
1432 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1433 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1434 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1435 assert!(!matches_both.opts_present(&["f".to_string()]));
1436 assert!(!matches_both.opts_present(&["thing".to_string()]));
1437 assert!(!matches_both.opts_present(&[]));
1439 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1440 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1441 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1443 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1449 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1450 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1451 optmulti("M", "", "something", "MMMM"));
1452 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1453 result::Result::Ok(m) => m,
1454 result::Result::Err(_) => panic!()
1456 assert!(matches.opts_present(&["L".to_string()]));
1457 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1458 assert!(matches.opts_present(&["M".to_string()]));
1459 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1464 fn test_nospace_conflict() {
1465 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1466 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1467 optflagmulti("v", "verbose", "Verbose"));
1468 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1469 result::Result::Ok(m) => m,
1470 result::Result::Err(e) => panic!( "{}", e )
1472 assert!(matches.opts_present(&["L".to_string()]));
1473 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1474 assert!(matches.opts_present(&["v".to_string()]));
1475 assert_eq!(3, matches.opt_count("v"));
1479 fn test_long_to_short() {
1480 let mut short = Opt {
1481 name: Name::Long("banana".to_string()),
1482 hasarg: HasArg::Yes,
1484 aliases: Vec::new(),
1486 short.aliases = vec!(Opt { name: Name::Short('b'),
1487 hasarg: HasArg::Yes,
1489 aliases: Vec::new() });
1490 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1492 assert!(verbose.long_to_short() == short);
1496 fn test_aliases_long_and_short() {
1498 optflagmulti("a", "apple", "Desc"));
1500 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1502 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1503 assert_eq!(3, matches.opt_count("a"));
1504 assert_eq!(3, matches.opt_count("apple"));
1509 let optgroups = vec!(
1510 reqopt("b", "banana", "Desc", "VAL"),
1511 optopt("a", "012345678901234567890123456789",
1513 optflag("k", "kiwi", "Desc"),
1514 optflagopt("p", "", "Desc", "VAL"),
1515 optmulti("l", "", "Desc", "VAL"));
1521 -b --banana VAL Desc
1522 -a --012345678901234567890123456789 VAL
1529 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1531 debug!("expected: <<{}>>", expected);
1532 debug!("generated: <<{}>>", generated_usage);
1533 assert_eq!(generated_usage, expected);
1537 fn test_usage_description_wrapping() {
1538 // indentation should be 24 spaces
1539 // lines wrap after 78: or rather descriptions wrap after 54
1541 let optgroups = vec!(
1542 optflag("k", "kiwi",
1543 "This is a long description which won't be wrapped..+.."), // 54
1544 optflag("a", "apple",
1545 "This is a long description which _will_ be wrapped..+.."));
1551 -k --kiwi This is a long description which won't be wrapped..+..
1552 -a --apple This is a long description which _will_ be
1556 let usage = usage("Usage: fruits", optgroups.as_slice());
1558 debug!("expected: <<{}>>", expected);
1559 debug!("generated: <<{}>>", usage);
1560 assert!(usage == expected)
1564 fn test_usage_description_multibyte_handling() {
1565 let optgroups = vec!(
1566 optflag("k", "k\u{2013}w\u{2013}",
1567 "The word kiwi is normally spelled with two i's"),
1568 optflag("a", "apple",
1569 "This \u{201C}description\u{201D} has some characters that could \
1570 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1576 -k --k–w– The word kiwi is normally spelled with two i's
1577 -a --apple This “description” has some characters that could
1578 confuse the line wrapping; an apple costs 0.51€ in
1579 some parts of Europe.
1582 let usage = usage("Usage: fruits", optgroups.as_slice());
1584 debug!("expected: <<{}>>", expected);
1585 debug!("generated: <<{}>>", usage);
1586 assert!(usage == expected)
1590 fn test_short_usage() {
1591 let optgroups = vec!(
1592 reqopt("b", "banana", "Desc", "VAL"),
1593 optopt("a", "012345678901234567890123456789",
1595 optflag("k", "kiwi", "Desc"),
1596 optflagopt("p", "", "Desc", "VAL"),
1597 optmulti("l", "", "Desc", "VAL"));
1599 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1600 let generated_usage = short_usage("fruits", optgroups.as_slice());
1602 debug!("expected: <<{}>>", expected);
1603 debug!("generated: <<{}>>", generated_usage);
1604 assert_eq!(generated_usage, expected);