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(slicing_syntax)]
89 #![deny(missing_docs)]
91 #[cfg(test)] #[macro_use] extern crate log;
98 use self::SplitWithinState::*;
99 use self::Whitespace::*;
100 use self::LengthLimit::*;
103 use std::iter::repeat;
106 /// Name of an option. Either a string or a single char.
107 #[derive(Clone, PartialEq, Eq, Show)]
109 /// A string representing the long name of an option.
110 /// For example: "help"
112 /// A char representing the short name of an option.
117 /// Describes whether an option has an argument.
118 #[derive(Clone, Copy, PartialEq, Eq, Show)]
120 /// The option requires an argument.
122 /// The option takes no argument.
124 /// The option argument is optional.
128 /// Describes how often an option may occur.
129 #[derive(Clone, Copy, PartialEq, Eq, Show)]
131 /// The option occurs once.
133 /// The option occurs at most once.
135 /// The option occurs zero or more times.
139 /// A description of a possible option.
140 #[derive(Clone, PartialEq, Eq, Show)]
142 /// Name of the option
144 /// Whether it has an argument
146 /// How often it can occur
148 /// Which options it aliases
149 pub aliases: Vec<Opt>,
152 /// One group of options, e.g., both `-h` and `--help`, along with
153 /// their shared description and properties.
154 #[derive(Clone, PartialEq, Eq, Show)]
155 pub struct OptGroup {
156 /// Short name of the option, e.g. `h` for a `-h` option
157 pub short_name: String,
158 /// Long name of the option, e.g. `help` for a `--help` option
159 pub long_name: String,
160 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
162 /// Description for usage help text
164 /// Whether option has an argument
166 /// How often it can occur
170 /// Describes whether an option is given at all or has a value.
171 #[derive(Clone, PartialEq, Eq, Show)]
177 /// The result of checking command line arguments. Contains a vector
178 /// of matches and a vector of free strings.
179 #[derive(Clone, PartialEq, Eq, Show)]
181 /// Options that matched
183 /// Values of the Options that matched
184 vals: Vec<Vec<Optval>>,
185 /// Free string fragments
186 pub free: Vec<String>,
189 /// The type returned when the command line does not conform to the
190 /// expected format. Use the `Show` implementation to output detailed
192 #[derive(Clone, PartialEq, Eq, Show)]
194 /// The option requires an argument but none was passed.
195 ArgumentMissing(String),
196 /// The passed option is not declared among the possible options.
197 UnrecognizedOption(String),
198 /// A required option is not present.
199 OptionMissing(String),
200 /// A single occurrence option is being used multiple times.
201 OptionDuplicated(String),
202 /// There's an argument being passed to a non-argument option.
203 UnexpectedArgument(String),
206 /// The type of failure that occurred.
207 #[derive(Copy, PartialEq, Eq, Show)]
208 #[allow(missing_docs)]
217 /// The result of parsing a command line with a set of options.
218 pub type Result = result::Result<Matches, Fail>;
221 fn from_str(nm: &str) -> Name {
223 Short(nm.char_at(0u))
229 fn to_string(&self) -> String {
231 Short(ch) => ch.to_string(),
232 Long(ref s) => s.to_string()
238 /// Translate OptGroup into Opt.
239 /// (Both short and long names correspond to different Opts).
240 pub fn long_to_short(&self) -> Opt {
249 match (short_name.len(), long_name.len()) {
250 (0,0) => panic!("this long-format option was given no name"),
252 name: Long((long_name)),
258 name: Short(short_name.char_at(0)),
264 name: Long((long_name)),
269 name: Short(short_name.char_at(0)),
276 (_,_) => panic!("something is wrong with the long-form opt")
282 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
283 match find_opt(self.opts.index(&FullRange), Name::from_str(nm)) {
284 Some(id) => self.vals[id].clone(),
285 None => panic!("No option '{}' defined", nm)
289 fn opt_val(&self, nm: &str) -> Option<Optval> {
290 let vals = self.opt_vals(nm);
294 Some(vals[0].clone())
298 /// Returns true if an option was matched.
299 pub fn opt_present(&self, nm: &str) -> bool {
300 !self.opt_vals(nm).is_empty()
303 /// Returns the number of times an option was matched.
304 pub fn opt_count(&self, nm: &str) -> uint {
305 self.opt_vals(nm).len()
308 /// Returns true if any of several options were matched.
309 pub fn opts_present(&self, names: &[String]) -> bool {
310 for nm in names.iter() {
311 match find_opt(self.opts.as_slice(), Name::from_str(nm.index(&FullRange))) {
312 Some(id) if !self.vals[id].is_empty() => return true,
319 /// Returns the string argument supplied to one of several matching options or `None`.
320 pub fn opts_str(&self, names: &[String]) -> Option<String> {
321 for nm in names.iter() {
322 match self.opt_val(nm.index(&FullRange)) {
323 Some(Val(ref s)) => return Some(s.clone()),
330 /// Returns a vector of the arguments provided to all matches of the given
333 /// Used when an option accepts multiple values.
334 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
335 let mut acc: Vec<String> = Vec::new();
336 let r = self.opt_vals(nm);
339 Val(ref s) => acc.push((*s).clone()),
346 /// Returns the string argument supplied to a matching option or `None`.
347 pub fn opt_str(&self, nm: &str) -> Option<String> {
348 let vals = self.opt_vals(nm);
350 return None::<String>;
353 Val(ref s) => Some((*s).clone()),
359 /// Returns the matching string, a default, or none.
361 /// Returns none if the option was not present, `def` if the option was
362 /// present but no argument was provided, and the argument if the option was
363 /// present and an argument was provided.
364 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
365 let vals = self.opt_vals(nm);
370 Val(ref s) => Some((*s).clone()),
371 _ => Some(def.to_string())
378 fn is_arg(arg: &str) -> bool {
379 arg.len() > 1 && arg.as_bytes()[0] == b'-'
382 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
383 // Search main options.
384 let pos = opts.iter().position(|opt| opt.name == nm);
389 // Search in aliases.
390 for candidate in opts.iter() {
391 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
392 return opts.iter().position(|opt| opt.name == candidate.name);
399 /// Create a long option that is required and takes an argument.
401 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
402 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
403 /// * `desc` - Description for usage help
404 /// * `hint` - Hint that is used in place of the argument in the usage help,
405 /// e.g. `"FILE"` for a `-o FILE` option
406 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
407 let len = short_name.len();
408 assert!(len == 1 || len == 0);
410 short_name: short_name.to_string(),
411 long_name: long_name.to_string(),
412 hint: hint.to_string(),
413 desc: desc.to_string(),
419 /// Create a long option that is optional and takes an argument.
421 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
422 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
423 /// * `desc` - Description for usage help
424 /// * `hint` - Hint that is used in place of the argument in the usage help,
425 /// e.g. `"FILE"` for a `-o FILE` option
426 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
427 let len = short_name.len();
428 assert!(len == 1 || len == 0);
430 short_name: short_name.to_string(),
431 long_name: long_name.to_string(),
432 hint: hint.to_string(),
433 desc: desc.to_string(),
439 /// Create a long option that is optional and does not take an argument.
441 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
442 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
443 /// * `desc` - Description for usage help
444 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
445 let len = short_name.len();
446 assert!(len == 1 || len == 0);
448 short_name: short_name.to_string(),
449 long_name: long_name.to_string(),
450 hint: "".to_string(),
451 desc: desc.to_string(),
457 /// Create a long option that can occur more than once and does not
458 /// take an argument.
460 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
461 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
462 /// * `desc` - Description for usage help
463 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
464 let len = short_name.len();
465 assert!(len == 1 || len == 0);
467 short_name: short_name.to_string(),
468 long_name: long_name.to_string(),
469 hint: "".to_string(),
470 desc: desc.to_string(),
476 /// Create a long option that is optional and takes an optional argument.
478 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
479 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
480 /// * `desc` - Description for usage help
481 /// * `hint` - Hint that is used in place of the argument in the usage help,
482 /// e.g. `"FILE"` for a `-o FILE` option
483 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
484 let len = short_name.len();
485 assert!(len == 1 || len == 0);
487 short_name: short_name.to_string(),
488 long_name: long_name.to_string(),
489 hint: hint.to_string(),
490 desc: desc.to_string(),
496 /// Create a long option that is optional, takes an argument, and may occur
499 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
500 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
501 /// * `desc` - Description for usage help
502 /// * `hint` - Hint that is used in place of the argument in the usage help,
503 /// e.g. `"FILE"` for a `-o FILE` option
504 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
505 let len = short_name.len();
506 assert!(len == 1 || len == 0);
508 short_name: short_name.to_string(),
509 long_name: long_name.to_string(),
510 hint: hint.to_string(),
511 desc: desc.to_string(),
517 /// Create a generic option group, stating all parameters explicitly
518 pub fn opt(short_name: &str,
523 occur: Occur) -> OptGroup {
524 let len = short_name.len();
525 assert!(len == 1 || len == 0);
527 short_name: short_name.to_string(),
528 long_name: long_name.to_string(),
529 hint: hint.to_string(),
530 desc: desc.to_string(),
537 /// Convert a `Fail` enum into an error string.
538 #[deprecated="use `fmt::String` (`{}` format specifier)"]
539 pub fn to_err_msg(self) -> String {
544 impl fmt::String for Fail {
545 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
547 ArgumentMissing(ref nm) => {
548 write!(f, "Argument to option '{}' missing.", *nm)
550 UnrecognizedOption(ref nm) => {
551 write!(f, "Unrecognized option: '{}'.", *nm)
553 OptionMissing(ref nm) => {
554 write!(f, "Required option '{}' missing.", *nm)
556 OptionDuplicated(ref nm) => {
557 write!(f, "Option '{}' given more than once.", *nm)
559 UnexpectedArgument(ref nm) => {
560 write!(f, "Option '{}' does not take an argument.", *nm)
566 /// Parse command line arguments according to the provided options.
568 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
569 /// `opt_str`, etc. to interrogate results.
572 /// Returns `Err(Fail)` on failure: use the `Show` implementation of `Fail` to display
573 /// information about it.
574 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
575 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
576 let n_opts = opts.len();
578 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
580 let mut vals: Vec<_> = range(0, n_opts).map(f).collect();
581 let mut free: Vec<String> = Vec::new();
585 let cur = args[i].clone();
586 let curlen = cur.len();
587 if !is_arg(cur.index(&FullRange)) {
589 } else if cur == "--" {
591 while j < l { free.push(args[j].clone()); j += 1; }
595 let mut i_arg = None;
596 if cur.as_bytes()[1] == b'-' {
597 let tail = cur.index(&(2..curlen));
598 let tail_eq: Vec<&str> = tail.split('=').collect();
599 if tail_eq.len() <= 1 {
600 names = vec!(Long(tail.to_string()));
603 vec!(Long(tail_eq[0].to_string()));
604 i_arg = Some(tail_eq[1].to_string());
610 let range = cur.char_range_at(j);
611 let opt = Short(range.ch);
613 /* In a series of potential options (eg. -aheJ), if we
614 see one which takes an argument, we assume all
615 subsequent characters make up the argument. This
616 allows options such as -L/usr/local/lib/foo to be
617 interpreted correctly
620 let opt_id = match find_opt(opts.as_slice(), opt.clone()) {
622 None => return Err(UnrecognizedOption(opt.to_string()))
627 let arg_follows = match opts[opt_id].hasarg {
632 if arg_follows && range.next < curlen {
633 i_arg = Some(cur.index(&(range.next..curlen)).to_string());
640 let mut name_pos = 0;
641 for nm in names.iter() {
643 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
645 None => return Err(UnrecognizedOption(nm.to_string()))
647 match opts[optid].hasarg {
649 if name_pos == names.len() && !i_arg.is_none() {
650 return Err(UnexpectedArgument(nm.to_string()));
652 let v = &mut vals[optid];
656 if !i_arg.is_none() {
657 let v = &mut vals[optid];
658 v.push(Val((i_arg.clone())
660 } else if name_pos < names.len() || i + 1 == l ||
661 is_arg(args[i + 1].index(&FullRange)) {
662 let v = &mut vals[optid];
666 let v = &mut vals[optid];
667 v.push(Val(args[i].clone()));
671 if !i_arg.is_none() {
672 let v = &mut vals[optid];
673 v.push(Val(i_arg.clone().unwrap()));
674 } else if i + 1 == l {
675 return Err(ArgumentMissing(nm.to_string()));
678 let v = &mut vals[optid];
679 v.push(Val(args[i].clone()));
687 for i in range(0u, n_opts) {
688 let n = vals[i].len();
689 let occ = opts[i].occur;
690 if occ == Req && n == 0 {
691 return Err(OptionMissing(opts[i].name.to_string()));
693 if occ != Multi && n > 1 {
694 return Err(OptionDuplicated(opts[i].name.to_string()));
704 /// Derive a usage message from a set of long options.
705 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
707 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
709 let rows = opts.iter().map(|optref| {
710 let OptGroup{short_name,
715 ..} = (*optref).clone();
717 let mut row = repeat(" ").take(4).collect::<String>();
720 match short_name.len() {
724 row.push_str(short_name.index(&FullRange));
727 _ => panic!("the short name should only be 1 ascii char long"),
731 match long_name.len() {
735 row.push_str(long_name.index(&FullRange));
743 Yes => row.push_str(hint.index(&FullRange)),
746 row.push_str(hint.index(&FullRange));
751 // FIXME: #5516 should be graphemes not codepoints
752 // here we just need to indent the start of the description
753 let rowlen = row.chars().count();
755 for _ in range(0, 24 - rowlen) {
759 row.push_str(desc_sep.index(&FullRange));
762 // Normalize desc to contain words separated by one space character
763 let mut desc_normalized_whitespace = String::new();
764 for word in desc.words() {
765 desc_normalized_whitespace.push_str(word);
766 desc_normalized_whitespace.push(' ');
769 // FIXME: #5516 should be graphemes not codepoints
770 let mut desc_rows = Vec::new();
771 each_split_within(desc_normalized_whitespace.index(&FullRange), 54, |substr| {
772 desc_rows.push(substr.to_string());
776 // FIXME: #5516 should be graphemes not codepoints
777 // wrapped description
778 row.push_str(desc_rows.connect(desc_sep.index(&FullRange)).index(&FullRange));
783 format!("{}\n\nOptions:\n{}\n", brief,
784 rows.collect::<Vec<String>>().connect("\n"))
787 fn format_option(opt: &OptGroup) -> String {
788 let mut line = String::new();
790 if opt.occur != Req {
794 // Use short_name is possible, but fallback to long_name.
795 if opt.short_name.len() > 0 {
797 line.push_str(opt.short_name.index(&FullRange));
800 line.push_str(opt.long_name.index(&FullRange));
803 if opt.hasarg != No {
805 if opt.hasarg == Maybe {
808 line.push_str(opt.hint.index(&FullRange));
809 if opt.hasarg == Maybe {
814 if opt.occur != Req {
817 if opt.occur == Multi {
824 /// Derive a short one-line usage summary from a set of long options.
825 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
826 let mut line = format!("Usage: {} ", program_name);
827 line.push_str(opts.iter()
829 .collect::<Vec<String>>()
830 .connect(" ").index(&FullRange));
835 enum SplitWithinState {
836 A, // leading whitespace, initial state
838 C, // internal and trailing whitespace
842 Ws, // current char is whitespace
843 Cr // current char is not whitespace
847 UnderLim, // current char makes current substring still fit in limit
848 OverLim // current char makes current substring no longer fit in limit
852 /// Splits a string into substrings with possibly internal whitespace,
853 /// each of them at most `lim` bytes long. The substrings have leading and trailing
854 /// whitespace removed, and are only cut at whitespace boundaries.
856 /// Note: Function was moved here from `std::str` because this module is the only place that
857 /// uses it, and because it was too specific for a general string function.
861 /// Panics during iteration if the string contains a non-whitespace
862 /// sequence longer than the limit.
863 fn each_split_within<F>(ss: &str, lim: uint, mut it: F) -> bool where
864 F: FnMut(&str) -> bool
866 // Just for fun, let's write this as a state machine:
868 let mut slice_start = 0;
869 let mut last_start = 0;
870 let mut last_end = 0;
872 let mut fake_i = ss.len();
877 // if the limit is larger than the string, lower it to save cycles
882 let mut machine = |&mut: cont: &mut bool, (i, c): (uint, char)| -> bool {
883 let whitespace = if c.is_whitespace() { Ws } else { Cr };
884 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
886 state = match (state, whitespace, limit) {
888 (A, Cr, _) => { slice_start = i; last_start = i; B }
890 (B, Cr, UnderLim) => { B }
891 (B, Cr, OverLim) if (i - last_start + 1) > lim
892 => panic!("word starting with {} longer than limit!",
893 ss.index(&(last_start..(i + 1)))),
894 (B, Cr, OverLim) => {
895 *cont = it(ss.index(&(slice_start..last_end)));
896 slice_start = last_start;
899 (B, Ws, UnderLim) => {
903 (B, Ws, OverLim) => {
905 *cont = it(ss.index(&(slice_start..last_end)));
909 (C, Cr, UnderLim) => {
913 (C, Cr, OverLim) => {
914 *cont = it(ss.index(&(slice_start..last_end)));
920 (C, Ws, OverLim) => {
921 *cont = it(ss.index(&(slice_start..last_end)));
924 (C, Ws, UnderLim) => {
932 ss.char_indices().all(|x| machine(&mut cont, x));
934 // Let the automaton 'run out' by supplying trailing whitespace
935 while cont && match state { B | C => true, A => false } {
936 machine(&mut cont, (fake_i, ' '));
943 fn test_split_within() {
944 fn t(s: &str, i: uint, u: &[String]) {
945 let mut v = Vec::new();
946 each_split_within(s, i, |s| { v.push(s.to_string()); true });
947 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
951 t("hello", 15, &["hello".to_string()]);
952 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
953 "Mary had a".to_string(),
954 "little lamb".to_string(),
955 "Little lamb".to_string()
957 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
958 &["Mary had a little lamb\nLittle lamb".to_string()]);
966 use std::result::Result::{Err, Ok};
972 let long_args = vec!("--test=20".to_string());
973 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
974 let rs = getopts(long_args.as_slice(), opts.as_slice());
977 assert!(m.opt_present("test"));
978 assert_eq!(m.opt_str("test").unwrap(), "20");
979 assert!(m.opt_present("t"));
980 assert_eq!(m.opt_str("t").unwrap(), "20");
982 _ => { panic!("test_reqopt failed (long arg)"); }
984 let short_args = vec!("-t".to_string(), "20".to_string());
985 match getopts(short_args.as_slice(), opts.as_slice()) {
987 assert!((m.opt_present("test")));
988 assert_eq!(m.opt_str("test").unwrap(), "20");
989 assert!((m.opt_present("t")));
990 assert_eq!(m.opt_str("t").unwrap(), "20");
992 _ => { panic!("test_reqopt failed (short arg)"); }
997 fn test_reqopt_missing() {
998 let args = vec!("blah".to_string());
999 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1000 let rs = getopts(args.as_slice(), opts.as_slice());
1002 Err(OptionMissing(_)) => {},
1008 fn test_reqopt_no_arg() {
1009 let long_args = vec!("--test".to_string());
1010 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1011 let rs = getopts(long_args.as_slice(), opts.as_slice());
1013 Err(ArgumentMissing(_)) => {},
1016 let short_args = vec!("-t".to_string());
1017 match getopts(short_args.as_slice(), opts.as_slice()) {
1018 Err(ArgumentMissing(_)) => {},
1024 fn test_reqopt_multi() {
1025 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1026 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1027 let rs = getopts(args.as_slice(), opts.as_slice());
1029 Err(OptionDuplicated(_)) => {},
1037 let long_args = vec!("--test=20".to_string());
1038 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1039 let rs = getopts(long_args.as_slice(), opts.as_slice());
1042 assert!(m.opt_present("test"));
1043 assert_eq!(m.opt_str("test").unwrap(), "20");
1044 assert!((m.opt_present("t")));
1045 assert_eq!(m.opt_str("t").unwrap(), "20");
1049 let short_args = vec!("-t".to_string(), "20".to_string());
1050 match getopts(short_args.as_slice(), opts.as_slice()) {
1052 assert!((m.opt_present("test")));
1053 assert_eq!(m.opt_str("test").unwrap(), "20");
1054 assert!((m.opt_present("t")));
1055 assert_eq!(m.opt_str("t").unwrap(), "20");
1062 fn test_optopt_missing() {
1063 let args = vec!("blah".to_string());
1064 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1065 let rs = getopts(args.as_slice(), opts.as_slice());
1068 assert!(!m.opt_present("test"));
1069 assert!(!m.opt_present("t"));
1076 fn test_optopt_no_arg() {
1077 let long_args = vec!("--test".to_string());
1078 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1079 let rs = getopts(long_args.as_slice(), opts.as_slice());
1081 Err(ArgumentMissing(_)) => {},
1084 let short_args = vec!("-t".to_string());
1085 match getopts(short_args.as_slice(), opts.as_slice()) {
1086 Err(ArgumentMissing(_)) => {},
1092 fn test_optopt_multi() {
1093 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1094 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1095 let rs = getopts(args.as_slice(), opts.as_slice());
1097 Err(OptionDuplicated(_)) => {},
1102 // Tests for optflag
1105 let long_args = vec!("--test".to_string());
1106 let opts = vec!(optflag("t", "test", "testing"));
1107 let rs = getopts(long_args.as_slice(), opts.as_slice());
1110 assert!(m.opt_present("test"));
1111 assert!(m.opt_present("t"));
1115 let short_args = vec!("-t".to_string());
1116 match getopts(short_args.as_slice(), opts.as_slice()) {
1118 assert!(m.opt_present("test"));
1119 assert!(m.opt_present("t"));
1126 fn test_optflag_missing() {
1127 let args = vec!("blah".to_string());
1128 let opts = vec!(optflag("t", "test", "testing"));
1129 let rs = getopts(args.as_slice(), opts.as_slice());
1132 assert!(!m.opt_present("test"));
1133 assert!(!m.opt_present("t"));
1140 fn test_optflag_long_arg() {
1141 let args = vec!("--test=20".to_string());
1142 let opts = vec!(optflag("t", "test", "testing"));
1143 let rs = getopts(args.as_slice(), opts.as_slice());
1145 Err(UnexpectedArgument(_)) => {},
1151 fn test_optflag_multi() {
1152 let args = vec!("--test".to_string(), "-t".to_string());
1153 let opts = vec!(optflag("t", "test", "testing"));
1154 let rs = getopts(args.as_slice(), opts.as_slice());
1156 Err(OptionDuplicated(_)) => {},
1162 fn test_optflag_short_arg() {
1163 let args = vec!("-t".to_string(), "20".to_string());
1164 let opts = vec!(optflag("t", "test", "testing"));
1165 let rs = getopts(args.as_slice(), opts.as_slice());
1168 // The next variable after the flag is just a free argument
1170 assert!(m.free[0] == "20");
1176 // Tests for optflagmulti
1178 fn test_optflagmulti_short1() {
1179 let args = vec!("-v".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"), 1);
1191 fn test_optflagmulti_short2a() {
1192 let args = vec!("-v".to_string(), "-v".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("v"), 2);
1204 fn test_optflagmulti_short2b() {
1205 let args = vec!("-vv".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("v"), 2);
1217 fn test_optflagmulti_long1() {
1218 let args = vec!("--verbose".to_string());
1219 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1220 let rs = getopts(args.as_slice(), opts.as_slice());
1223 assert_eq!(m.opt_count("verbose"), 1);
1230 fn test_optflagmulti_long2() {
1231 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1232 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1233 let rs = getopts(args.as_slice(), opts.as_slice());
1236 assert_eq!(m.opt_count("verbose"), 2);
1243 fn test_optflagmulti_mix() {
1244 let args = vec!("--verbose".to_string(), "-v".to_string(),
1245 "-vv".to_string(), "verbose".to_string());
1246 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1247 let rs = getopts(args.as_slice(), opts.as_slice());
1250 assert_eq!(m.opt_count("verbose"), 4);
1251 assert_eq!(m.opt_count("v"), 4);
1257 // Tests for optmulti
1259 fn test_optmulti() {
1260 let long_args = vec!("--test=20".to_string());
1261 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1262 let rs = getopts(long_args.as_slice(), opts.as_slice());
1265 assert!((m.opt_present("test")));
1266 assert_eq!(m.opt_str("test").unwrap(), "20");
1267 assert!((m.opt_present("t")));
1268 assert_eq!(m.opt_str("t").unwrap(), "20");
1272 let short_args = vec!("-t".to_string(), "20".to_string());
1273 match getopts(short_args.as_slice(), opts.as_slice()) {
1275 assert!((m.opt_present("test")));
1276 assert_eq!(m.opt_str("test").unwrap(), "20");
1277 assert!((m.opt_present("t")));
1278 assert_eq!(m.opt_str("t").unwrap(), "20");
1285 fn test_optmulti_missing() {
1286 let args = vec!("blah".to_string());
1287 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1288 let rs = getopts(args.as_slice(), opts.as_slice());
1291 assert!(!m.opt_present("test"));
1292 assert!(!m.opt_present("t"));
1299 fn test_optmulti_no_arg() {
1300 let long_args = vec!("--test".to_string());
1301 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1302 let rs = getopts(long_args.as_slice(), opts.as_slice());
1304 Err(ArgumentMissing(_)) => {},
1307 let short_args = vec!("-t".to_string());
1308 match getopts(short_args.as_slice(), opts.as_slice()) {
1309 Err(ArgumentMissing(_)) => {},
1315 fn test_optmulti_multi() {
1316 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1317 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1318 let rs = getopts(args.as_slice(), opts.as_slice());
1321 assert!(m.opt_present("test"));
1322 assert_eq!(m.opt_str("test").unwrap(), "20");
1323 assert!(m.opt_present("t"));
1324 assert_eq!(m.opt_str("t").unwrap(), "20");
1325 let pair = m.opt_strs("test");
1326 assert!(pair[0] == "20");
1327 assert!(pair[1] == "30");
1334 fn test_unrecognized_option() {
1335 let long_args = vec!("--untest".to_string());
1336 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1337 let rs = getopts(long_args.as_slice(), opts.as_slice());
1339 Err(UnrecognizedOption(_)) => {},
1342 let short_args = vec!("-u".to_string());
1343 match getopts(short_args.as_slice(), opts.as_slice()) {
1344 Err(UnrecognizedOption(_)) => {},
1350 fn test_combined() {
1352 vec!("prog".to_string(),
1353 "free1".to_string(),
1356 "free2".to_string(),
1357 "--flag".to_string(),
1358 "--long=30".to_string(),
1367 "-60 70".to_string());
1369 vec!(optopt("s", "something", "something", "SOMETHING"),
1370 optflag("", "flag", "a flag"),
1371 reqopt("", "long", "hi", "LONG"),
1372 optflag("f", "", "another flag"),
1373 optmulti("m", "", "mmmmmm", "YUM"),
1374 optmulti("n", "", "nothing", "NOTHING"),
1375 optopt("", "notpresent", "nothing to see here", "NOPE"));
1376 let rs = getopts(args.as_slice(), opts.as_slice());
1379 assert!(m.free[0] == "prog");
1380 assert!(m.free[1] == "free1");
1381 assert_eq!(m.opt_str("s").unwrap(), "20");
1382 assert!(m.free[2] == "free2");
1383 assert!((m.opt_present("flag")));
1384 assert_eq!(m.opt_str("long").unwrap(), "30");
1385 assert!((m.opt_present("f")));
1386 let pair = m.opt_strs("m");
1387 assert!(pair[0] == "40");
1388 assert!(pair[1] == "50");
1389 let pair = m.opt_strs("n");
1390 assert!(pair[0] == "-A B");
1391 assert!(pair[1] == "-60 70");
1392 assert!((!m.opt_present("notpresent")));
1400 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1401 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1402 optopt("f", "", "flag", "FLAG"));
1404 let args_single = vec!("-e".to_string(), "foo".to_string());
1405 let matches_single = &match getopts(args_single.as_slice(),
1407 result::Result::Ok(m) => m,
1408 result::Result::Err(_) => panic!()
1410 assert!(matches_single.opts_present(&["e".to_string()]));
1411 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1412 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1413 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1414 assert!(!matches_single.opts_present(&["thing".to_string()]));
1415 assert!(!matches_single.opts_present(&[]));
1417 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1418 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1420 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1423 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1425 let matches_both = &match getopts(args_both.as_slice(),
1427 result::Result::Ok(m) => m,
1428 result::Result::Err(_) => panic!()
1430 assert!(matches_both.opts_present(&["e".to_string()]));
1431 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1432 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1433 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1434 assert!(!matches_both.opts_present(&["f".to_string()]));
1435 assert!(!matches_both.opts_present(&["thing".to_string()]));
1436 assert!(!matches_both.opts_present(&[]));
1438 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1439 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1440 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1442 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1448 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1449 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1450 optmulti("M", "", "something", "MMMM"));
1451 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1452 result::Result::Ok(m) => m,
1453 result::Result::Err(_) => panic!()
1455 assert!(matches.opts_present(&["L".to_string()]));
1456 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1457 assert!(matches.opts_present(&["M".to_string()]));
1458 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1463 fn test_nospace_conflict() {
1464 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1465 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1466 optflagmulti("v", "verbose", "Verbose"));
1467 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1468 result::Result::Ok(m) => m,
1469 result::Result::Err(e) => panic!( "{}", e )
1471 assert!(matches.opts_present(&["L".to_string()]));
1472 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1473 assert!(matches.opts_present(&["v".to_string()]));
1474 assert_eq!(3, matches.opt_count("v"));
1478 fn test_long_to_short() {
1479 let mut short = Opt {
1480 name: Name::Long("banana".to_string()),
1481 hasarg: HasArg::Yes,
1483 aliases: Vec::new(),
1485 short.aliases = vec!(Opt { name: Name::Short('b'),
1486 hasarg: HasArg::Yes,
1488 aliases: Vec::new() });
1489 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1491 assert!(verbose.long_to_short() == short);
1495 fn test_aliases_long_and_short() {
1497 optflagmulti("a", "apple", "Desc"));
1499 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1501 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1502 assert_eq!(3, matches.opt_count("a"));
1503 assert_eq!(3, matches.opt_count("apple"));
1508 let optgroups = vec!(
1509 reqopt("b", "banana", "Desc", "VAL"),
1510 optopt("a", "012345678901234567890123456789",
1512 optflag("k", "kiwi", "Desc"),
1513 optflagopt("p", "", "Desc", "VAL"),
1514 optmulti("l", "", "Desc", "VAL"));
1520 -b --banana VAL Desc
1521 -a --012345678901234567890123456789 VAL
1528 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1530 debug!("expected: <<{}>>", expected);
1531 debug!("generated: <<{}>>", generated_usage);
1532 assert_eq!(generated_usage, expected);
1536 fn test_usage_description_wrapping() {
1537 // indentation should be 24 spaces
1538 // lines wrap after 78: or rather descriptions wrap after 54
1540 let optgroups = vec!(
1541 optflag("k", "kiwi",
1542 "This is a long description which won't be wrapped..+.."), // 54
1543 optflag("a", "apple",
1544 "This is a long description which _will_ be wrapped..+.."));
1550 -k --kiwi This is a long description which won't be wrapped..+..
1551 -a --apple This is a long description which _will_ be
1555 let usage = usage("Usage: fruits", optgroups.as_slice());
1557 debug!("expected: <<{}>>", expected);
1558 debug!("generated: <<{}>>", usage);
1559 assert!(usage == expected)
1563 fn test_usage_description_multibyte_handling() {
1564 let optgroups = vec!(
1565 optflag("k", "k\u{2013}w\u{2013}",
1566 "The word kiwi is normally spelled with two i's"),
1567 optflag("a", "apple",
1568 "This \u{201C}description\u{201D} has some characters that could \
1569 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1575 -k --k–w– The word kiwi is normally spelled with two i's
1576 -a --apple This “description” has some characters that could
1577 confuse the line wrapping; an apple costs 0.51€ in
1578 some parts of Europe.
1581 let usage = usage("Usage: fruits", optgroups.as_slice());
1583 debug!("expected: <<{}>>", expected);
1584 debug!("generated: <<{}>>", usage);
1585 assert!(usage == expected)
1589 fn test_short_usage() {
1590 let optgroups = vec!(
1591 reqopt("b", "banana", "Desc", "VAL"),
1592 optopt("a", "012345678901234567890123456789",
1594 optflag("k", "kiwi", "Desc"),
1595 optflagopt("p", "", "Desc", "VAL"),
1596 optmulti("l", "", "Desc", "VAL"));
1598 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1599 let generated_usage = short_usage("fruits", optgroups.as_slice());
1601 debug!("expected: <<{}>>", expected);
1602 debug!("generated: <<{}>>", generated_usage);
1603 assert_eq!(generated_usage, expected);