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 #![unstable = "use the crates.io `getopts` library instead"]
83 #![crate_type = "rlib"]
84 #![crate_type = "dylib"]
85 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
86 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
87 html_root_url = "http://doc.rust-lang.org/nightly/",
88 html_playground_url = "http://play.rust-lang.org/")]
89 #![feature(slicing_syntax)]
90 #![allow(unknown_features)] #![feature(int_uint)]
92 #![deny(missing_docs)]
94 #[cfg(test)] #[macro_use] extern crate log;
101 use self::SplitWithinState::*;
102 use self::Whitespace::*;
103 use self::LengthLimit::*;
106 use std::iter::repeat;
109 /// Name of an option. Either a string or a single char.
110 #[derive(Clone, PartialEq, Eq, Show)]
112 /// A string representing the long name of an option.
113 /// For example: "help"
115 /// A char representing the short name of an option.
120 /// Describes whether an option has an argument.
121 #[derive(Clone, Copy, PartialEq, Eq, Show)]
123 /// The option requires an argument.
125 /// The option takes no argument.
127 /// The option argument is optional.
131 /// Describes how often an option may occur.
132 #[derive(Clone, Copy, PartialEq, Eq, Show)]
134 /// The option occurs once.
136 /// The option occurs at most once.
138 /// The option occurs zero or more times.
142 /// A description of a possible option.
143 #[derive(Clone, PartialEq, Eq, Show)]
145 /// Name of the option
147 /// Whether it has an argument
149 /// How often it can occur
151 /// Which options it aliases
152 pub aliases: Vec<Opt>,
155 /// One group of options, e.g., both `-h` and `--help`, along with
156 /// their shared description and properties.
157 #[derive(Clone, PartialEq, Eq, Show)]
158 pub struct OptGroup {
159 /// Short name of the option, e.g. `h` for a `-h` option
160 pub short_name: String,
161 /// Long name of the option, e.g. `help` for a `--help` option
162 pub long_name: String,
163 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
165 /// Description for usage help text
167 /// Whether option has an argument
169 /// How often it can occur
173 /// Describes whether an option is given at all or has a value.
174 #[derive(Clone, PartialEq, Eq, Show)]
180 /// The result of checking command line arguments. Contains a vector
181 /// of matches and a vector of free strings.
182 #[derive(Clone, PartialEq, Eq, Show)]
184 /// Options that matched
186 /// Values of the Options that matched
187 vals: Vec<Vec<Optval>>,
188 /// Free string fragments
189 pub free: Vec<String>,
192 /// The type returned when the command line does not conform to the
193 /// expected format. Use the `Show` implementation to output detailed
195 #[derive(Clone, PartialEq, Eq, Show)]
197 /// The option requires an argument but none was passed.
198 ArgumentMissing(String),
199 /// The passed option is not declared among the possible options.
200 UnrecognizedOption(String),
201 /// A required option is not present.
202 OptionMissing(String),
203 /// A single occurrence option is being used multiple times.
204 OptionDuplicated(String),
205 /// There's an argument being passed to a non-argument option.
206 UnexpectedArgument(String),
209 /// The type of failure that occurred.
210 #[derive(Copy, PartialEq, Eq, Show)]
211 #[allow(missing_docs)]
220 /// The result of parsing a command line with a set of options.
221 pub type Result = result::Result<Matches, Fail>;
224 fn from_str(nm: &str) -> Name {
226 Short(nm.char_at(0u))
232 fn to_string(&self) -> String {
234 Short(ch) => ch.to_string(),
235 Long(ref s) => s.to_string()
241 /// Translate OptGroup into Opt.
242 /// (Both short and long names correspond to different Opts).
243 pub fn long_to_short(&self) -> Opt {
252 match (short_name.len(), long_name.len()) {
253 (0,0) => panic!("this long-format option was given no name"),
255 name: Long((long_name)),
261 name: Short(short_name.char_at(0)),
267 name: Long((long_name)),
272 name: Short(short_name.char_at(0)),
279 (_,_) => panic!("something is wrong with the long-form opt")
285 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
286 match find_opt(&self.opts[], Name::from_str(nm)) {
287 Some(id) => self.vals[id].clone(),
288 None => panic!("No option '{}' defined", nm)
292 fn opt_val(&self, nm: &str) -> Option<Optval> {
293 let vals = self.opt_vals(nm);
297 Some(vals[0].clone())
301 /// Returns true if an option was matched.
302 pub fn opt_present(&self, nm: &str) -> bool {
303 !self.opt_vals(nm).is_empty()
306 /// Returns the number of times an option was matched.
307 pub fn opt_count(&self, nm: &str) -> uint {
308 self.opt_vals(nm).len()
311 /// Returns true if any of several options were matched.
312 pub fn opts_present(&self, names: &[String]) -> bool {
313 for nm in names.iter() {
314 match find_opt(self.opts.as_slice(), Name::from_str(&nm[])) {
315 Some(id) if !self.vals[id].is_empty() => return true,
322 /// Returns the string argument supplied to one of several matching options or `None`.
323 pub fn opts_str(&self, names: &[String]) -> Option<String> {
324 for nm in names.iter() {
325 match self.opt_val(&nm[]) {
326 Some(Val(ref s)) => return Some(s.clone()),
333 /// Returns a vector of the arguments provided to all matches of the given
336 /// Used when an option accepts multiple values.
337 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
338 let mut acc: Vec<String> = Vec::new();
339 let r = self.opt_vals(nm);
342 Val(ref s) => acc.push((*s).clone()),
349 /// Returns the string argument supplied to a matching option or `None`.
350 pub fn opt_str(&self, nm: &str) -> Option<String> {
351 let vals = self.opt_vals(nm);
353 return None::<String>;
356 Val(ref s) => Some((*s).clone()),
362 /// Returns the matching string, a default, or none.
364 /// Returns none if the option was not present, `def` if the option was
365 /// present but no argument was provided, and the argument if the option was
366 /// present and an argument was provided.
367 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
368 let vals = self.opt_vals(nm);
373 Val(ref s) => Some((*s).clone()),
374 _ => Some(def.to_string())
381 fn is_arg(arg: &str) -> bool {
382 arg.len() > 1 && arg.as_bytes()[0] == b'-'
385 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
386 // Search main options.
387 let pos = opts.iter().position(|opt| opt.name == nm);
392 // Search in aliases.
393 for candidate in opts.iter() {
394 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
395 return opts.iter().position(|opt| opt.name == candidate.name);
402 /// Create a long option that is required and takes an argument.
404 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
405 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
406 /// * `desc` - Description for usage help
407 /// * `hint` - Hint that is used in place of the argument in the usage help,
408 /// e.g. `"FILE"` for a `-o FILE` option
409 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
410 let len = short_name.len();
411 assert!(len == 1 || len == 0);
413 short_name: short_name.to_string(),
414 long_name: long_name.to_string(),
415 hint: hint.to_string(),
416 desc: desc.to_string(),
422 /// Create a long option that is optional and takes an argument.
424 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
425 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
426 /// * `desc` - Description for usage help
427 /// * `hint` - Hint that is used in place of the argument in the usage help,
428 /// e.g. `"FILE"` for a `-o FILE` option
429 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
430 let len = short_name.len();
431 assert!(len == 1 || len == 0);
433 short_name: short_name.to_string(),
434 long_name: long_name.to_string(),
435 hint: hint.to_string(),
436 desc: desc.to_string(),
442 /// Create a long option that is optional and does not take an argument.
444 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
445 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
446 /// * `desc` - Description for usage help
447 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
448 let len = short_name.len();
449 assert!(len == 1 || len == 0);
451 short_name: short_name.to_string(),
452 long_name: long_name.to_string(),
453 hint: "".to_string(),
454 desc: desc.to_string(),
460 /// Create a long option that can occur more than once and does not
461 /// take an argument.
463 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
464 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
465 /// * `desc` - Description for usage help
466 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
467 let len = short_name.len();
468 assert!(len == 1 || len == 0);
470 short_name: short_name.to_string(),
471 long_name: long_name.to_string(),
472 hint: "".to_string(),
473 desc: desc.to_string(),
479 /// Create a long option that is optional and takes an optional argument.
481 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
482 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
483 /// * `desc` - Description for usage help
484 /// * `hint` - Hint that is used in place of the argument in the usage help,
485 /// e.g. `"FILE"` for a `-o FILE` option
486 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
487 let len = short_name.len();
488 assert!(len == 1 || len == 0);
490 short_name: short_name.to_string(),
491 long_name: long_name.to_string(),
492 hint: hint.to_string(),
493 desc: desc.to_string(),
499 /// Create a long option that is optional, takes an argument, and may occur
502 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
503 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
504 /// * `desc` - Description for usage help
505 /// * `hint` - Hint that is used in place of the argument in the usage help,
506 /// e.g. `"FILE"` for a `-o FILE` option
507 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
508 let len = short_name.len();
509 assert!(len == 1 || len == 0);
511 short_name: short_name.to_string(),
512 long_name: long_name.to_string(),
513 hint: hint.to_string(),
514 desc: desc.to_string(),
520 /// Create a generic option group, stating all parameters explicitly
521 pub fn opt(short_name: &str,
526 occur: Occur) -> OptGroup {
527 let len = short_name.len();
528 assert!(len == 1 || len == 0);
530 short_name: short_name.to_string(),
531 long_name: long_name.to_string(),
532 hint: hint.to_string(),
533 desc: desc.to_string(),
540 /// Convert a `Fail` enum into an error string.
541 #[deprecated="use `fmt::String` (`{}` format specifier)"]
542 pub fn to_err_msg(self) -> String {
547 impl fmt::Display for Fail {
548 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
550 ArgumentMissing(ref nm) => {
551 write!(f, "Argument to option '{}' missing.", *nm)
553 UnrecognizedOption(ref nm) => {
554 write!(f, "Unrecognized option: '{}'.", *nm)
556 OptionMissing(ref nm) => {
557 write!(f, "Required option '{}' missing.", *nm)
559 OptionDuplicated(ref nm) => {
560 write!(f, "Option '{}' given more than once.", *nm)
562 UnexpectedArgument(ref nm) => {
563 write!(f, "Option '{}' does not take an argument.", *nm)
569 /// Parse command line arguments according to the provided options.
571 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
572 /// `opt_str`, etc. to interrogate results.
575 /// Returns `Err(Fail)` on failure: use the `Show` implementation of `Fail` to display
576 /// information about it.
577 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
578 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
579 let n_opts = opts.len();
581 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
583 let mut vals: Vec<_> = range(0, n_opts).map(f).collect();
584 let mut free: Vec<String> = Vec::new();
588 let cur = args[i].clone();
589 let curlen = cur.len();
592 } else if cur == "--" {
594 while j < l { free.push(args[j].clone()); j += 1; }
598 let mut i_arg = None;
599 if cur.as_bytes()[1] == b'-' {
600 let tail = &cur[2..curlen];
601 let tail_eq: Vec<&str> = tail.split('=').collect();
602 if tail_eq.len() <= 1 {
603 names = vec!(Long(tail.to_string()));
606 vec!(Long(tail_eq[0].to_string()));
607 i_arg = Some(tail_eq[1].to_string());
613 let range = cur.char_range_at(j);
614 let opt = Short(range.ch);
616 /* In a series of potential options (eg. -aheJ), if we
617 see one which takes an argument, we assume all
618 subsequent characters make up the argument. This
619 allows options such as -L/usr/local/lib/foo to be
620 interpreted correctly
623 let opt_id = match find_opt(opts.as_slice(), opt.clone()) {
625 None => return Err(UnrecognizedOption(opt.to_string()))
630 let arg_follows = match opts[opt_id].hasarg {
635 if arg_follows && range.next < curlen {
636 i_arg = Some((&cur[range.next..curlen]).to_string());
643 let mut name_pos = 0;
644 for nm in names.iter() {
646 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
648 None => return Err(UnrecognizedOption(nm.to_string()))
650 match opts[optid].hasarg {
652 if name_pos == names.len() && !i_arg.is_none() {
653 return Err(UnexpectedArgument(nm.to_string()));
655 let v = &mut vals[optid];
659 if !i_arg.is_none() {
660 let v = &mut vals[optid];
661 v.push(Val((i_arg.clone())
663 } else if name_pos < names.len() || i + 1 == l ||
664 is_arg(&args[i + 1][]) {
665 let v = &mut vals[optid];
669 let v = &mut vals[optid];
670 v.push(Val(args[i].clone()));
674 if !i_arg.is_none() {
675 let v = &mut vals[optid];
676 v.push(Val(i_arg.clone().unwrap()));
677 } else if i + 1 == l {
678 return Err(ArgumentMissing(nm.to_string()));
681 let v = &mut vals[optid];
682 v.push(Val(args[i].clone()));
690 for i in range(0u, n_opts) {
691 let n = vals[i].len();
692 let occ = opts[i].occur;
693 if occ == Req && n == 0 {
694 return Err(OptionMissing(opts[i].name.to_string()));
696 if occ != Multi && n > 1 {
697 return Err(OptionDuplicated(opts[i].name.to_string()));
707 /// Derive a usage message from a set of long options.
708 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
710 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
712 let rows = opts.iter().map(|optref| {
713 let OptGroup{short_name,
718 ..} = (*optref).clone();
720 let mut row = repeat(" ").take(4).collect::<String>();
723 match short_name.len() {
727 row.push_str(&short_name[]);
730 _ => panic!("the short name should only be 1 ascii char long"),
734 match long_name.len() {
738 row.push_str(&long_name[]);
746 Yes => row.push_str(&hint[]),
749 row.push_str(&hint[]);
754 // FIXME: #5516 should be graphemes not codepoints
755 // here we just need to indent the start of the description
756 let rowlen = row.chars().count();
758 for _ in range(0, 24 - rowlen) {
762 row.push_str(&desc_sep[]);
765 // Normalize desc to contain words separated by one space character
766 let mut desc_normalized_whitespace = String::new();
767 for word in desc.words() {
768 desc_normalized_whitespace.push_str(word);
769 desc_normalized_whitespace.push(' ');
772 // FIXME: #5516 should be graphemes not codepoints
773 let mut desc_rows = Vec::new();
774 each_split_within(&desc_normalized_whitespace[], 54, |substr| {
775 desc_rows.push(substr.to_string());
779 // FIXME: #5516 should be graphemes not codepoints
780 // wrapped description
781 row.push_str(&desc_rows.connect(&desc_sep[])[]);
786 format!("{}\n\nOptions:\n{}\n", brief,
787 rows.collect::<Vec<String>>().connect("\n"))
790 fn format_option(opt: &OptGroup) -> String {
791 let mut line = String::new();
793 if opt.occur != Req {
797 // Use short_name is possible, but fallback to long_name.
798 if opt.short_name.len() > 0 {
800 line.push_str(&opt.short_name[]);
803 line.push_str(&opt.long_name[]);
806 if opt.hasarg != No {
808 if opt.hasarg == Maybe {
811 line.push_str(&opt.hint[]);
812 if opt.hasarg == Maybe {
817 if opt.occur != Req {
820 if opt.occur == Multi {
827 /// Derive a short one-line usage summary from a set of long options.
828 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
829 let mut line = format!("Usage: {} ", program_name);
830 line.push_str(&opts.iter()
832 .collect::<Vec<String>>()
838 enum SplitWithinState {
839 A, // leading whitespace, initial state
841 C, // internal and trailing whitespace
845 Ws, // current char is whitespace
846 Cr // current char is not whitespace
850 UnderLim, // current char makes current substring still fit in limit
851 OverLim // current char makes current substring no longer fit in limit
855 /// Splits a string into substrings with possibly internal whitespace,
856 /// each of them at most `lim` bytes long. The substrings have leading and trailing
857 /// whitespace removed, and are only cut at whitespace boundaries.
859 /// Note: Function was moved here from `std::str` because this module is the only place that
860 /// uses it, and because it was too specific for a general string function.
864 /// Panics during iteration if the string contains a non-whitespace
865 /// sequence longer than the limit.
866 fn each_split_within<F>(ss: &str, lim: uint, mut it: F) -> bool where
867 F: FnMut(&str) -> bool
869 // Just for fun, let's write this as a state machine:
871 let mut slice_start = 0;
872 let mut last_start = 0;
873 let mut last_end = 0;
875 let mut fake_i = ss.len();
880 // if the limit is larger than the string, lower it to save cycles
885 let mut machine = |&mut: cont: &mut bool, (i, c): (uint, char)| -> bool {
886 let whitespace = if c.is_whitespace() { Ws } else { Cr };
887 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
889 state = match (state, whitespace, limit) {
891 (A, Cr, _) => { slice_start = i; last_start = i; B }
893 (B, Cr, UnderLim) => { B }
894 (B, Cr, OverLim) if (i - last_start + 1) > lim
895 => panic!("word starting with {} longer than limit!",
896 &ss[last_start..(i + 1)]),
897 (B, Cr, OverLim) => {
898 *cont = it(&ss[slice_start..last_end]);
899 slice_start = last_start;
902 (B, Ws, UnderLim) => {
906 (B, Ws, OverLim) => {
908 *cont = it(&ss[slice_start..last_end]);
912 (C, Cr, UnderLim) => {
916 (C, Cr, OverLim) => {
917 *cont = it(&ss[slice_start..last_end]);
923 (C, Ws, OverLim) => {
924 *cont = it(&ss[slice_start..last_end]);
927 (C, Ws, UnderLim) => {
935 ss.char_indices().all(|x| machine(&mut cont, x));
937 // Let the automaton 'run out' by supplying trailing whitespace
938 while cont && match state { B | C => true, A => false } {
939 machine(&mut cont, (fake_i, ' '));
946 fn test_split_within() {
947 fn t(s: &str, i: uint, u: &[String]) {
948 let mut v = Vec::new();
949 each_split_within(s, i, |s| { v.push(s.to_string()); true });
950 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
954 t("hello", 15, &["hello".to_string()]);
955 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
956 "Mary had a".to_string(),
957 "little lamb".to_string(),
958 "Little lamb".to_string()
960 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
961 &["Mary had a little lamb\nLittle lamb".to_string()]);
969 use std::result::Result::{Err, Ok};
975 let long_args = vec!("--test=20".to_string());
976 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
977 let rs = getopts(long_args.as_slice(), opts.as_slice());
980 assert!(m.opt_present("test"));
981 assert_eq!(m.opt_str("test").unwrap(), "20");
982 assert!(m.opt_present("t"));
983 assert_eq!(m.opt_str("t").unwrap(), "20");
985 _ => { panic!("test_reqopt failed (long arg)"); }
987 let short_args = vec!("-t".to_string(), "20".to_string());
988 match getopts(short_args.as_slice(), opts.as_slice()) {
990 assert!((m.opt_present("test")));
991 assert_eq!(m.opt_str("test").unwrap(), "20");
992 assert!((m.opt_present("t")));
993 assert_eq!(m.opt_str("t").unwrap(), "20");
995 _ => { panic!("test_reqopt failed (short arg)"); }
1000 fn test_reqopt_missing() {
1001 let args = vec!("blah".to_string());
1002 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1003 let rs = getopts(args.as_slice(), opts.as_slice());
1005 Err(OptionMissing(_)) => {},
1011 fn test_reqopt_no_arg() {
1012 let long_args = vec!("--test".to_string());
1013 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1014 let rs = getopts(long_args.as_slice(), opts.as_slice());
1016 Err(ArgumentMissing(_)) => {},
1019 let short_args = vec!("-t".to_string());
1020 match getopts(short_args.as_slice(), opts.as_slice()) {
1021 Err(ArgumentMissing(_)) => {},
1027 fn test_reqopt_multi() {
1028 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1029 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1030 let rs = getopts(args.as_slice(), opts.as_slice());
1032 Err(OptionDuplicated(_)) => {},
1040 let long_args = vec!("--test=20".to_string());
1041 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1042 let rs = getopts(long_args.as_slice(), opts.as_slice());
1045 assert!(m.opt_present("test"));
1046 assert_eq!(m.opt_str("test").unwrap(), "20");
1047 assert!((m.opt_present("t")));
1048 assert_eq!(m.opt_str("t").unwrap(), "20");
1052 let short_args = vec!("-t".to_string(), "20".to_string());
1053 match getopts(short_args.as_slice(), opts.as_slice()) {
1055 assert!((m.opt_present("test")));
1056 assert_eq!(m.opt_str("test").unwrap(), "20");
1057 assert!((m.opt_present("t")));
1058 assert_eq!(m.opt_str("t").unwrap(), "20");
1065 fn test_optopt_missing() {
1066 let args = vec!("blah".to_string());
1067 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1068 let rs = getopts(args.as_slice(), opts.as_slice());
1071 assert!(!m.opt_present("test"));
1072 assert!(!m.opt_present("t"));
1079 fn test_optopt_no_arg() {
1080 let long_args = vec!("--test".to_string());
1081 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1082 let rs = getopts(long_args.as_slice(), opts.as_slice());
1084 Err(ArgumentMissing(_)) => {},
1087 let short_args = vec!("-t".to_string());
1088 match getopts(short_args.as_slice(), opts.as_slice()) {
1089 Err(ArgumentMissing(_)) => {},
1095 fn test_optopt_multi() {
1096 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1097 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1098 let rs = getopts(args.as_slice(), opts.as_slice());
1100 Err(OptionDuplicated(_)) => {},
1105 // Tests for optflag
1108 let long_args = vec!("--test".to_string());
1109 let opts = vec!(optflag("t", "test", "testing"));
1110 let rs = getopts(long_args.as_slice(), opts.as_slice());
1113 assert!(m.opt_present("test"));
1114 assert!(m.opt_present("t"));
1118 let short_args = vec!("-t".to_string());
1119 match getopts(short_args.as_slice(), opts.as_slice()) {
1121 assert!(m.opt_present("test"));
1122 assert!(m.opt_present("t"));
1129 fn test_optflag_missing() {
1130 let args = vec!("blah".to_string());
1131 let opts = vec!(optflag("t", "test", "testing"));
1132 let rs = getopts(args.as_slice(), opts.as_slice());
1135 assert!(!m.opt_present("test"));
1136 assert!(!m.opt_present("t"));
1143 fn test_optflag_long_arg() {
1144 let args = vec!("--test=20".to_string());
1145 let opts = vec!(optflag("t", "test", "testing"));
1146 let rs = getopts(args.as_slice(), opts.as_slice());
1148 Err(UnexpectedArgument(_)) => {},
1154 fn test_optflag_multi() {
1155 let args = vec!("--test".to_string(), "-t".to_string());
1156 let opts = vec!(optflag("t", "test", "testing"));
1157 let rs = getopts(args.as_slice(), opts.as_slice());
1159 Err(OptionDuplicated(_)) => {},
1165 fn test_optflag_short_arg() {
1166 let args = vec!("-t".to_string(), "20".to_string());
1167 let opts = vec!(optflag("t", "test", "testing"));
1168 let rs = getopts(args.as_slice(), opts.as_slice());
1171 // The next variable after the flag is just a free argument
1173 assert!(m.free[0] == "20");
1179 // Tests for optflagmulti
1181 fn test_optflagmulti_short1() {
1182 let args = vec!("-v".to_string());
1183 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1184 let rs = getopts(args.as_slice(), opts.as_slice());
1187 assert_eq!(m.opt_count("v"), 1);
1194 fn test_optflagmulti_short2a() {
1195 let args = vec!("-v".to_string(), "-v".to_string());
1196 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1197 let rs = getopts(args.as_slice(), opts.as_slice());
1200 assert_eq!(m.opt_count("v"), 2);
1207 fn test_optflagmulti_short2b() {
1208 let args = vec!("-vv".to_string());
1209 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1210 let rs = getopts(args.as_slice(), opts.as_slice());
1213 assert_eq!(m.opt_count("v"), 2);
1220 fn test_optflagmulti_long1() {
1221 let args = vec!("--verbose".to_string());
1222 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1223 let rs = getopts(args.as_slice(), opts.as_slice());
1226 assert_eq!(m.opt_count("verbose"), 1);
1233 fn test_optflagmulti_long2() {
1234 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1235 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1236 let rs = getopts(args.as_slice(), opts.as_slice());
1239 assert_eq!(m.opt_count("verbose"), 2);
1246 fn test_optflagmulti_mix() {
1247 let args = vec!("--verbose".to_string(), "-v".to_string(),
1248 "-vv".to_string(), "verbose".to_string());
1249 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1250 let rs = getopts(args.as_slice(), opts.as_slice());
1253 assert_eq!(m.opt_count("verbose"), 4);
1254 assert_eq!(m.opt_count("v"), 4);
1260 // Tests for optmulti
1262 fn test_optmulti() {
1263 let long_args = vec!("--test=20".to_string());
1264 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1265 let rs = getopts(long_args.as_slice(), opts.as_slice());
1268 assert!((m.opt_present("test")));
1269 assert_eq!(m.opt_str("test").unwrap(), "20");
1270 assert!((m.opt_present("t")));
1271 assert_eq!(m.opt_str("t").unwrap(), "20");
1275 let short_args = vec!("-t".to_string(), "20".to_string());
1276 match getopts(short_args.as_slice(), opts.as_slice()) {
1278 assert!((m.opt_present("test")));
1279 assert_eq!(m.opt_str("test").unwrap(), "20");
1280 assert!((m.opt_present("t")));
1281 assert_eq!(m.opt_str("t").unwrap(), "20");
1288 fn test_optmulti_missing() {
1289 let args = vec!("blah".to_string());
1290 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1291 let rs = getopts(args.as_slice(), opts.as_slice());
1294 assert!(!m.opt_present("test"));
1295 assert!(!m.opt_present("t"));
1302 fn test_optmulti_no_arg() {
1303 let long_args = vec!("--test".to_string());
1304 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1305 let rs = getopts(long_args.as_slice(), opts.as_slice());
1307 Err(ArgumentMissing(_)) => {},
1310 let short_args = vec!("-t".to_string());
1311 match getopts(short_args.as_slice(), opts.as_slice()) {
1312 Err(ArgumentMissing(_)) => {},
1318 fn test_optmulti_multi() {
1319 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1320 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1321 let rs = getopts(args.as_slice(), opts.as_slice());
1324 assert!(m.opt_present("test"));
1325 assert_eq!(m.opt_str("test").unwrap(), "20");
1326 assert!(m.opt_present("t"));
1327 assert_eq!(m.opt_str("t").unwrap(), "20");
1328 let pair = m.opt_strs("test");
1329 assert!(pair[0] == "20");
1330 assert!(pair[1] == "30");
1337 fn test_unrecognized_option() {
1338 let long_args = vec!("--untest".to_string());
1339 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1340 let rs = getopts(long_args.as_slice(), opts.as_slice());
1342 Err(UnrecognizedOption(_)) => {},
1345 let short_args = vec!("-u".to_string());
1346 match getopts(short_args.as_slice(), opts.as_slice()) {
1347 Err(UnrecognizedOption(_)) => {},
1353 fn test_combined() {
1355 vec!("prog".to_string(),
1356 "free1".to_string(),
1359 "free2".to_string(),
1360 "--flag".to_string(),
1361 "--long=30".to_string(),
1370 "-60 70".to_string());
1372 vec!(optopt("s", "something", "something", "SOMETHING"),
1373 optflag("", "flag", "a flag"),
1374 reqopt("", "long", "hi", "LONG"),
1375 optflag("f", "", "another flag"),
1376 optmulti("m", "", "mmmmmm", "YUM"),
1377 optmulti("n", "", "nothing", "NOTHING"),
1378 optopt("", "notpresent", "nothing to see here", "NOPE"));
1379 let rs = getopts(args.as_slice(), opts.as_slice());
1382 assert!(m.free[0] == "prog");
1383 assert!(m.free[1] == "free1");
1384 assert_eq!(m.opt_str("s").unwrap(), "20");
1385 assert!(m.free[2] == "free2");
1386 assert!((m.opt_present("flag")));
1387 assert_eq!(m.opt_str("long").unwrap(), "30");
1388 assert!((m.opt_present("f")));
1389 let pair = m.opt_strs("m");
1390 assert!(pair[0] == "40");
1391 assert!(pair[1] == "50");
1392 let pair = m.opt_strs("n");
1393 assert!(pair[0] == "-A B");
1394 assert!(pair[1] == "-60 70");
1395 assert!((!m.opt_present("notpresent")));
1403 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1404 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1405 optopt("f", "", "flag", "FLAG"));
1407 let args_single = vec!("-e".to_string(), "foo".to_string());
1408 let matches_single = &match getopts(args_single.as_slice(),
1410 result::Result::Ok(m) => m,
1411 result::Result::Err(_) => panic!()
1413 assert!(matches_single.opts_present(&["e".to_string()]));
1414 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1415 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1416 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1417 assert!(!matches_single.opts_present(&["thing".to_string()]));
1418 assert!(!matches_single.opts_present(&[]));
1420 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1421 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1423 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1426 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1428 let matches_both = &match getopts(args_both.as_slice(),
1430 result::Result::Ok(m) => m,
1431 result::Result::Err(_) => panic!()
1433 assert!(matches_both.opts_present(&["e".to_string()]));
1434 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1435 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1436 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1437 assert!(!matches_both.opts_present(&["f".to_string()]));
1438 assert!(!matches_both.opts_present(&["thing".to_string()]));
1439 assert!(!matches_both.opts_present(&[]));
1441 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1442 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1443 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1445 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1451 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1452 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1453 optmulti("M", "", "something", "MMMM"));
1454 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1455 result::Result::Ok(m) => m,
1456 result::Result::Err(_) => panic!()
1458 assert!(matches.opts_present(&["L".to_string()]));
1459 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1460 assert!(matches.opts_present(&["M".to_string()]));
1461 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1466 fn test_nospace_conflict() {
1467 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1468 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1469 optflagmulti("v", "verbose", "Verbose"));
1470 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1471 result::Result::Ok(m) => m,
1472 result::Result::Err(e) => panic!( "{}", e )
1474 assert!(matches.opts_present(&["L".to_string()]));
1475 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1476 assert!(matches.opts_present(&["v".to_string()]));
1477 assert_eq!(3, matches.opt_count("v"));
1481 fn test_long_to_short() {
1482 let mut short = Opt {
1483 name: Name::Long("banana".to_string()),
1484 hasarg: HasArg::Yes,
1486 aliases: Vec::new(),
1488 short.aliases = vec!(Opt { name: Name::Short('b'),
1489 hasarg: HasArg::Yes,
1491 aliases: Vec::new() });
1492 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1494 assert!(verbose.long_to_short() == short);
1498 fn test_aliases_long_and_short() {
1500 optflagmulti("a", "apple", "Desc"));
1502 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1504 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1505 assert_eq!(3, matches.opt_count("a"));
1506 assert_eq!(3, matches.opt_count("apple"));
1511 let optgroups = vec!(
1512 reqopt("b", "banana", "Desc", "VAL"),
1513 optopt("a", "012345678901234567890123456789",
1515 optflag("k", "kiwi", "Desc"),
1516 optflagopt("p", "", "Desc", "VAL"),
1517 optmulti("l", "", "Desc", "VAL"));
1523 -b --banana VAL Desc
1524 -a --012345678901234567890123456789 VAL
1531 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1533 debug!("expected: <<{}>>", expected);
1534 debug!("generated: <<{}>>", generated_usage);
1535 assert_eq!(generated_usage, expected);
1539 fn test_usage_description_wrapping() {
1540 // indentation should be 24 spaces
1541 // lines wrap after 78: or rather descriptions wrap after 54
1543 let optgroups = vec!(
1544 optflag("k", "kiwi",
1545 "This is a long description which won't be wrapped..+.."), // 54
1546 optflag("a", "apple",
1547 "This is a long description which _will_ be wrapped..+.."));
1553 -k --kiwi This is a long description which won't be wrapped..+..
1554 -a --apple This is a long description which _will_ be
1558 let usage = usage("Usage: fruits", optgroups.as_slice());
1560 debug!("expected: <<{}>>", expected);
1561 debug!("generated: <<{}>>", usage);
1562 assert!(usage == expected)
1566 fn test_usage_description_multibyte_handling() {
1567 let optgroups = vec!(
1568 optflag("k", "k\u{2013}w\u{2013}",
1569 "The word kiwi is normally spelled with two i's"),
1570 optflag("a", "apple",
1571 "This \u{201C}description\u{201D} has some characters that could \
1572 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1578 -k --k–w– The word kiwi is normally spelled with two i's
1579 -a --apple This “description” has some characters that could
1580 confuse the line wrapping; an apple costs 0.51€ in
1581 some parts of Europe.
1584 let usage = usage("Usage: fruits", optgroups.as_slice());
1586 debug!("expected: <<{}>>", expected);
1587 debug!("generated: <<{}>>", usage);
1588 assert!(usage == expected)
1592 fn test_short_usage() {
1593 let optgroups = vec!(
1594 reqopt("b", "banana", "Desc", "VAL"),
1595 optopt("a", "012345678901234567890123456789",
1597 optflag("k", "kiwi", "Desc"),
1598 optflagopt("p", "", "Desc", "VAL"),
1599 optmulti("l", "", "Desc", "VAL"));
1601 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1602 let generated_usage = short_usage("fruits", optgroups.as_slice());
1604 debug!("expected: <<{}>>", expected);
1605 debug!("generated: <<{}>>", generated_usage);
1606 assert_eq!(generated_usage, expected);