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 //! Simple getopt alternative.
13 //! Construct a vector of options, either by using `reqopt`, `optopt`, and `optflag`
14 //! or by building them from components yourself, and pass them to `getopts`,
15 //! along with a vector of actual arguments (not including `argv[0]`). You'll
16 //! either get a failure code back, or a match. You'll have to verify whether
17 //! the amount of 'free' arguments in the match is what you expect. Use `opt_*`
18 //! accessors to get argument values out of the matches object.
20 //! Single-character options are expected to appear on the command line with a
21 //! single preceding dash; multiple-character options are expected to be
22 //! proceeded by two dashes. Options that expect an argument accept their
23 //! argument following either a space or an equals sign. Single-character
24 //! options don't require the space.
28 //! The following example shows simple command line parsing for an application
29 //! that requires an input file to be specified, accepts an optional output
30 //! file name following `-o`, and accepts both `-h` and `--help` as optional flags.
33 //! extern crate getopts;
34 //! use getopts::{optopt,optflag,getopts,OptGroup,usage};
37 //! fn do_work(inp: &str, out: Option<String>) {
38 //! println!("{}", inp);
40 //! Some(x) => println!("{}", x),
41 //! None => println!("No Output"),
45 //! fn print_usage(program: &str, opts: &[OptGroup]) {
46 //! let brief = format!("Usage: {} [options]", program);
47 //! print!("{}", usage(brief, opts));
51 //! let args: Vec<String> = os::args();
53 //! let program = args[0].clone();
56 //! optopt("o", "", "set output file name", "NAME"),
57 //! optflag("h", "help", "print this help menu")
59 //! let matches = match getopts(args[1..], opts) {
61 //! Err(f) => { panic!(f.to_string()) }
63 //! if matches.opt_present("h") {
64 //! print_usage(program, opts);
67 //! let output = matches.opt_str("o");
68 //! let input = if !matches.free.is_empty() {
69 //! matches.free[0].clone()
71 //! print_usage(program, opts);
74 //! do_work(input, output);
79 // Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
80 #![cfg_attr(stage0, feature(custom_attribute))]
81 #![crate_name = "getopts"]
82 #![unstable(feature = "rustc_private",
83 reason = "use the crates.io `getopts` library instead",
86 #![crate_type = "rlib"]
87 #![crate_type = "dylib"]
88 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
89 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
90 html_root_url = "https://doc.rust-lang.org/nightly/",
91 html_playground_url = "https://play.rust-lang.org/")]
93 #![deny(missing_docs)]
94 #![feature(staged_api)]
96 #![cfg_attr(test, feature(rustc_private))]
98 #[cfg(test)] #[macro_use] extern crate log;
105 use self::SplitWithinState::*;
106 use self::Whitespace::*;
107 use self::LengthLimit::*;
110 use std::iter::repeat;
113 /// Name of an option. Either a string or a single char.
114 #[derive(Clone, PartialEq, Eq, Debug)]
116 /// A string representing the long name of an option.
117 /// For example: "help"
119 /// A char representing the short name of an option.
124 /// Describes whether an option has an argument.
125 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
127 /// The option requires an argument.
129 /// The option takes no argument.
131 /// The option argument is optional.
135 /// Describes how often an option may occur.
136 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
138 /// The option occurs once.
140 /// The option occurs at most once.
142 /// The option occurs zero or more times.
146 /// A description of a possible option.
147 #[derive(Clone, PartialEq, Eq, Debug)]
149 /// Name of the option
151 /// Whether it has an argument
153 /// How often it can occur
155 /// Which options it aliases
156 pub aliases: Vec<Opt>,
159 /// One group of options, e.g., both `-h` and `--help`, along with
160 /// their shared description and properties.
161 #[derive(Clone, PartialEq, Eq, Debug)]
162 pub struct OptGroup {
163 /// Short name of the option, e.g. `h` for a `-h` option
164 pub short_name: String,
165 /// Long name of the option, e.g. `help` for a `--help` option
166 pub long_name: String,
167 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
169 /// Description for usage help text
171 /// Whether option has an argument
173 /// How often it can occur
177 /// Describes whether an option is given at all or has a value.
178 #[derive(Clone, PartialEq, Eq, Debug)]
184 /// The result of checking command line arguments. Contains a vector
185 /// of matches and a vector of free strings.
186 #[derive(Clone, PartialEq, Eq, Debug)]
188 /// Options that matched
190 /// Values of the Options that matched
191 vals: Vec<Vec<Optval>>,
192 /// Free string fragments
193 pub free: Vec<String>,
196 /// The type returned when the command line does not conform to the
197 /// expected format. Use the `Debug` implementation to output detailed
199 #[derive(Clone, PartialEq, Eq, Debug)]
201 /// The option requires an argument but none was passed.
202 ArgumentMissing(String),
203 /// The passed option is not declared among the possible options.
204 UnrecognizedOption(String),
205 /// A required option is not present.
206 OptionMissing(String),
207 /// A single occurrence option is being used multiple times.
208 OptionDuplicated(String),
209 /// There's an argument being passed to a non-argument option.
210 UnexpectedArgument(String),
213 /// The type of failure that occurred.
214 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
215 #[allow(missing_docs)]
224 /// The result of parsing a command line with a set of options.
225 pub type Result = result::Result<Matches, Fail>;
228 fn from_str(nm: &str) -> Name {
236 fn to_string(&self) -> String {
238 Short(ch) => ch.to_string(),
239 Long(ref s) => s.to_owned()
245 /// Translate OptGroup into Opt.
246 /// (Both short and long names correspond to different Opts).
247 pub fn long_to_short(&self) -> Opt {
256 match (short_name.len(), long_name.len()) {
257 (0,0) => panic!("this long-format option was given no name"),
259 name: Long((long_name)),
265 name: Short(short_name.char_at(0)),
271 name: Long((long_name)),
276 name: Short(short_name.char_at(0)),
283 (_,_) => panic!("something is wrong with the long-form opt")
289 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
290 match find_opt(&self.opts[..], Name::from_str(nm)) {
291 Some(id) => self.vals[id].clone(),
292 None => panic!("No option '{}' defined", nm)
296 fn opt_val(&self, nm: &str) -> Option<Optval> {
297 let vals = self.opt_vals(nm);
301 Some(vals[0].clone())
305 /// Returns true if an option was matched.
306 pub fn opt_present(&self, nm: &str) -> bool {
307 !self.opt_vals(nm).is_empty()
310 /// Returns the number of times an option was matched.
311 pub fn opt_count(&self, nm: &str) -> usize {
312 self.opt_vals(nm).len()
315 /// Returns true if any of several options were matched.
316 pub fn opts_present(&self, names: &[String]) -> bool {
318 match find_opt(&self.opts, Name::from_str(&**nm)) {
319 Some(id) if !self.vals[id].is_empty() => return true,
326 /// Returns the string argument supplied to one of several matching options or `None`.
327 pub fn opts_str(&self, names: &[String]) -> Option<String> {
329 match self.opt_val(&nm[..]) {
330 Some(Val(ref s)) => return Some(s.clone()),
337 /// Returns a vector of the arguments provided to all matches of the given
340 /// Used when an option accepts multiple values.
341 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
342 let mut acc: Vec<String> = Vec::new();
343 let r = self.opt_vals(nm);
346 Val(ref s) => acc.push((*s).clone()),
353 /// Returns the string argument supplied to a matching option or `None`.
354 pub fn opt_str(&self, nm: &str) -> Option<String> {
355 let vals = self.opt_vals(nm);
357 return None::<String>;
360 Val(ref s) => Some((*s).clone()),
366 /// Returns the matching string, a default, or none.
368 /// Returns none if the option was not present, `def` if the option was
369 /// present but no argument was provided, and the argument if the option was
370 /// present and an argument was provided.
371 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
372 let vals = self.opt_vals(nm);
377 Val(ref s) => Some((*s).clone()),
378 _ => Some(def.to_owned())
385 fn is_arg(arg: &str) -> bool {
386 arg.len() > 1 && arg.as_bytes()[0] == b'-'
389 fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
390 // Search main options.
391 let pos = opts.iter().position(|opt| opt.name == nm);
396 // Search in aliases.
397 for candidate in opts {
398 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
399 return opts.iter().position(|opt| opt.name == candidate.name);
406 /// Create a long option that is required and takes an argument.
408 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
409 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
410 /// * `desc` - Description for usage help
411 /// * `hint` - Hint that is used in place of the argument in the usage help,
412 /// e.g. `"FILE"` for a `-o FILE` option
413 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
414 let len = short_name.len();
415 assert!(len == 1 || len == 0);
417 short_name: short_name.to_owned(),
418 long_name: long_name.to_owned(),
419 hint: hint.to_owned(),
420 desc: desc.to_owned(),
426 /// Create a long option that is optional and takes an argument.
428 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
429 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
430 /// * `desc` - Description for usage help
431 /// * `hint` - Hint that is used in place of the argument in the usage help,
432 /// e.g. `"FILE"` for a `-o FILE` option
433 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
434 let len = short_name.len();
435 assert!(len == 1 || len == 0);
437 short_name: short_name.to_owned(),
438 long_name: long_name.to_owned(),
439 hint: hint.to_owned(),
440 desc: desc.to_owned(),
446 /// Create a long option that is optional and does not take an argument.
448 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
449 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
450 /// * `desc` - Description for usage help
451 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
452 let len = short_name.len();
453 assert!(len == 1 || len == 0);
455 short_name: short_name.to_owned(),
456 long_name: long_name.to_owned(),
458 desc: desc.to_owned(),
464 /// Create a long option that can occur more than once and does not
465 /// take an argument.
467 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
468 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
469 /// * `desc` - Description for usage help
470 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
471 let len = short_name.len();
472 assert!(len == 1 || len == 0);
474 short_name: short_name.to_owned(),
475 long_name: long_name.to_owned(),
477 desc: desc.to_owned(),
483 /// Create a long option that is optional and takes an optional argument.
485 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
486 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
487 /// * `desc` - Description for usage help
488 /// * `hint` - Hint that is used in place of the argument in the usage help,
489 /// e.g. `"FILE"` for a `-o FILE` option
490 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
491 let len = short_name.len();
492 assert!(len == 1 || len == 0);
494 short_name: short_name.to_owned(),
495 long_name: long_name.to_owned(),
496 hint: hint.to_owned(),
497 desc: desc.to_owned(),
503 /// Create a long option that is optional, takes an argument, and may occur
506 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
507 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
508 /// * `desc` - Description for usage help
509 /// * `hint` - Hint that is used in place of the argument in the usage help,
510 /// e.g. `"FILE"` for a `-o FILE` option
511 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
512 let len = short_name.len();
513 assert!(len == 1 || len == 0);
515 short_name: short_name.to_owned(),
516 long_name: long_name.to_owned(),
517 hint: hint.to_owned(),
518 desc: desc.to_owned(),
524 /// Create a generic option group, stating all parameters explicitly
525 pub fn opt(short_name: &str,
530 occur: Occur) -> OptGroup {
531 let len = short_name.len();
532 assert!(len == 1 || len == 0);
534 short_name: short_name.to_owned(),
535 long_name: long_name.to_owned(),
536 hint: hint.to_owned(),
537 desc: desc.to_owned(),
543 impl fmt::Display for Fail {
544 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
546 ArgumentMissing(ref nm) => {
547 write!(f, "Argument to option '{}' missing.", *nm)
549 UnrecognizedOption(ref nm) => {
550 write!(f, "Unrecognized option: '{}'.", *nm)
552 OptionMissing(ref nm) => {
553 write!(f, "Required option '{}' missing.", *nm)
555 OptionDuplicated(ref nm) => {
556 write!(f, "Option '{}' given more than once.", *nm)
558 UnexpectedArgument(ref nm) => {
559 write!(f, "Option '{}' does not take an argument.", *nm)
565 /// Parse command line arguments according to the provided options.
567 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
568 /// `opt_str`, etc. to interrogate results.
571 /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` to display
572 /// information about it.
573 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
574 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
575 let n_opts = opts.len();
577 fn f(_x: usize) -> Vec<Optval> { Vec::new() }
579 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
580 let mut free: Vec<String> = Vec::new();
584 let cur = args[i].clone();
585 let curlen = cur.len();
586 if !is_arg(&cur[..]) {
588 } else if cur == "--" {
590 while j < l { free.push(args[j].clone()); j += 1; }
594 let mut i_arg = None;
595 if cur.as_bytes()[1] == b'-' {
596 let tail = &cur[2..curlen];
597 let tail_eq: Vec<&str> = tail.split('=').collect();
598 if tail_eq.len() <= 1 {
599 names = vec!(Long(tail.to_owned()));
602 vec!(Long(tail_eq[0].to_owned()));
603 i_arg = Some(tail_eq[1].to_owned());
609 let ch = cur.char_at(j);
612 /* In a series of potential options (eg. -aheJ), if we
613 see one which takes an argument, we assume all
614 subsequent characters make up the argument. This
615 allows options such as -L/usr/local/lib/foo to be
616 interpreted correctly
619 let opt_id = match find_opt(&opts, opt.clone()) {
621 None => return Err(UnrecognizedOption(opt.to_string()))
626 let arg_follows = match opts[opt_id].hasarg {
631 let next = j + ch.len_utf8();
632 if arg_follows && next < curlen {
633 i_arg = Some((&cur[next..curlen]).to_owned());
640 let mut name_pos = 0;
643 let optid = match find_opt(&opts, (*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][..]) {
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()));
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[..]);
727 _ => panic!("the short name should only be 1 ascii char long"),
731 match long_name.len() {
735 row.push_str(&long_name[..]);
743 Yes => row.push_str(&hint[..]),
746 row.push_str(&hint[..]);
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 0..24 - rowlen {
759 row.push_str(&desc_sep[..]);
762 // Normalize desc to contain words separated by one space character
763 let mut desc_normalized_whitespace = String::new();
764 for word in desc.split_whitespace() {
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[..], 54, |substr| {
772 desc_rows.push(substr.to_owned());
776 // FIXME: #5516 should be graphemes not codepoints
777 // wrapped description
778 row.push_str(&desc_rows.join(&desc_sep[..]));
783 format!("{}\n\nOptions:\n{}\n", brief,
784 rows.collect::<Vec<String>>().join("\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.is_empty() {
797 line.push_str(&opt.short_name[..]);
800 line.push_str(&opt.long_name[..]);
803 if opt.hasarg != No {
805 if opt.hasarg == Maybe {
808 line.push_str(&opt.hint[..]);
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>>()
834 #[derive(Copy, Clone)]
835 enum SplitWithinState {
836 A, // leading whitespace, initial state
838 C, // internal and trailing whitespace
840 #[derive(Copy, Clone)]
842 Ws, // current char is whitespace
843 Cr // current char is not whitespace
845 #[derive(Copy, Clone)]
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: usize, 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 = |cont: &mut bool, (i, c): (usize, 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[last_start..i + 1]),
894 (B, Cr, OverLim) => {
895 *cont = it(&ss[slice_start..last_end]);
896 slice_start = last_start;
899 (B, Ws, UnderLim) => {
903 (B, Ws, OverLim) => {
905 *cont = it(&ss[slice_start..last_end]);
909 (C, Cr, UnderLim) => {
913 (C, Cr, OverLim) => {
914 *cont = it(&ss[slice_start..last_end]);
920 (C, Ws, OverLim) => {
921 *cont = it(&ss[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: usize, 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).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::usize::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, &opts);
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, &opts) {
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, &opts);
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, &opts);
1013 Err(ArgumentMissing(_)) => {},
1016 let short_args = vec!("-t".to_string());
1017 match getopts(&short_args, &opts) {
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, &opts);
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, &opts);
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, &opts) {
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, &opts);
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, &opts);
1081 Err(ArgumentMissing(_)) => {},
1084 let short_args = vec!("-t".to_string());
1085 match getopts(&short_args, &opts) {
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, &opts);
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, &opts);
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, &opts) {
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts);
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, &opts) {
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, &opts);
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, &opts);
1304 Err(ArgumentMissing(_)) => {},
1307 let short_args = vec!("-t".to_string());
1308 match getopts(&short_args, &opts) {
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, &opts);
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, &opts);
1339 Err(UnrecognizedOption(_)) => {},
1342 let short_args = vec!("-u".to_string());
1343 match getopts(&short_args, &opts) {
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, &opts);
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, &opts) {
1406 result::Result::Ok(m) => m,
1407 result::Result::Err(_) => panic!()
1409 assert!(matches_single.opts_present(&["e".to_string()]));
1410 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1411 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1412 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1413 assert!(!matches_single.opts_present(&["thing".to_string()]));
1414 assert!(!matches_single.opts_present(&[]));
1416 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1417 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1419 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1422 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1424 let matches_both = &match getopts(&args_both, &opts) {
1425 result::Result::Ok(m) => m,
1426 result::Result::Err(_) => panic!()
1428 assert!(matches_both.opts_present(&["e".to_string()]));
1429 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1430 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1431 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1432 assert!(!matches_both.opts_present(&["f".to_string()]));
1433 assert!(!matches_both.opts_present(&["thing".to_string()]));
1434 assert!(!matches_both.opts_present(&[]));
1436 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1437 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1438 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1440 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1446 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1447 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1448 optmulti("M", "", "something", "MMMM"));
1449 let matches = &match getopts(&args, &opts) {
1450 result::Result::Ok(m) => m,
1451 result::Result::Err(_) => panic!()
1453 assert!(matches.opts_present(&["L".to_string()]));
1454 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1455 assert!(matches.opts_present(&["M".to_string()]));
1456 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1461 fn test_nospace_conflict() {
1462 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1463 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1464 optflagmulti("v", "verbose", "Verbose"));
1465 let matches = &match getopts(&args, &opts) {
1466 result::Result::Ok(m) => m,
1467 result::Result::Err(e) => panic!( "{}", e )
1469 assert!(matches.opts_present(&["L".to_string()]));
1470 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1471 assert!(matches.opts_present(&["v".to_string()]));
1472 assert_eq!(3, matches.opt_count("v"));
1476 fn test_long_to_short() {
1477 let mut short = Opt {
1478 name: Name::Long("banana".to_string()),
1479 hasarg: HasArg::Yes,
1481 aliases: Vec::new(),
1483 short.aliases = vec!(Opt { name: Name::Short('b'),
1484 hasarg: HasArg::Yes,
1486 aliases: Vec::new() });
1487 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1489 assert!(verbose.long_to_short() == short);
1493 fn test_aliases_long_and_short() {
1495 optflagmulti("a", "apple", "Desc"));
1497 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1499 let matches = getopts(&args, &opts).unwrap();
1500 assert_eq!(3, matches.opt_count("a"));
1501 assert_eq!(3, matches.opt_count("apple"));
1506 let optgroups = vec!(
1507 reqopt("b", "banana", "Desc", "VAL"),
1508 optopt("a", "012345678901234567890123456789",
1510 optflag("k", "kiwi", "Desc"),
1511 optflagopt("p", "", "Desc", "VAL"),
1512 optmulti("l", "", "Desc", "VAL"));
1518 -b --banana VAL Desc
1519 -a --012345678901234567890123456789 VAL
1526 let generated_usage = usage("Usage: fruits", &optgroups);
1528 debug!("expected: <<{}>>", expected);
1529 debug!("generated: <<{}>>", generated_usage);
1530 assert_eq!(generated_usage, expected);
1534 fn test_usage_description_wrapping() {
1535 // indentation should be 24 spaces
1536 // lines wrap after 78: or rather descriptions wrap after 54
1538 let optgroups = vec!(
1539 optflag("k", "kiwi",
1540 "This is a long description which won't be wrapped..+.."), // 54
1541 optflag("a", "apple",
1542 "This is a long description which _will_ be wrapped..+.."));
1548 -k --kiwi This is a long description which won't be wrapped..+..
1549 -a --apple This is a long description which _will_ be
1553 let usage = usage("Usage: fruits", &optgroups);
1555 debug!("expected: <<{}>>", expected);
1556 debug!("generated: <<{}>>", usage);
1557 assert!(usage == expected)
1561 fn test_usage_description_multibyte_handling() {
1562 let optgroups = vec!(
1563 optflag("k", "k\u{2013}w\u{2013}",
1564 "The word kiwi is normally spelled with two i's"),
1565 optflag("a", "apple",
1566 "This \u{201C}description\u{201D} has some characters that could \
1567 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1573 -k --k–w– The word kiwi is normally spelled with two i's
1574 -a --apple This “description” has some characters that could
1575 confuse the line wrapping; an apple costs 0.51€ in
1576 some parts of Europe.
1579 let usage = usage("Usage: fruits", &optgroups);
1581 debug!("expected: <<{}>>", expected);
1582 debug!("generated: <<{}>>", usage);
1583 assert!(usage == expected)
1587 fn test_short_usage() {
1588 let optgroups = vec!(
1589 reqopt("b", "banana", "Desc", "VAL"),
1590 optopt("a", "012345678901234567890123456789",
1592 optflag("k", "kiwi", "Desc"),
1593 optflagopt("p", "", "Desc", "VAL"),
1594 optmulti("l", "", "Desc", "VAL"));
1596 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1597 let generated_usage = short_usage("fruits", &optgroups);
1599 debug!("expected: <<{}>>", expected);
1600 debug!("generated: <<{}>>", generated_usage);
1601 assert_eq!(generated_usage, expected);