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(feature = "rustc_private",
82 reason = "use the crates.io `getopts` library instead")]
84 #![crate_type = "rlib"]
85 #![crate_type = "dylib"]
86 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
87 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
88 html_root_url = "http://doc.rust-lang.org/nightly/",
89 html_playground_url = "http://play.rust-lang.org/")]
91 #![deny(missing_docs)]
92 #![feature(collections)]
95 #![feature(slicing_syntax)]
96 #![feature(staged_api)]
97 #![cfg_attr(test, feature(rustc_private))]
99 #[cfg(test)] #[macro_use] extern crate log;
106 use self::SplitWithinState::*;
107 use self::Whitespace::*;
108 use self::LengthLimit::*;
111 use std::iter::repeat;
114 /// Name of an option. Either a string or a single char.
115 #[derive(Clone, PartialEq, Eq, Debug)]
117 /// A string representing the long name of an option.
118 /// For example: "help"
120 /// A char representing the short name of an option.
125 /// Describes whether an option has an argument.
126 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
128 /// The option requires an argument.
130 /// The option takes no argument.
132 /// The option argument is optional.
136 /// Describes how often an option may occur.
137 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
139 /// The option occurs once.
141 /// The option occurs at most once.
143 /// The option occurs zero or more times.
147 /// A description of a possible option.
148 #[derive(Clone, PartialEq, Eq, Debug)]
150 /// Name of the option
152 /// Whether it has an argument
154 /// How often it can occur
156 /// Which options it aliases
157 pub aliases: Vec<Opt>,
160 /// One group of options, e.g., both `-h` and `--help`, along with
161 /// their shared description and properties.
162 #[derive(Clone, PartialEq, Eq, Debug)]
163 pub struct OptGroup {
164 /// Short name of the option, e.g. `h` for a `-h` option
165 pub short_name: String,
166 /// Long name of the option, e.g. `help` for a `--help` option
167 pub long_name: String,
168 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
170 /// Description for usage help text
172 /// Whether option has an argument
174 /// How often it can occur
178 /// Describes whether an option is given at all or has a value.
179 #[derive(Clone, PartialEq, Eq, Debug)]
185 /// The result of checking command line arguments. Contains a vector
186 /// of matches and a vector of free strings.
187 #[derive(Clone, PartialEq, Eq, Debug)]
189 /// Options that matched
191 /// Values of the Options that matched
192 vals: Vec<Vec<Optval>>,
193 /// Free string fragments
194 pub free: Vec<String>,
197 /// The type returned when the command line does not conform to the
198 /// expected format. Use the `Show` implementation to output detailed
200 #[derive(Clone, PartialEq, Eq, Debug)]
202 /// The option requires an argument but none was passed.
203 ArgumentMissing(String),
204 /// The passed option is not declared among the possible options.
205 UnrecognizedOption(String),
206 /// A required option is not present.
207 OptionMissing(String),
208 /// A single occurrence option is being used multiple times.
209 OptionDuplicated(String),
210 /// There's an argument being passed to a non-argument option.
211 UnexpectedArgument(String),
214 /// The type of failure that occurred.
215 #[derive(Copy, PartialEq, Eq, Debug)]
216 #[allow(missing_docs)]
225 /// The result of parsing a command line with a set of options.
226 pub type Result = result::Result<Matches, Fail>;
229 fn from_str(nm: &str) -> Name {
237 fn to_string(&self) -> String {
239 Short(ch) => ch.to_string(),
240 Long(ref s) => s.to_string()
246 /// Translate OptGroup into Opt.
247 /// (Both short and long names correspond to different Opts).
248 pub fn long_to_short(&self) -> Opt {
257 match (short_name.len(), long_name.len()) {
258 (0,0) => panic!("this long-format option was given no name"),
260 name: Long((long_name)),
266 name: Short(short_name.char_at(0)),
272 name: Long((long_name)),
277 name: Short(short_name.char_at(0)),
284 (_,_) => panic!("something is wrong with the long-form opt")
290 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
291 match find_opt(&self.opts[], Name::from_str(nm)) {
292 Some(id) => self.vals[id].clone(),
293 None => panic!("No option '{}' defined", nm)
297 fn opt_val(&self, nm: &str) -> Option<Optval> {
298 let vals = self.opt_vals(nm);
302 Some(vals[0].clone())
306 /// Returns true if an option was matched.
307 pub fn opt_present(&self, nm: &str) -> bool {
308 !self.opt_vals(nm).is_empty()
311 /// Returns the number of times an option was matched.
312 pub fn opt_count(&self, nm: &str) -> uint {
313 self.opt_vals(nm).len()
316 /// Returns true if any of several options were matched.
317 pub fn opts_present(&self, names: &[String]) -> bool {
319 match find_opt(self.opts.as_slice(), Name::from_str(&nm[])) {
320 Some(id) if !self.vals[id].is_empty() => return true,
327 /// Returns the string argument supplied to one of several matching options or `None`.
328 pub fn opts_str(&self, names: &[String]) -> Option<String> {
330 match self.opt_val(&nm[]) {
331 Some(Val(ref s)) => return Some(s.clone()),
338 /// Returns a vector of the arguments provided to all matches of the given
341 /// Used when an option accepts multiple values.
342 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
343 let mut acc: Vec<String> = Vec::new();
344 let r = self.opt_vals(nm);
347 Val(ref s) => acc.push((*s).clone()),
354 /// Returns the string argument supplied to a matching option or `None`.
355 pub fn opt_str(&self, nm: &str) -> Option<String> {
356 let vals = self.opt_vals(nm);
358 return None::<String>;
361 Val(ref s) => Some((*s).clone()),
367 /// Returns the matching string, a default, or none.
369 /// Returns none if the option was not present, `def` if the option was
370 /// present but no argument was provided, and the argument if the option was
371 /// present and an argument was provided.
372 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
373 let vals = self.opt_vals(nm);
378 Val(ref s) => Some((*s).clone()),
379 _ => Some(def.to_string())
386 fn is_arg(arg: &str) -> bool {
387 arg.len() > 1 && arg.as_bytes()[0] == b'-'
390 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
391 // Search main options.
392 let pos = opts.iter().position(|opt| opt.name == nm);
397 // Search in aliases.
398 for candidate in opts {
399 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
400 return opts.iter().position(|opt| opt.name == candidate.name);
407 /// Create a long option that is required and takes an argument.
409 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
410 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
411 /// * `desc` - Description for usage help
412 /// * `hint` - Hint that is used in place of the argument in the usage help,
413 /// e.g. `"FILE"` for a `-o FILE` option
414 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
415 let len = short_name.len();
416 assert!(len == 1 || len == 0);
418 short_name: short_name.to_string(),
419 long_name: long_name.to_string(),
420 hint: hint.to_string(),
421 desc: desc.to_string(),
427 /// Create a long option that is optional and takes an argument.
429 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
430 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
431 /// * `desc` - Description for usage help
432 /// * `hint` - Hint that is used in place of the argument in the usage help,
433 /// e.g. `"FILE"` for a `-o FILE` option
434 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
435 let len = short_name.len();
436 assert!(len == 1 || len == 0);
438 short_name: short_name.to_string(),
439 long_name: long_name.to_string(),
440 hint: hint.to_string(),
441 desc: desc.to_string(),
447 /// Create a long option that is optional and does not take an argument.
449 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
450 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
451 /// * `desc` - Description for usage help
452 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
453 let len = short_name.len();
454 assert!(len == 1 || len == 0);
456 short_name: short_name.to_string(),
457 long_name: long_name.to_string(),
458 hint: "".to_string(),
459 desc: desc.to_string(),
465 /// Create a long option that can occur more than once and does not
466 /// take an argument.
468 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
469 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
470 /// * `desc` - Description for usage help
471 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
472 let len = short_name.len();
473 assert!(len == 1 || len == 0);
475 short_name: short_name.to_string(),
476 long_name: long_name.to_string(),
477 hint: "".to_string(),
478 desc: desc.to_string(),
484 /// Create a long option that is optional and takes an optional argument.
486 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
487 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
488 /// * `desc` - Description for usage help
489 /// * `hint` - Hint that is used in place of the argument in the usage help,
490 /// e.g. `"FILE"` for a `-o FILE` option
491 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
492 let len = short_name.len();
493 assert!(len == 1 || len == 0);
495 short_name: short_name.to_string(),
496 long_name: long_name.to_string(),
497 hint: hint.to_string(),
498 desc: desc.to_string(),
504 /// Create a long option that is optional, takes an argument, and may occur
507 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
508 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
509 /// * `desc` - Description for usage help
510 /// * `hint` - Hint that is used in place of the argument in the usage help,
511 /// e.g. `"FILE"` for a `-o FILE` option
512 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
513 let len = short_name.len();
514 assert!(len == 1 || len == 0);
516 short_name: short_name.to_string(),
517 long_name: long_name.to_string(),
518 hint: hint.to_string(),
519 desc: desc.to_string(),
525 /// Create a generic option group, stating all parameters explicitly
526 pub fn opt(short_name: &str,
531 occur: Occur) -> OptGroup {
532 let len = short_name.len();
533 assert!(len == 1 || len == 0);
535 short_name: short_name.to_string(),
536 long_name: long_name.to_string(),
537 hint: hint.to_string(),
538 desc: desc.to_string(),
545 /// Convert a `Fail` enum into an error string.
546 #[unstable(feature = "rustc_private")]
547 #[deprecated(since = "1.0.0",
548 reason = "use `fmt::String` (`{}` format specifier)")]
549 pub fn to_err_msg(self) -> String {
554 impl fmt::Display for Fail {
555 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
557 ArgumentMissing(ref nm) => {
558 write!(f, "Argument to option '{}' missing.", *nm)
560 UnrecognizedOption(ref nm) => {
561 write!(f, "Unrecognized option: '{}'.", *nm)
563 OptionMissing(ref nm) => {
564 write!(f, "Required option '{}' missing.", *nm)
566 OptionDuplicated(ref nm) => {
567 write!(f, "Option '{}' given more than once.", *nm)
569 UnexpectedArgument(ref nm) => {
570 write!(f, "Option '{}' does not take an argument.", *nm)
576 /// Parse command line arguments according to the provided options.
578 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
579 /// `opt_str`, etc. to interrogate results.
582 /// Returns `Err(Fail)` on failure: use the `Show` implementation of `Fail` to display
583 /// information about it.
584 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
585 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
586 let n_opts = opts.len();
588 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
590 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
591 let mut free: Vec<String> = Vec::new();
595 let cur = args[i].clone();
596 let curlen = cur.len();
599 } else if cur == "--" {
601 while j < l { free.push(args[j].clone()); j += 1; }
605 let mut i_arg = None;
606 if cur.as_bytes()[1] == b'-' {
607 let tail = &cur[2..curlen];
608 let tail_eq: Vec<&str> = tail.split('=').collect();
609 if tail_eq.len() <= 1 {
610 names = vec!(Long(tail.to_string()));
613 vec!(Long(tail_eq[0].to_string()));
614 i_arg = Some(tail_eq[1].to_string());
620 let range = cur.char_range_at(j);
621 let opt = Short(range.ch);
623 /* In a series of potential options (eg. -aheJ), if we
624 see one which takes an argument, we assume all
625 subsequent characters make up the argument. This
626 allows options such as -L/usr/local/lib/foo to be
627 interpreted correctly
630 let opt_id = match find_opt(opts.as_slice(), opt.clone()) {
632 None => return Err(UnrecognizedOption(opt.to_string()))
637 let arg_follows = match opts[opt_id].hasarg {
642 if arg_follows && range.next < curlen {
643 i_arg = Some((&cur[range.next..curlen]).to_string());
650 let mut name_pos = 0;
653 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
655 None => return Err(UnrecognizedOption(nm.to_string()))
657 match opts[optid].hasarg {
659 if name_pos == names.len() && !i_arg.is_none() {
660 return Err(UnexpectedArgument(nm.to_string()));
662 let v = &mut vals[optid];
666 if !i_arg.is_none() {
667 let v = &mut vals[optid];
668 v.push(Val((i_arg.clone())
670 } else if name_pos < names.len() || i + 1 == l ||
671 is_arg(&args[i + 1][]) {
672 let v = &mut vals[optid];
676 let v = &mut vals[optid];
677 v.push(Val(args[i].clone()));
681 if !i_arg.is_none() {
682 let v = &mut vals[optid];
683 v.push(Val(i_arg.clone().unwrap()));
684 } else if i + 1 == l {
685 return Err(ArgumentMissing(nm.to_string()));
688 let v = &mut vals[optid];
689 v.push(Val(args[i].clone()));
698 let n = vals[i].len();
699 let occ = opts[i].occur;
700 if occ == Req && n == 0 {
701 return Err(OptionMissing(opts[i].name.to_string()));
703 if occ != Multi && n > 1 {
704 return Err(OptionDuplicated(opts[i].name.to_string()));
714 /// Derive a usage message from a set of long options.
715 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
717 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
719 let rows = opts.iter().map(|optref| {
720 let OptGroup{short_name,
725 ..} = (*optref).clone();
727 let mut row = repeat(" ").take(4).collect::<String>();
730 match short_name.len() {
734 row.push_str(&short_name[]);
737 _ => panic!("the short name should only be 1 ascii char long"),
741 match long_name.len() {
745 row.push_str(&long_name[]);
753 Yes => row.push_str(&hint[]),
756 row.push_str(&hint[]);
761 // FIXME: #5516 should be graphemes not codepoints
762 // here we just need to indent the start of the description
763 let rowlen = row.chars().count();
765 for _ in 0..24 - rowlen {
769 row.push_str(&desc_sep[]);
772 // Normalize desc to contain words separated by one space character
773 let mut desc_normalized_whitespace = String::new();
774 for word in desc.words() {
775 desc_normalized_whitespace.push_str(word);
776 desc_normalized_whitespace.push(' ');
779 // FIXME: #5516 should be graphemes not codepoints
780 let mut desc_rows = Vec::new();
781 each_split_within(&desc_normalized_whitespace[], 54, |substr| {
782 desc_rows.push(substr.to_string());
786 // FIXME: #5516 should be graphemes not codepoints
787 // wrapped description
788 row.push_str(&desc_rows.connect(&desc_sep[])[]);
793 format!("{}\n\nOptions:\n{}\n", brief,
794 rows.collect::<Vec<String>>().connect("\n"))
797 fn format_option(opt: &OptGroup) -> String {
798 let mut line = String::new();
800 if opt.occur != Req {
804 // Use short_name is possible, but fallback to long_name.
805 if opt.short_name.len() > 0 {
807 line.push_str(&opt.short_name[]);
810 line.push_str(&opt.long_name[]);
813 if opt.hasarg != No {
815 if opt.hasarg == Maybe {
818 line.push_str(&opt.hint[]);
819 if opt.hasarg == Maybe {
824 if opt.occur != Req {
827 if opt.occur == Multi {
834 /// Derive a short one-line usage summary from a set of long options.
835 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
836 let mut line = format!("Usage: {} ", program_name);
837 line.push_str(&opts.iter()
839 .collect::<Vec<String>>()
845 enum SplitWithinState {
846 A, // leading whitespace, initial state
848 C, // internal and trailing whitespace
852 Ws, // current char is whitespace
853 Cr // current char is not whitespace
857 UnderLim, // current char makes current substring still fit in limit
858 OverLim // current char makes current substring no longer fit in limit
862 /// Splits a string into substrings with possibly internal whitespace,
863 /// each of them at most `lim` bytes long. The substrings have leading and trailing
864 /// whitespace removed, and are only cut at whitespace boundaries.
866 /// Note: Function was moved here from `std::str` because this module is the only place that
867 /// uses it, and because it was too specific for a general string function.
871 /// Panics during iteration if the string contains a non-whitespace
872 /// sequence longer than the limit.
873 fn each_split_within<F>(ss: &str, lim: uint, mut it: F) -> bool where
874 F: FnMut(&str) -> bool
876 // Just for fun, let's write this as a state machine:
878 let mut slice_start = 0;
879 let mut last_start = 0;
880 let mut last_end = 0;
882 let mut fake_i = ss.len();
887 // if the limit is larger than the string, lower it to save cycles
892 let mut machine = |&mut: cont: &mut bool, (i, c): (uint, char)| -> bool {
893 let whitespace = if c.is_whitespace() { Ws } else { Cr };
894 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
896 state = match (state, whitespace, limit) {
898 (A, Cr, _) => { slice_start = i; last_start = i; B }
900 (B, Cr, UnderLim) => { B }
901 (B, Cr, OverLim) if (i - last_start + 1) > lim
902 => panic!("word starting with {} longer than limit!",
903 &ss[last_start..i + 1]),
904 (B, Cr, OverLim) => {
905 *cont = it(&ss[slice_start..last_end]);
906 slice_start = last_start;
909 (B, Ws, UnderLim) => {
913 (B, Ws, OverLim) => {
915 *cont = it(&ss[slice_start..last_end]);
919 (C, Cr, UnderLim) => {
923 (C, Cr, OverLim) => {
924 *cont = it(&ss[slice_start..last_end]);
930 (C, Ws, OverLim) => {
931 *cont = it(&ss[slice_start..last_end]);
934 (C, Ws, UnderLim) => {
942 ss.char_indices().all(|x| machine(&mut cont, x));
944 // Let the automaton 'run out' by supplying trailing whitespace
945 while cont && match state { B | C => true, A => false } {
946 machine(&mut cont, (fake_i, ' '));
953 fn test_split_within() {
954 fn t(s: &str, i: uint, u: &[String]) {
955 let mut v = Vec::new();
956 each_split_within(s, i, |s| { v.push(s.to_string()); true });
957 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
961 t("hello", 15, &["hello".to_string()]);
962 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
963 "Mary had a".to_string(),
964 "little lamb".to_string(),
965 "Little lamb".to_string()
967 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
968 &["Mary had a little lamb\nLittle lamb".to_string()]);
976 use std::result::Result::{Err, Ok};
982 let long_args = vec!("--test=20".to_string());
983 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
984 let rs = getopts(long_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 (long arg)"); }
994 let short_args = vec!("-t".to_string(), "20".to_string());
995 match getopts(short_args.as_slice(), opts.as_slice()) {
997 assert!((m.opt_present("test")));
998 assert_eq!(m.opt_str("test").unwrap(), "20");
999 assert!((m.opt_present("t")));
1000 assert_eq!(m.opt_str("t").unwrap(), "20");
1002 _ => { panic!("test_reqopt failed (short arg)"); }
1007 fn test_reqopt_missing() {
1008 let args = vec!("blah".to_string());
1009 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1010 let rs = getopts(args.as_slice(), opts.as_slice());
1012 Err(OptionMissing(_)) => {},
1018 fn test_reqopt_no_arg() {
1019 let long_args = vec!("--test".to_string());
1020 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1021 let rs = getopts(long_args.as_slice(), opts.as_slice());
1023 Err(ArgumentMissing(_)) => {},
1026 let short_args = vec!("-t".to_string());
1027 match getopts(short_args.as_slice(), opts.as_slice()) {
1028 Err(ArgumentMissing(_)) => {},
1034 fn test_reqopt_multi() {
1035 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1036 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
1037 let rs = getopts(args.as_slice(), opts.as_slice());
1039 Err(OptionDuplicated(_)) => {},
1047 let long_args = vec!("--test=20".to_string());
1048 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1049 let rs = getopts(long_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");
1059 let short_args = vec!("-t".to_string(), "20".to_string());
1060 match getopts(short_args.as_slice(), opts.as_slice()) {
1062 assert!((m.opt_present("test")));
1063 assert_eq!(m.opt_str("test").unwrap(), "20");
1064 assert!((m.opt_present("t")));
1065 assert_eq!(m.opt_str("t").unwrap(), "20");
1072 fn test_optopt_missing() {
1073 let args = vec!("blah".to_string());
1074 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1075 let rs = getopts(args.as_slice(), opts.as_slice());
1078 assert!(!m.opt_present("test"));
1079 assert!(!m.opt_present("t"));
1086 fn test_optopt_no_arg() {
1087 let long_args = vec!("--test".to_string());
1088 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1089 let rs = getopts(long_args.as_slice(), opts.as_slice());
1091 Err(ArgumentMissing(_)) => {},
1094 let short_args = vec!("-t".to_string());
1095 match getopts(short_args.as_slice(), opts.as_slice()) {
1096 Err(ArgumentMissing(_)) => {},
1102 fn test_optopt_multi() {
1103 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1104 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1105 let rs = getopts(args.as_slice(), opts.as_slice());
1107 Err(OptionDuplicated(_)) => {},
1112 // Tests for optflag
1115 let long_args = vec!("--test".to_string());
1116 let opts = vec!(optflag("t", "test", "testing"));
1117 let rs = getopts(long_args.as_slice(), opts.as_slice());
1120 assert!(m.opt_present("test"));
1121 assert!(m.opt_present("t"));
1125 let short_args = vec!("-t".to_string());
1126 match getopts(short_args.as_slice(), opts.as_slice()) {
1128 assert!(m.opt_present("test"));
1129 assert!(m.opt_present("t"));
1136 fn test_optflag_missing() {
1137 let args = vec!("blah".to_string());
1138 let opts = vec!(optflag("t", "test", "testing"));
1139 let rs = getopts(args.as_slice(), opts.as_slice());
1142 assert!(!m.opt_present("test"));
1143 assert!(!m.opt_present("t"));
1150 fn test_optflag_long_arg() {
1151 let args = vec!("--test=20".to_string());
1152 let opts = vec!(optflag("t", "test", "testing"));
1153 let rs = getopts(args.as_slice(), opts.as_slice());
1155 Err(UnexpectedArgument(_)) => {},
1161 fn test_optflag_multi() {
1162 let args = vec!("--test".to_string(), "-t".to_string());
1163 let opts = vec!(optflag("t", "test", "testing"));
1164 let rs = getopts(args.as_slice(), opts.as_slice());
1166 Err(OptionDuplicated(_)) => {},
1172 fn test_optflag_short_arg() {
1173 let args = vec!("-t".to_string(), "20".to_string());
1174 let opts = vec!(optflag("t", "test", "testing"));
1175 let rs = getopts(args.as_slice(), opts.as_slice());
1178 // The next variable after the flag is just a free argument
1180 assert!(m.free[0] == "20");
1186 // Tests for optflagmulti
1188 fn test_optflagmulti_short1() {
1189 let args = vec!("-v".to_string());
1190 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1191 let rs = getopts(args.as_slice(), opts.as_slice());
1194 assert_eq!(m.opt_count("v"), 1);
1201 fn test_optflagmulti_short2a() {
1202 let args = vec!("-v".to_string(), "-v".to_string());
1203 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1204 let rs = getopts(args.as_slice(), opts.as_slice());
1207 assert_eq!(m.opt_count("v"), 2);
1214 fn test_optflagmulti_short2b() {
1215 let args = vec!("-vv".to_string());
1216 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1217 let rs = getopts(args.as_slice(), opts.as_slice());
1220 assert_eq!(m.opt_count("v"), 2);
1227 fn test_optflagmulti_long1() {
1228 let args = vec!("--verbose".to_string());
1229 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1230 let rs = getopts(args.as_slice(), opts.as_slice());
1233 assert_eq!(m.opt_count("verbose"), 1);
1240 fn test_optflagmulti_long2() {
1241 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1242 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1243 let rs = getopts(args.as_slice(), opts.as_slice());
1246 assert_eq!(m.opt_count("verbose"), 2);
1253 fn test_optflagmulti_mix() {
1254 let args = vec!("--verbose".to_string(), "-v".to_string(),
1255 "-vv".to_string(), "verbose".to_string());
1256 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1257 let rs = getopts(args.as_slice(), opts.as_slice());
1260 assert_eq!(m.opt_count("verbose"), 4);
1261 assert_eq!(m.opt_count("v"), 4);
1267 // Tests for optmulti
1269 fn test_optmulti() {
1270 let long_args = vec!("--test=20".to_string());
1271 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1272 let rs = getopts(long_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");
1282 let short_args = vec!("-t".to_string(), "20".to_string());
1283 match getopts(short_args.as_slice(), opts.as_slice()) {
1285 assert!((m.opt_present("test")));
1286 assert_eq!(m.opt_str("test").unwrap(), "20");
1287 assert!((m.opt_present("t")));
1288 assert_eq!(m.opt_str("t").unwrap(), "20");
1295 fn test_optmulti_missing() {
1296 let args = vec!("blah".to_string());
1297 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1298 let rs = getopts(args.as_slice(), opts.as_slice());
1301 assert!(!m.opt_present("test"));
1302 assert!(!m.opt_present("t"));
1309 fn test_optmulti_no_arg() {
1310 let long_args = vec!("--test".to_string());
1311 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1312 let rs = getopts(long_args.as_slice(), opts.as_slice());
1314 Err(ArgumentMissing(_)) => {},
1317 let short_args = vec!("-t".to_string());
1318 match getopts(short_args.as_slice(), opts.as_slice()) {
1319 Err(ArgumentMissing(_)) => {},
1325 fn test_optmulti_multi() {
1326 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1327 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1328 let rs = getopts(args.as_slice(), opts.as_slice());
1331 assert!(m.opt_present("test"));
1332 assert_eq!(m.opt_str("test").unwrap(), "20");
1333 assert!(m.opt_present("t"));
1334 assert_eq!(m.opt_str("t").unwrap(), "20");
1335 let pair = m.opt_strs("test");
1336 assert!(pair[0] == "20");
1337 assert!(pair[1] == "30");
1344 fn test_unrecognized_option() {
1345 let long_args = vec!("--untest".to_string());
1346 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1347 let rs = getopts(long_args.as_slice(), opts.as_slice());
1349 Err(UnrecognizedOption(_)) => {},
1352 let short_args = vec!("-u".to_string());
1353 match getopts(short_args.as_slice(), opts.as_slice()) {
1354 Err(UnrecognizedOption(_)) => {},
1360 fn test_combined() {
1362 vec!("prog".to_string(),
1363 "free1".to_string(),
1366 "free2".to_string(),
1367 "--flag".to_string(),
1368 "--long=30".to_string(),
1377 "-60 70".to_string());
1379 vec!(optopt("s", "something", "something", "SOMETHING"),
1380 optflag("", "flag", "a flag"),
1381 reqopt("", "long", "hi", "LONG"),
1382 optflag("f", "", "another flag"),
1383 optmulti("m", "", "mmmmmm", "YUM"),
1384 optmulti("n", "", "nothing", "NOTHING"),
1385 optopt("", "notpresent", "nothing to see here", "NOPE"));
1386 let rs = getopts(args.as_slice(), opts.as_slice());
1389 assert!(m.free[0] == "prog");
1390 assert!(m.free[1] == "free1");
1391 assert_eq!(m.opt_str("s").unwrap(), "20");
1392 assert!(m.free[2] == "free2");
1393 assert!((m.opt_present("flag")));
1394 assert_eq!(m.opt_str("long").unwrap(), "30");
1395 assert!((m.opt_present("f")));
1396 let pair = m.opt_strs("m");
1397 assert!(pair[0] == "40");
1398 assert!(pair[1] == "50");
1399 let pair = m.opt_strs("n");
1400 assert!(pair[0] == "-A B");
1401 assert!(pair[1] == "-60 70");
1402 assert!((!m.opt_present("notpresent")));
1410 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1411 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1412 optopt("f", "", "flag", "FLAG"));
1414 let args_single = vec!("-e".to_string(), "foo".to_string());
1415 let matches_single = &match getopts(args_single.as_slice(),
1417 result::Result::Ok(m) => m,
1418 result::Result::Err(_) => panic!()
1420 assert!(matches_single.opts_present(&["e".to_string()]));
1421 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1422 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1423 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1424 assert!(!matches_single.opts_present(&["thing".to_string()]));
1425 assert!(!matches_single.opts_present(&[]));
1427 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1428 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1430 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1433 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1435 let matches_both = &match getopts(args_both.as_slice(),
1437 result::Result::Ok(m) => m,
1438 result::Result::Err(_) => panic!()
1440 assert!(matches_both.opts_present(&["e".to_string()]));
1441 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1442 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1443 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1444 assert!(!matches_both.opts_present(&["f".to_string()]));
1445 assert!(!matches_both.opts_present(&["thing".to_string()]));
1446 assert!(!matches_both.opts_present(&[]));
1448 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1449 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1450 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1452 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1458 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1459 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1460 optmulti("M", "", "something", "MMMM"));
1461 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1462 result::Result::Ok(m) => m,
1463 result::Result::Err(_) => panic!()
1465 assert!(matches.opts_present(&["L".to_string()]));
1466 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1467 assert!(matches.opts_present(&["M".to_string()]));
1468 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1473 fn test_nospace_conflict() {
1474 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1475 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1476 optflagmulti("v", "verbose", "Verbose"));
1477 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1478 result::Result::Ok(m) => m,
1479 result::Result::Err(e) => panic!( "{}", e )
1481 assert!(matches.opts_present(&["L".to_string()]));
1482 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1483 assert!(matches.opts_present(&["v".to_string()]));
1484 assert_eq!(3, matches.opt_count("v"));
1488 fn test_long_to_short() {
1489 let mut short = Opt {
1490 name: Name::Long("banana".to_string()),
1491 hasarg: HasArg::Yes,
1493 aliases: Vec::new(),
1495 short.aliases = vec!(Opt { name: Name::Short('b'),
1496 hasarg: HasArg::Yes,
1498 aliases: Vec::new() });
1499 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1501 assert!(verbose.long_to_short() == short);
1505 fn test_aliases_long_and_short() {
1507 optflagmulti("a", "apple", "Desc"));
1509 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1511 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1512 assert_eq!(3, matches.opt_count("a"));
1513 assert_eq!(3, matches.opt_count("apple"));
1518 let optgroups = vec!(
1519 reqopt("b", "banana", "Desc", "VAL"),
1520 optopt("a", "012345678901234567890123456789",
1522 optflag("k", "kiwi", "Desc"),
1523 optflagopt("p", "", "Desc", "VAL"),
1524 optmulti("l", "", "Desc", "VAL"));
1530 -b --banana VAL Desc
1531 -a --012345678901234567890123456789 VAL
1538 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1540 debug!("expected: <<{}>>", expected);
1541 debug!("generated: <<{}>>", generated_usage);
1542 assert_eq!(generated_usage, expected);
1546 fn test_usage_description_wrapping() {
1547 // indentation should be 24 spaces
1548 // lines wrap after 78: or rather descriptions wrap after 54
1550 let optgroups = vec!(
1551 optflag("k", "kiwi",
1552 "This is a long description which won't be wrapped..+.."), // 54
1553 optflag("a", "apple",
1554 "This is a long description which _will_ be wrapped..+.."));
1560 -k --kiwi This is a long description which won't be wrapped..+..
1561 -a --apple This is a long description which _will_ be
1565 let usage = usage("Usage: fruits", optgroups.as_slice());
1567 debug!("expected: <<{}>>", expected);
1568 debug!("generated: <<{}>>", usage);
1569 assert!(usage == expected)
1573 fn test_usage_description_multibyte_handling() {
1574 let optgroups = vec!(
1575 optflag("k", "k\u{2013}w\u{2013}",
1576 "The word kiwi is normally spelled with two i's"),
1577 optflag("a", "apple",
1578 "This \u{201C}description\u{201D} has some characters that could \
1579 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1585 -k --k–w– The word kiwi is normally spelled with two i's
1586 -a --apple This “description” has some characters that could
1587 confuse the line wrapping; an apple costs 0.51€ in
1588 some parts of Europe.
1591 let usage = usage("Usage: fruits", optgroups.as_slice());
1593 debug!("expected: <<{}>>", expected);
1594 debug!("generated: <<{}>>", usage);
1595 assert!(usage == expected)
1599 fn test_short_usage() {
1600 let optgroups = vec!(
1601 reqopt("b", "banana", "Desc", "VAL"),
1602 optopt("a", "012345678901234567890123456789",
1604 optflag("k", "kiwi", "Desc"),
1605 optflagopt("p", "", "Desc", "VAL"),
1606 optmulti("l", "", "Desc", "VAL"));
1608 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1609 let generated_usage = short_usage("fruits", optgroups.as_slice());
1611 debug!("expected: <<{}>>", expected);
1612 debug!("generated: <<{}>>", generated_usage);
1613 assert_eq!(generated_usage, expected);