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 //! #![feature(rustc_private)]
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, opts));
53 //! let args: Vec<String> = env::args().collect();
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[1..], opts) {
63 //! Err(f) => { panic!(f.to_string()) }
65 //! if matches.opt_present("h") {
66 //! print_usage(&program, 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, opts);
76 //! do_work(&input, output);
81 // Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
82 #![cfg_attr(stage0, feature(custom_attribute))]
83 #![crate_name = "getopts"]
84 #![unstable(feature = "rustc_private",
85 reason = "use the crates.io `getopts` library instead",
87 #![cfg_attr(stage0, staged_api)]
88 #![crate_type = "rlib"]
89 #![crate_type = "dylib"]
90 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
91 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
92 html_root_url = "https://doc.rust-lang.org/nightly/",
93 html_playground_url = "https://play.rust-lang.org/",
94 test(attr(deny(warnings))))]
96 #![deny(missing_docs)]
97 #![feature(staged_api)]
99 #![cfg_attr(test, feature(rustc_private))]
110 use self::SplitWithinState::*;
111 use self::Whitespace::*;
112 use self::LengthLimit::*;
115 use std::iter::repeat;
118 /// Name of an option. Either a string or a single char.
119 #[derive(Clone, PartialEq, Eq, Debug)]
121 /// A string representing the long name of an option.
122 /// For example: "help"
124 /// A char representing the short name of an option.
129 /// Describes whether an option has an argument.
130 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
132 /// The option requires an argument.
134 /// The option takes no argument.
136 /// The option argument is optional.
140 /// Describes how often an option may occur.
141 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
143 /// The option occurs once.
145 /// The option occurs at most once.
147 /// The option occurs zero or more times.
151 /// A description of a possible option.
152 #[derive(Clone, PartialEq, Eq, Debug)]
154 /// Name of the option
156 /// Whether it has an argument
158 /// How often it can occur
160 /// Which options it aliases
161 pub aliases: Vec<Opt>,
164 /// One group of options, e.g., both `-h` and `--help`, along with
165 /// their shared description and properties.
166 #[derive(Clone, PartialEq, Eq, Debug)]
167 pub struct OptGroup {
168 /// Short name of the option, e.g. `h` for a `-h` option
169 pub short_name: String,
170 /// Long name of the option, e.g. `help` for a `--help` option
171 pub long_name: String,
172 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
174 /// Description for usage help text
176 /// Whether option has an argument
178 /// How often it can occur
182 /// Describes whether an option is given at all or has a value.
183 #[derive(Clone, PartialEq, Eq, Debug)]
189 /// The result of checking command line arguments. Contains a vector
190 /// of matches and a vector of free strings.
191 #[derive(Clone, PartialEq, Eq, Debug)]
193 /// Options that matched
195 /// Values of the Options that matched
196 vals: Vec<Vec<Optval>>,
197 /// Free string fragments
198 pub free: Vec<String>,
201 /// The type returned when the command line does not conform to the
202 /// expected format. Use the `Debug` implementation to output detailed
204 #[derive(Clone, PartialEq, Eq, Debug)]
206 /// The option requires an argument but none was passed.
207 ArgumentMissing(String),
208 /// The passed option is not declared among the possible options.
209 UnrecognizedOption(String),
210 /// A required option is not present.
211 OptionMissing(String),
212 /// A single occurrence option is being used multiple times.
213 OptionDuplicated(String),
214 /// There's an argument being passed to a non-argument option.
215 UnexpectedArgument(String),
218 /// The type of failure that occurred.
219 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
220 #[allow(missing_docs)]
229 /// The result of parsing a command line with a set of options.
230 pub type Result = result::Result<Matches, Fail>;
233 fn from_str(nm: &str) -> Name {
241 fn to_string(&self) -> String {
243 Short(ch) => ch.to_string(),
244 Long(ref s) => s.to_owned(),
250 /// Translate OptGroup into Opt.
251 /// (Both short and long names correspond to different Opts).
252 pub fn long_to_short(&self) -> Opt {
261 match (short_name.len(), long_name.len()) {
262 (0, 0) => panic!("this long-format option was given no name"),
265 name: Long((long_name)),
273 name: Short(short_name.char_at(0)),
281 name: Long((long_name)),
285 name: Short(short_name.char_at(0)),
292 (_, _) => panic!("something is wrong with the long-form opt"),
298 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
299 match find_opt(&self.opts[..], Name::from_str(nm)) {
300 Some(id) => self.vals[id].clone(),
301 None => panic!("No option '{}' defined", nm),
305 fn opt_val(&self, nm: &str) -> Option<Optval> {
306 let vals = self.opt_vals(nm);
310 Some(vals[0].clone())
314 /// Returns true if an option was matched.
315 pub fn opt_present(&self, nm: &str) -> bool {
316 !self.opt_vals(nm).is_empty()
319 /// Returns the number of times an option was matched.
320 pub fn opt_count(&self, nm: &str) -> usize {
321 self.opt_vals(nm).len()
324 /// Returns true if any of several options were matched.
325 pub fn opts_present(&self, names: &[String]) -> bool {
327 match find_opt(&self.opts, Name::from_str(&**nm)) {
328 Some(id) if !self.vals[id].is_empty() => return true,
335 /// Returns the string argument supplied to one of several matching options or `None`.
336 pub fn opts_str(&self, names: &[String]) -> Option<String> {
338 match self.opt_val(&nm[..]) {
339 Some(Val(ref s)) => return Some(s.clone()),
346 /// Returns a vector of the arguments provided to all matches of the given
349 /// Used when an option accepts multiple values.
350 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
351 let mut acc: Vec<String> = Vec::new();
352 let r = self.opt_vals(nm);
355 Val(ref s) => acc.push((*s).clone()),
362 /// Returns the string argument supplied to a matching option or `None`.
363 pub fn opt_str(&self, nm: &str) -> Option<String> {
364 let vals = self.opt_vals(nm);
366 return None::<String>;
369 Val(ref s) => Some((*s).clone()),
375 /// Returns the matching string, a default, or none.
377 /// Returns none if the option was not present, `def` if the option was
378 /// present but no argument was provided, and the argument if the option was
379 /// present and an argument was provided.
380 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
381 let vals = self.opt_vals(nm);
386 Val(ref s) => Some((*s).clone()),
387 _ => Some(def.to_owned()),
393 fn is_arg(arg: &str) -> bool {
394 arg.len() > 1 && arg.as_bytes()[0] == b'-'
397 fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
398 // Search main options.
399 let pos = opts.iter().position(|opt| opt.name == nm);
404 // Search in aliases.
405 for candidate in opts {
406 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
407 return opts.iter().position(|opt| opt.name == candidate.name);
414 /// Create a long option that is required and takes an argument.
416 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
417 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
418 /// * `desc` - Description for usage help
419 /// * `hint` - Hint that is used in place of the argument in the usage help,
420 /// e.g. `"FILE"` for a `-o FILE` option
421 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
422 let len = short_name.len();
423 assert!(len == 1 || len == 0);
425 short_name: short_name.to_owned(),
426 long_name: long_name.to_owned(),
427 hint: hint.to_owned(),
428 desc: desc.to_owned(),
434 /// Create a long option that is optional and takes an argument.
436 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
437 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
438 /// * `desc` - Description for usage help
439 /// * `hint` - Hint that is used in place of the argument in the usage help,
440 /// e.g. `"FILE"` for a `-o FILE` option
441 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
442 let len = short_name.len();
443 assert!(len == 1 || len == 0);
445 short_name: short_name.to_owned(),
446 long_name: long_name.to_owned(),
447 hint: hint.to_owned(),
448 desc: desc.to_owned(),
454 /// Create a long option that is optional and does not take an argument.
456 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
457 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
458 /// * `desc` - Description for usage help
459 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
460 let len = short_name.len();
461 assert!(len == 1 || len == 0);
463 short_name: short_name.to_owned(),
464 long_name: long_name.to_owned(),
466 desc: desc.to_owned(),
472 /// Create a long option that can occur more than once and does not
473 /// take an argument.
475 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
476 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
477 /// * `desc` - Description for usage help
478 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
479 let len = short_name.len();
480 assert!(len == 1 || len == 0);
482 short_name: short_name.to_owned(),
483 long_name: long_name.to_owned(),
485 desc: desc.to_owned(),
491 /// Create a long option that is optional and takes an optional argument.
493 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
494 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
495 /// * `desc` - Description for usage help
496 /// * `hint` - Hint that is used in place of the argument in the usage help,
497 /// e.g. `"FILE"` for a `-o FILE` option
498 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
499 let len = short_name.len();
500 assert!(len == 1 || len == 0);
502 short_name: short_name.to_owned(),
503 long_name: long_name.to_owned(),
504 hint: hint.to_owned(),
505 desc: desc.to_owned(),
511 /// Create a long option that is optional, takes an argument, and may occur
514 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
515 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
516 /// * `desc` - Description for usage help
517 /// * `hint` - Hint that is used in place of the argument in the usage help,
518 /// e.g. `"FILE"` for a `-o FILE` option
519 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
520 let len = short_name.len();
521 assert!(len == 1 || len == 0);
523 short_name: short_name.to_owned(),
524 long_name: long_name.to_owned(),
525 hint: hint.to_owned(),
526 desc: desc.to_owned(),
532 /// Create a generic option group, stating all parameters explicitly
533 pub fn opt(short_name: &str,
540 let len = short_name.len();
541 assert!(len == 1 || len == 0);
543 short_name: short_name.to_owned(),
544 long_name: long_name.to_owned(),
545 hint: hint.to_owned(),
546 desc: desc.to_owned(),
552 impl fmt::Display for Fail {
553 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
555 ArgumentMissing(ref nm) => write!(f, "Argument to option '{}' missing.", *nm),
556 UnrecognizedOption(ref nm) => write!(f, "Unrecognized option: '{}'.", *nm),
557 OptionMissing(ref nm) => write!(f, "Required option '{}' missing.", *nm),
558 OptionDuplicated(ref nm) => write!(f, "Option '{}' given more than once.", *nm),
559 UnexpectedArgument(ref nm) => write!(f, "Option '{}' does not take an argument.", *nm),
564 /// Parse command line arguments according to the provided options.
566 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
567 /// `opt_str`, etc. to interrogate results.
570 /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` to display
571 /// information about it.
572 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
573 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
574 let n_opts = opts.len();
576 fn f(_x: usize) -> Vec<Optval> {
580 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
581 let mut free: Vec<String> = Vec::new();
585 let cur = args[i].clone();
586 let curlen = cur.len();
587 if !is_arg(&cur[..]) {
589 } else if cur == "--" {
592 free.push(args[j].clone());
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_owned())];
605 names = vec![Long(tail_eq[0].to_owned())];
606 i_arg = Some(tail_eq[1].to_owned());
612 let ch = cur.char_at(j);
615 // In a series of potential options (eg. -aheJ), if we
616 // see one which takes an argument, we assume all
617 // subsequent characters make up the argument. This
618 // allows options such as -L/usr/local/lib/foo to be
619 // interpreted correctly
621 let opt_id = match find_opt(&opts, opt.clone()) {
623 None => return Err(UnrecognizedOption(opt.to_string())),
628 let arg_follows = match opts[opt_id].hasarg {
633 let next = j + ch.len_utf8();
634 if arg_follows && next < curlen {
635 i_arg = Some((&cur[next..curlen]).to_owned());
642 let mut name_pos = 0;
645 let optid = match find_opt(&opts, (*nm).clone()) {
647 None => return Err(UnrecognizedOption(nm.to_string())),
649 match opts[optid].hasarg {
651 if name_pos == names.len() && !i_arg.is_none() {
652 return Err(UnexpectedArgument(nm.to_string()));
654 let v = &mut vals[optid];
658 if !i_arg.is_none() {
659 let v = &mut vals[optid];
660 v.push(Val((i_arg.clone()).unwrap()));
661 } else if name_pos < names.len() || i + 1 == l || 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",
785 rows.collect::<Vec<String>>().join("\n"))
788 fn format_option(opt: &OptGroup) -> String {
789 let mut line = String::new();
791 if opt.occur != Req {
795 // Use short_name is possible, but fallback to long_name.
796 if !opt.short_name.is_empty() {
798 line.push_str(&opt.short_name[..]);
801 line.push_str(&opt.long_name[..]);
804 if opt.hasarg != No {
806 if opt.hasarg == Maybe {
809 line.push_str(&opt.hint[..]);
810 if opt.hasarg == Maybe {
815 if opt.occur != Req {
818 if opt.occur == Multi {
825 /// Derive a short one-line usage summary from a set of long options.
826 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
827 let mut line = format!("Usage: {} ", program_name);
828 line.push_str(&opts.iter()
830 .collect::<Vec<String>>()
835 #[derive(Copy, Clone)]
836 enum SplitWithinState {
837 A, // leading whitespace, initial state
839 C, // internal and trailing whitespace
841 #[derive(Copy, Clone)]
843 Ws, // current char is whitespace
844 Cr, // current char is not whitespace
846 #[derive(Copy, Clone)]
848 UnderLim, // current char makes current substring still fit in limit
849 OverLim, // current char makes current substring no longer fit in limit
853 /// Splits a string into substrings with possibly internal whitespace,
854 /// each of them at most `lim` bytes long. The substrings have leading and trailing
855 /// whitespace removed, and are only cut at whitespace boundaries.
857 /// Note: Function was moved here from `std::str` because this module is the only place that
858 /// uses it, and because it was too specific for a general string function.
862 /// Panics during iteration if the string contains a non-whitespace
863 /// sequence longer than the limit.
864 fn each_split_within<F>(ss: &str, lim: usize, mut it: F) -> bool
865 where F: FnMut(&str) -> bool
867 // Just for fun, let's write this as a state machine:
869 let mut slice_start = 0;
870 let mut last_start = 0;
871 let mut last_end = 0;
873 let mut fake_i = ss.len();
878 // if the limit is larger than the string, lower it to save cycles
883 let mut machine = |cont: &mut bool, (i, c): (usize, char)| -> bool {
884 let whitespace = if c.is_whitespace() {
889 let limit = if (i - slice_start + 1) <= lim {
895 state = match (state, whitespace, limit) {
903 (B, Cr, UnderLim) => B,
904 (B, Cr, OverLim) if (i - last_start + 1) > lim => {
905 panic!("word starting with {} longer than limit!",
906 &ss[last_start..i + 1])
908 (B, Cr, OverLim) => {
909 *cont = it(&ss[slice_start..last_end]);
910 slice_start = last_start;
913 (B, Ws, UnderLim) => {
917 (B, Ws, OverLim) => {
919 *cont = it(&ss[slice_start..last_end]);
923 (C, Cr, UnderLim) => {
927 (C, Cr, OverLim) => {
928 *cont = it(&ss[slice_start..last_end]);
934 (C, Ws, OverLim) => {
935 *cont = it(&ss[slice_start..last_end]);
938 (C, Ws, UnderLim) => C,
944 ss.char_indices().all(|x| machine(&mut cont, x));
946 // Let the automaton 'run out' by supplying trailing whitespace
952 machine(&mut cont, (fake_i, ' '));
959 fn test_split_within() {
960 fn t(s: &str, i: usize, u: &[String]) {
961 let mut v = Vec::new();
962 each_split_within(s, i, |s| {
963 v.push(s.to_string());
966 assert!(v.iter().zip(u).all(|(a, b)| a == b));
970 t("hello", 15, &["hello".to_string()]);
971 t("\nMary had a little lamb\nLittle lamb\n",
973 &["Mary had a".to_string(), "little lamb".to_string(), "Little lamb".to_string()]);
974 t("\nMary had a little lamb\nLittle lamb\n",
976 &["Mary had a little lamb\nLittle lamb".to_string()]);
984 use std::result::Result::{Err, Ok};
990 let long_args = vec!["--test=20".to_string()];
991 let opts = vec![reqopt("t", "test", "testing", "TEST")];
992 let rs = getopts(&long_args, &opts);
995 assert!(m.opt_present("test"));
996 assert_eq!(m.opt_str("test").unwrap(), "20");
997 assert!(m.opt_present("t"));
998 assert_eq!(m.opt_str("t").unwrap(), "20");
1001 panic!("test_reqopt failed (long arg)");
1004 let short_args = vec!["-t".to_string(), "20".to_string()];
1005 match getopts(&short_args, &opts) {
1007 assert!((m.opt_present("test")));
1008 assert_eq!(m.opt_str("test").unwrap(), "20");
1009 assert!((m.opt_present("t")));
1010 assert_eq!(m.opt_str("t").unwrap(), "20");
1013 panic!("test_reqopt failed (short arg)");
1019 fn test_reqopt_missing() {
1020 let args = vec!["blah".to_string()];
1021 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1022 let rs = getopts(&args, &opts);
1024 Err(OptionMissing(_)) => {}
1030 fn test_reqopt_no_arg() {
1031 let long_args = vec!["--test".to_string()];
1032 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1033 let rs = getopts(&long_args, &opts);
1035 Err(ArgumentMissing(_)) => {}
1038 let short_args = vec!["-t".to_string()];
1039 match getopts(&short_args, &opts) {
1040 Err(ArgumentMissing(_)) => {}
1046 fn test_reqopt_multi() {
1047 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1048 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1049 let rs = getopts(&args, &opts);
1051 Err(OptionDuplicated(_)) => {}
1059 let long_args = vec!["--test=20".to_string()];
1060 let opts = vec![optopt("t", "test", "testing", "TEST")];
1061 let rs = getopts(&long_args, &opts);
1064 assert!(m.opt_present("test"));
1065 assert_eq!(m.opt_str("test").unwrap(), "20");
1066 assert!((m.opt_present("t")));
1067 assert_eq!(m.opt_str("t").unwrap(), "20");
1071 let short_args = vec!["-t".to_string(), "20".to_string()];
1072 match getopts(&short_args, &opts) {
1074 assert!((m.opt_present("test")));
1075 assert_eq!(m.opt_str("test").unwrap(), "20");
1076 assert!((m.opt_present("t")));
1077 assert_eq!(m.opt_str("t").unwrap(), "20");
1084 fn test_optopt_missing() {
1085 let args = vec!["blah".to_string()];
1086 let opts = vec![optopt("t", "test", "testing", "TEST")];
1087 let rs = getopts(&args, &opts);
1090 assert!(!m.opt_present("test"));
1091 assert!(!m.opt_present("t"));
1098 fn test_optopt_no_arg() {
1099 let long_args = vec!["--test".to_string()];
1100 let opts = vec![optopt("t", "test", "testing", "TEST")];
1101 let rs = getopts(&long_args, &opts);
1103 Err(ArgumentMissing(_)) => {}
1106 let short_args = vec!["-t".to_string()];
1107 match getopts(&short_args, &opts) {
1108 Err(ArgumentMissing(_)) => {}
1114 fn test_optopt_multi() {
1115 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1116 let opts = vec![optopt("t", "test", "testing", "TEST")];
1117 let rs = getopts(&args, &opts);
1119 Err(OptionDuplicated(_)) => {}
1124 // Tests for optflag
1127 let long_args = vec!["--test".to_string()];
1128 let opts = vec![optflag("t", "test", "testing")];
1129 let rs = getopts(&long_args, &opts);
1132 assert!(m.opt_present("test"));
1133 assert!(m.opt_present("t"));
1137 let short_args = vec!["-t".to_string()];
1138 match getopts(&short_args, &opts) {
1140 assert!(m.opt_present("test"));
1141 assert!(m.opt_present("t"));
1148 fn test_optflag_missing() {
1149 let args = vec!["blah".to_string()];
1150 let opts = vec![optflag("t", "test", "testing")];
1151 let rs = getopts(&args, &opts);
1154 assert!(!m.opt_present("test"));
1155 assert!(!m.opt_present("t"));
1162 fn test_optflag_long_arg() {
1163 let args = vec!["--test=20".to_string()];
1164 let opts = vec![optflag("t", "test", "testing")];
1165 let rs = getopts(&args, &opts);
1167 Err(UnexpectedArgument(_)) => {}
1173 fn test_optflag_multi() {
1174 let args = vec!["--test".to_string(), "-t".to_string()];
1175 let opts = vec![optflag("t", "test", "testing")];
1176 let rs = getopts(&args, &opts);
1178 Err(OptionDuplicated(_)) => {}
1184 fn test_optflag_short_arg() {
1185 let args = vec!["-t".to_string(), "20".to_string()];
1186 let opts = vec![optflag("t", "test", "testing")];
1187 let rs = getopts(&args, &opts);
1190 // The next variable after the flag is just a free argument
1192 assert!(m.free[0] == "20");
1198 // Tests for optflagmulti
1200 fn test_optflagmulti_short1() {
1201 let args = vec!["-v".to_string()];
1202 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1203 let rs = getopts(&args, &opts);
1206 assert_eq!(m.opt_count("v"), 1);
1213 fn test_optflagmulti_short2a() {
1214 let args = vec!["-v".to_string(), "-v".to_string()];
1215 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1216 let rs = getopts(&args, &opts);
1219 assert_eq!(m.opt_count("v"), 2);
1226 fn test_optflagmulti_short2b() {
1227 let args = vec!["-vv".to_string()];
1228 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1229 let rs = getopts(&args, &opts);
1232 assert_eq!(m.opt_count("v"), 2);
1239 fn test_optflagmulti_long1() {
1240 let args = vec!["--verbose".to_string()];
1241 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1242 let rs = getopts(&args, &opts);
1245 assert_eq!(m.opt_count("verbose"), 1);
1252 fn test_optflagmulti_long2() {
1253 let args = vec!["--verbose".to_string(), "--verbose".to_string()];
1254 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1255 let rs = getopts(&args, &opts);
1258 assert_eq!(m.opt_count("verbose"), 2);
1265 fn test_optflagmulti_mix() {
1266 let args = vec!["--verbose".to_string(),
1269 "verbose".to_string()];
1270 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1271 let rs = getopts(&args, &opts);
1274 assert_eq!(m.opt_count("verbose"), 4);
1275 assert_eq!(m.opt_count("v"), 4);
1281 // Tests for optmulti
1283 fn test_optmulti() {
1284 let long_args = vec!["--test=20".to_string()];
1285 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1286 let rs = getopts(&long_args, &opts);
1289 assert!((m.opt_present("test")));
1290 assert_eq!(m.opt_str("test").unwrap(), "20");
1291 assert!((m.opt_present("t")));
1292 assert_eq!(m.opt_str("t").unwrap(), "20");
1296 let short_args = vec!["-t".to_string(), "20".to_string()];
1297 match getopts(&short_args, &opts) {
1299 assert!((m.opt_present("test")));
1300 assert_eq!(m.opt_str("test").unwrap(), "20");
1301 assert!((m.opt_present("t")));
1302 assert_eq!(m.opt_str("t").unwrap(), "20");
1309 fn test_optmulti_missing() {
1310 let args = vec!["blah".to_string()];
1311 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1312 let rs = getopts(&args, &opts);
1315 assert!(!m.opt_present("test"));
1316 assert!(!m.opt_present("t"));
1323 fn test_optmulti_no_arg() {
1324 let long_args = vec!["--test".to_string()];
1325 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1326 let rs = getopts(&long_args, &opts);
1328 Err(ArgumentMissing(_)) => {}
1331 let short_args = vec!["-t".to_string()];
1332 match getopts(&short_args, &opts) {
1333 Err(ArgumentMissing(_)) => {}
1339 fn test_optmulti_multi() {
1340 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1341 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1342 let rs = getopts(&args, &opts);
1345 assert!(m.opt_present("test"));
1346 assert_eq!(m.opt_str("test").unwrap(), "20");
1347 assert!(m.opt_present("t"));
1348 assert_eq!(m.opt_str("t").unwrap(), "20");
1349 let pair = m.opt_strs("test");
1350 assert!(pair[0] == "20");
1351 assert!(pair[1] == "30");
1358 fn test_unrecognized_option() {
1359 let long_args = vec!["--untest".to_string()];
1360 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1361 let rs = getopts(&long_args, &opts);
1363 Err(UnrecognizedOption(_)) => {}
1366 let short_args = vec!["-u".to_string()];
1367 match getopts(&short_args, &opts) {
1368 Err(UnrecognizedOption(_)) => {}
1374 fn test_combined() {
1375 let args = vec!["prog".to_string(),
1376 "free1".to_string(),
1379 "free2".to_string(),
1380 "--flag".to_string(),
1381 "--long=30".to_string(),
1390 "-60 70".to_string()];
1391 let opts = vec![optopt("s", "something", "something", "SOMETHING"),
1392 optflag("", "flag", "a flag"),
1393 reqopt("", "long", "hi", "LONG"),
1394 optflag("f", "", "another flag"),
1395 optmulti("m", "", "mmmmmm", "YUM"),
1396 optmulti("n", "", "nothing", "NOTHING"),
1397 optopt("", "notpresent", "nothing to see here", "NOPE")];
1398 let rs = getopts(&args, &opts);
1401 assert!(m.free[0] == "prog");
1402 assert!(m.free[1] == "free1");
1403 assert_eq!(m.opt_str("s").unwrap(), "20");
1404 assert!(m.free[2] == "free2");
1405 assert!((m.opt_present("flag")));
1406 assert_eq!(m.opt_str("long").unwrap(), "30");
1407 assert!((m.opt_present("f")));
1408 let pair = m.opt_strs("m");
1409 assert!(pair[0] == "40");
1410 assert!(pair[1] == "50");
1411 let pair = m.opt_strs("n");
1412 assert!(pair[0] == "-A B");
1413 assert!(pair[1] == "-60 70");
1414 assert!((!m.opt_present("notpresent")));
1422 let opts = vec![optopt("e", "", "encrypt", "ENCRYPT"),
1423 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1424 optopt("f", "", "flag", "FLAG")];
1426 let args_single = vec!["-e".to_string(), "foo".to_string()];
1427 let matches_single = &match getopts(&args_single, &opts) {
1428 result::Result::Ok(m) => m,
1429 result::Result::Err(_) => panic!(),
1431 assert!(matches_single.opts_present(&["e".to_string()]));
1432 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1433 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1434 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1435 assert!(!matches_single.opts_present(&["thing".to_string()]));
1436 assert!(!matches_single.opts_present(&[]));
1438 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1439 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1441 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1444 let args_both = vec!["-e".to_string(),
1446 "--encrypt".to_string(),
1448 let matches_both = &match getopts(&args_both, &opts) {
1449 result::Result::Ok(m) => m,
1450 result::Result::Err(_) => panic!(),
1452 assert!(matches_both.opts_present(&["e".to_string()]));
1453 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1454 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1455 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1456 assert!(!matches_both.opts_present(&["f".to_string()]));
1457 assert!(!matches_both.opts_present(&["thing".to_string()]));
1458 assert!(!matches_both.opts_present(&[]));
1460 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1461 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(),
1463 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1465 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1471 let args = vec!["-Lfoo".to_string(), "-M.".to_string()];
1472 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1473 optmulti("M", "", "something", "MMMM")];
1474 let matches = &match getopts(&args, &opts) {
1475 result::Result::Ok(m) => m,
1476 result::Result::Err(_) => panic!(),
1478 assert!(matches.opts_present(&["L".to_string()]));
1479 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1480 assert!(matches.opts_present(&["M".to_string()]));
1481 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1486 fn test_nospace_conflict() {
1487 let args = vec!["-vvLverbose".to_string(), "-v".to_string()];
1488 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1489 optflagmulti("v", "verbose", "Verbose")];
1490 let matches = &match getopts(&args, &opts) {
1491 result::Result::Ok(m) => m,
1492 result::Result::Err(e) => panic!("{}", e),
1494 assert!(matches.opts_present(&["L".to_string()]));
1495 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1496 assert!(matches.opts_present(&["v".to_string()]));
1497 assert_eq!(3, matches.opt_count("v"));
1501 fn test_long_to_short() {
1502 let mut short = Opt {
1503 name: Name::Long("banana".to_string()),
1504 hasarg: HasArg::Yes,
1506 aliases: Vec::new(),
1508 short.aliases = vec![Opt {
1509 name: Name::Short('b'),
1510 hasarg: HasArg::Yes,
1512 aliases: Vec::new(),
1514 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1516 assert!(verbose.long_to_short() == short);
1520 fn test_aliases_long_and_short() {
1521 let opts = vec![optflagmulti("a", "apple", "Desc")];
1523 let args = vec!["-a".to_string(), "--apple".to_string(), "-a".to_string()];
1525 let matches = getopts(&args, &opts).unwrap();
1526 assert_eq!(3, matches.opt_count("a"));
1527 assert_eq!(3, matches.opt_count("apple"));
1532 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1533 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1534 optflag("k", "kiwi", "Desc"),
1535 optflagopt("p", "", "Desc", "VAL"),
1536 optmulti("l", "", "Desc", "VAL")];
1542 -b --banana VAL Desc
1543 -a --012345678901234567890123456789 VAL
1550 let generated_usage = usage("Usage: fruits", &optgroups);
1552 debug!("expected: <<{}>>", expected);
1553 debug!("generated: <<{}>>", generated_usage);
1554 assert_eq!(generated_usage, expected);
1558 fn test_usage_description_wrapping() {
1559 // indentation should be 24 spaces
1560 // lines wrap after 78: or rather descriptions wrap after 54
1562 let optgroups = vec![optflag("k",
1565 "This is a long description which won't be wrapped..+.."),
1568 "This is a long description which _will_ be wrapped..+..")];
1574 -k --kiwi This is a long description which won't be wrapped..+..
1575 -a --apple This is a long description which _will_ be
1579 let usage = usage("Usage: fruits", &optgroups);
1581 debug!("expected: <<{}>>", expected);
1582 debug!("generated: <<{}>>", usage);
1583 assert!(usage == expected)
1587 fn test_usage_description_multibyte_handling() {
1588 let optgroups = vec![optflag("k",
1589 "k\u{2013}w\u{2013}",
1590 "The word kiwi is normally spelled with two i's"),
1593 "This \u{201C}description\u{201D} has some characters that \
1594 could confuse the line wrapping; an apple costs 0.51€ in \
1595 some parts of Europe.")];
1601 -k --k–w– The word kiwi is normally spelled with two i's
1602 -a --apple This “description” has some characters that could
1603 confuse the line wrapping; an apple costs 0.51€ in
1604 some parts of Europe.
1607 let usage = usage("Usage: fruits", &optgroups);
1609 debug!("expected: <<{}>>", expected);
1610 debug!("generated: <<{}>>", usage);
1611 assert!(usage == expected)
1615 fn test_short_usage() {
1616 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1617 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1618 optflag("k", "kiwi", "Desc"),
1619 optflagopt("p", "", "Desc", "VAL"),
1620 optmulti("l", "", "Desc", "VAL")];
1622 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1623 let generated_usage = short_usage("fruits", &optgroups);
1625 debug!("expected: <<{}>>", expected);
1626 debug!("generated: <<{}>>", generated_usage);
1627 assert_eq!(generated_usage, expected);