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);
80 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
81 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
82 html_root_url = "https://doc.rust-lang.org/nightly/",
83 html_playground_url = "https://play.rust-lang.org/",
84 test(attr(deny(warnings))))]
86 #![deny(missing_docs)]
94 use self::SplitWithinState::*;
95 use self::Whitespace::*;
96 use self::LengthLimit::*;
99 use std::iter::repeat;
102 /// Name of an option. Either a string or a single char.
103 #[derive(Clone, PartialEq, Eq, Debug)]
105 /// A string representing the long name of an option.
106 /// For example: "help"
108 /// A char representing the short name of an option.
113 /// Describes whether an option has an argument.
114 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
116 /// The option requires an argument.
118 /// The option takes no argument.
120 /// The option argument is optional.
124 /// Describes how often an option may occur.
125 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
127 /// The option occurs once.
129 /// The option occurs at most once.
131 /// The option occurs zero or more times.
135 /// A description of a possible option.
136 #[derive(Clone, PartialEq, Eq, Debug)]
138 /// Name of the option
140 /// Whether it has an argument
142 /// How often it can occur
144 /// Which options it aliases
145 pub aliases: Vec<Opt>,
148 /// One group of options, e.g., both `-h` and `--help`, along with
149 /// their shared description and properties.
150 #[derive(Clone, PartialEq, Eq, Debug)]
151 pub struct OptGroup {
152 /// Short name of the option, e.g. `h` for a `-h` option
153 pub short_name: String,
154 /// Long name of the option, e.g. `help` for a `--help` option
155 pub long_name: String,
156 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
158 /// Description for usage help text
160 /// Whether option has an argument
162 /// How often it can occur
166 /// Describes whether an option is given at all or has a value.
167 #[derive(Clone, PartialEq, Eq, Debug)]
173 /// The result of checking command line arguments. Contains a vector
174 /// of matches and a vector of free strings.
175 #[derive(Clone, PartialEq, Eq, Debug)]
177 /// Options that matched
179 /// Values of the Options that matched
180 vals: Vec<Vec<Optval>>,
181 /// Free string fragments
182 pub free: Vec<String>,
185 /// The type returned when the command line does not conform to the
186 /// expected format. Use the `Debug` implementation to output detailed
188 #[derive(Clone, PartialEq, Eq, Debug)]
190 /// The option requires an argument but none was passed.
191 ArgumentMissing(String),
192 /// The passed option is not declared among the possible options.
193 UnrecognizedOption(String),
194 /// A required option is not present.
195 OptionMissing(String),
196 /// A single occurrence option is being used multiple times.
197 OptionDuplicated(String),
198 /// There's an argument being passed to a non-argument option.
199 UnexpectedArgument(String),
202 /// The type of failure that occurred.
203 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
204 #[allow(missing_docs)]
213 /// The result of parsing a command line with a set of options.
214 pub type Result = result::Result<Matches, Fail>;
217 fn from_str(nm: &str) -> Name {
219 Short(nm.chars().next().unwrap())
225 fn to_string(&self) -> String {
227 Short(ch) => ch.to_string(),
228 Long(ref s) => s.to_owned(),
234 /// Translate OptGroup into Opt.
235 /// (Both short and long names correspond to different Opts).
236 pub fn long_to_short(&self) -> Opt {
245 match (short_name.len(), long_name.len()) {
246 (0, 0) => panic!("this long-format option was given no name"),
249 name: Long((long_name)),
257 name: Short(short_name.chars().next().unwrap()),
265 name: Long((long_name)),
269 name: Short(short_name.chars().next().unwrap()),
276 _ => panic!("something is wrong with the long-form opt"),
282 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
283 match find_opt(&self.opts[..], Name::from_str(nm)) {
284 Some(id) => self.vals[id].clone(),
285 None => panic!("No option '{}' defined", nm),
289 fn opt_val(&self, nm: &str) -> Option<Optval> {
290 let vals = self.opt_vals(nm);
294 Some(vals[0].clone())
298 /// Returns true if an option was matched.
299 pub fn opt_present(&self, nm: &str) -> bool {
300 !self.opt_vals(nm).is_empty()
303 /// Returns the number of times an option was matched.
304 pub fn opt_count(&self, nm: &str) -> usize {
305 self.opt_vals(nm).len()
308 /// Returns true if any of several options were matched.
309 pub fn opts_present(&self, names: &[String]) -> bool {
311 match find_opt(&self.opts, Name::from_str(&**nm)) {
312 Some(id) if !self.vals[id].is_empty() => return true,
319 /// Returns the string argument supplied to one of several matching options or `None`.
320 pub fn opts_str(&self, names: &[String]) -> Option<String> {
322 if let Some(Val(ref s)) = self.opt_val(&nm[..]) {
323 return Some(s.clone())
329 /// Returns a vector of the arguments provided to all matches of the given
332 /// Used when an option accepts multiple values.
333 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
334 let mut acc: Vec<String> = Vec::new();
335 let r = self.opt_vals(nm);
338 Val(ref s) => acc.push((*s).clone()),
345 /// Returns the string argument supplied to a matching option or `None`.
346 pub fn opt_str(&self, nm: &str) -> Option<String> {
347 let vals = self.opt_vals(nm);
349 return None::<String>;
352 Val(ref s) => Some((*s).clone()),
358 /// Returns the matching string, a default, or none.
360 /// Returns none if the option was not present, `def` if the option was
361 /// present but no argument was provided, and the argument if the option was
362 /// present and an argument was provided.
363 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
364 let vals = self.opt_vals(nm);
369 Val(ref s) => Some((*s).clone()),
370 _ => Some(def.to_owned()),
376 fn is_arg(arg: &str) -> bool {
377 arg.len() > 1 && arg.as_bytes()[0] == b'-'
380 fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
381 // Search main options.
382 let pos = opts.iter().position(|opt| opt.name == nm);
387 // Search in aliases.
388 for candidate in opts {
389 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
390 return opts.iter().position(|opt| opt.name == candidate.name);
397 /// Create a long option that is required and takes an argument.
399 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
400 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
401 /// * `desc` - Description for usage help
402 /// * `hint` - Hint that is used in place of the argument in the usage help,
403 /// e.g. `"FILE"` for a `-o FILE` option
404 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
405 let len = short_name.len();
406 assert!(len == 1 || len == 0);
408 short_name: short_name.to_owned(),
409 long_name: long_name.to_owned(),
410 hint: hint.to_owned(),
411 desc: desc.to_owned(),
417 /// Create a long option that is optional and takes an argument.
419 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
420 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
421 /// * `desc` - Description for usage help
422 /// * `hint` - Hint that is used in place of the argument in the usage help,
423 /// e.g. `"FILE"` for a `-o FILE` option
424 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
425 let len = short_name.len();
426 assert!(len == 1 || len == 0);
428 short_name: short_name.to_owned(),
429 long_name: long_name.to_owned(),
430 hint: hint.to_owned(),
431 desc: desc.to_owned(),
437 /// Create a long option that is optional and does not take an argument.
439 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
440 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
441 /// * `desc` - Description for usage help
442 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
443 let len = short_name.len();
444 assert!(len == 1 || len == 0);
446 short_name: short_name.to_owned(),
447 long_name: long_name.to_owned(),
449 desc: desc.to_owned(),
455 /// Create a long option that can occur more than once and does not
456 /// take an argument.
458 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
459 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
460 /// * `desc` - Description for usage help
461 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
462 let len = short_name.len();
463 assert!(len == 1 || len == 0);
465 short_name: short_name.to_owned(),
466 long_name: long_name.to_owned(),
468 desc: desc.to_owned(),
474 /// Create a long option that is optional and takes an optional argument.
476 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
477 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
478 /// * `desc` - Description for usage help
479 /// * `hint` - Hint that is used in place of the argument in the usage help,
480 /// e.g. `"FILE"` for a `-o FILE` option
481 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
482 let len = short_name.len();
483 assert!(len == 1 || len == 0);
485 short_name: short_name.to_owned(),
486 long_name: long_name.to_owned(),
487 hint: hint.to_owned(),
488 desc: desc.to_owned(),
494 /// Create a long option that is optional, takes an argument, and may occur
497 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
498 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
499 /// * `desc` - Description for usage help
500 /// * `hint` - Hint that is used in place of the argument in the usage help,
501 /// e.g. `"FILE"` for a `-o FILE` option
502 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
503 let len = short_name.len();
504 assert!(len == 1 || len == 0);
506 short_name: short_name.to_owned(),
507 long_name: long_name.to_owned(),
508 hint: hint.to_owned(),
509 desc: desc.to_owned(),
515 /// Create a generic option group, stating all parameters explicitly
516 pub fn opt(short_name: &str,
523 let len = short_name.len();
524 assert!(len == 1 || len == 0);
526 short_name: short_name.to_owned(),
527 long_name: long_name.to_owned(),
528 hint: hint.to_owned(),
529 desc: desc.to_owned(),
535 impl fmt::Display for Fail {
536 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
538 ArgumentMissing(ref nm) => write!(f, "Argument to option '{}' missing.", *nm),
539 UnrecognizedOption(ref nm) => write!(f, "Unrecognized option: '{}'.", *nm),
540 OptionMissing(ref nm) => write!(f, "Required option '{}' missing.", *nm),
541 OptionDuplicated(ref nm) => write!(f, "Option '{}' given more than once.", *nm),
542 UnexpectedArgument(ref nm) => write!(f, "Option '{}' does not take an argument.", *nm),
547 /// Parse command line arguments according to the provided options.
549 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
550 /// `opt_str`, etc. to interrogate results.
553 /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` to display
554 /// information about it.
555 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
556 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
557 let n_opts = opts.len();
559 fn f(_x: usize) -> Vec<Optval> {
563 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
564 let mut free: Vec<String> = Vec::new();
568 let cur = args[i].clone();
569 let curlen = cur.len();
570 if !is_arg(&cur[..]) {
572 } else if cur == "--" {
575 free.push(args[j].clone());
581 let mut i_arg = None;
582 if cur.as_bytes()[1] == b'-' {
583 let tail = &cur[2..curlen];
584 let tail_eq: Vec<&str> = tail.splitn(2, '=').collect();
585 if tail_eq.len() <= 1 {
586 names = vec![Long(tail.to_owned())];
588 names = vec![Long(tail_eq[0].to_owned())];
589 i_arg = Some(tail_eq[1].to_owned());
595 let ch = cur[j..].chars().next().unwrap();
598 // In a series of potential options (eg. -aheJ), if we
599 // see one which takes an argument, we assume all
600 // subsequent characters make up the argument. This
601 // allows options such as -L/usr/local/lib/foo to be
602 // interpreted correctly
604 let opt_id = match find_opt(&opts, opt.clone()) {
606 None => return Err(UnrecognizedOption(opt.to_string())),
611 let arg_follows = match opts[opt_id].hasarg {
616 let next = j + ch.len_utf8();
617 if arg_follows && next < curlen {
618 i_arg = Some((&cur[next..curlen]).to_owned());
625 let mut name_pos = 0;
628 let optid = match find_opt(&opts, (*nm).clone()) {
630 None => return Err(UnrecognizedOption(nm.to_string())),
632 match opts[optid].hasarg {
634 if name_pos == names.len() && !i_arg.is_none() {
635 return Err(UnexpectedArgument(nm.to_string()));
637 let v = &mut vals[optid];
641 if !i_arg.is_none() {
642 let v = &mut vals[optid];
643 v.push(Val((i_arg.clone()).unwrap()));
644 } else if name_pos < names.len() || i + 1 == l || is_arg(&args[i + 1][..]) {
645 let v = &mut vals[optid];
649 let v = &mut vals[optid];
650 v.push(Val(args[i].clone()));
654 if !i_arg.is_none() {
655 let v = &mut vals[optid];
656 v.push(Val(i_arg.clone().unwrap()));
657 } else if i + 1 == l {
658 return Err(ArgumentMissing(nm.to_string()));
661 let v = &mut vals[optid];
662 v.push(Val(args[i].clone()));
671 let n = vals[i].len();
672 let occ = opts[i].occur;
673 if occ == Req && n == 0 {
674 return Err(OptionMissing(opts[i].name.to_string()));
676 if occ != Multi && n > 1 {
677 return Err(OptionDuplicated(opts[i].name.to_string()));
687 /// Derive a usage message from a set of long options.
688 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
690 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
692 let rows = opts.iter().map(|optref| {
693 let OptGroup{short_name,
698 ..} = (*optref).clone();
700 let mut row = repeat(" ").take(4).collect::<String>();
703 match short_name.len() {
707 row.push_str(&short_name[..]);
710 _ => panic!("the short name should only be 1 ascii char long"),
714 match long_name.len() {
718 row.push_str(&long_name[..]);
726 Yes => row.push_str(&hint[..]),
729 row.push_str(&hint[..]);
734 // FIXME(https://github.com/rust-lang-nursery/getopts/issues/7)
735 // should be graphemes not codepoints
737 // here we just need to indent the start of the description
738 let rowlen = row.chars().count();
740 for _ in 0..24 - rowlen {
744 row.push_str(&desc_sep[..]);
747 // Normalize desc to contain words separated by one space character
748 let mut desc_normalized_whitespace = String::new();
749 for word in desc.split_whitespace() {
750 desc_normalized_whitespace.push_str(word);
751 desc_normalized_whitespace.push(' ');
754 // FIXME(https://github.com/rust-lang-nursery/getopts/issues/7)
755 // should be graphemes not codepoints
756 let mut desc_rows = Vec::new();
757 each_split_within(&desc_normalized_whitespace[..], 54, |substr| {
758 desc_rows.push(substr.to_owned());
762 // FIXME(https://github.com/rust-lang-nursery/getopts/issues/7)
763 // should be graphemes not codepoints
765 // wrapped description
766 row.push_str(&desc_rows.join(&desc_sep[..]));
771 format!("{}\n\nOptions:\n{}\n",
773 rows.collect::<Vec<String>>().join("\n"))
776 fn format_option(opt: &OptGroup) -> String {
777 let mut line = String::new();
779 if opt.occur != Req {
783 // Use short_name is possible, but fallback to long_name.
784 if !opt.short_name.is_empty() {
786 line.push_str(&opt.short_name[..]);
789 line.push_str(&opt.long_name[..]);
792 if opt.hasarg != No {
794 if opt.hasarg == Maybe {
797 line.push_str(&opt.hint[..]);
798 if opt.hasarg == Maybe {
803 if opt.occur != Req {
806 if opt.occur == Multi {
813 /// Derive a short one-line usage summary from a set of long options.
814 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
815 let mut line = format!("Usage: {} ", program_name);
816 line.push_str(&opts.iter()
818 .collect::<Vec<String>>()
823 #[derive(Copy, Clone)]
824 enum SplitWithinState {
825 A, // leading whitespace, initial state
827 C, // internal and trailing whitespace
829 #[derive(Copy, Clone)]
831 Ws, // current char is whitespace
832 Cr, // current char is not whitespace
834 #[derive(Copy, Clone)]
836 UnderLim, // current char makes current substring still fit in limit
837 OverLim, // current char makes current substring no longer fit in limit
841 /// Splits a string into substrings with possibly internal whitespace,
842 /// each of them at most `lim` bytes long. The substrings have leading and trailing
843 /// whitespace removed, and are only cut at whitespace boundaries.
845 /// Note: Function was moved here from `std::str` because this module is the only place that
846 /// uses it, and because it was too specific for a general string function.
850 /// Panics during iteration if the string contains a non-whitespace
851 /// sequence longer than the limit.
852 fn each_split_within<F>(ss: &str, lim: usize, mut it: F) -> bool
853 where F: FnMut(&str) -> bool
855 // Just for fun, let's write this as a state machine:
857 let mut slice_start = 0;
858 let mut last_start = 0;
859 let mut last_end = 0;
861 let mut fake_i = ss.len();
866 // if the limit is larger than the string, lower it to save cycles
871 let mut machine = |cont: &mut bool, (i, c): (usize, char)| -> bool {
872 let whitespace = if c.is_whitespace() {
877 let limit = if (i - slice_start + 1) <= lim {
883 state = match (state, whitespace, limit) {
891 (B, Cr, UnderLim) => B,
892 (B, Cr, OverLim) if (i - last_start + 1) > lim => {
893 panic!("word starting with {} longer than limit!",
894 &ss[last_start..i + 1])
896 (B, Cr, OverLim) => {
897 *cont = it(&ss[slice_start..last_end]);
898 slice_start = last_start;
901 (B, Ws, UnderLim) => {
905 (B, Ws, OverLim) => {
907 *cont = it(&ss[slice_start..last_end]);
911 (C, Cr, UnderLim) => {
915 (C, Cr, OverLim) => {
916 *cont = it(&ss[slice_start..last_end]);
922 (C, Ws, OverLim) => {
923 *cont = it(&ss[slice_start..last_end]);
926 (C, Ws, UnderLim) => C,
932 ss.char_indices().all(|x| machine(&mut cont, x));
934 // Let the automaton 'run out' by supplying trailing whitespace
940 machine(&mut cont, (fake_i, ' '));
947 fn test_split_within() {
948 fn t(s: &str, i: usize, u: &[String]) {
949 let mut v = Vec::new();
950 each_split_within(s, i, |s| {
951 v.push(s.to_string());
954 assert!(v.iter().zip(u).all(|(a, b)| a == b));
958 t("hello", 15, &["hello".to_string()]);
959 t("\nMary had a little lamb\nLittle lamb\n",
961 &["Mary had a".to_string(), "little lamb".to_string(), "Little lamb".to_string()]);
962 t("\nMary had a little lamb\nLittle lamb\n",
964 &["Mary had a little lamb\nLittle lamb".to_string()]);
971 use std::result::Result::{Err, Ok};
977 let long_args = vec!["--test=20".to_string()];
978 let opts = vec![reqopt("t", "test", "testing", "TEST")];
979 let rs = getopts(&long_args, &opts);
982 assert!(m.opt_present("test"));
983 assert_eq!(m.opt_str("test").unwrap(), "20");
984 assert!(m.opt_present("t"));
985 assert_eq!(m.opt_str("t").unwrap(), "20");
988 panic!("test_reqopt failed (long arg)");
991 let short_args = vec!["-t".to_string(), "20".to_string()];
992 match getopts(&short_args, &opts) {
994 assert!((m.opt_present("test")));
995 assert_eq!(m.opt_str("test").unwrap(), "20");
996 assert!((m.opt_present("t")));
997 assert_eq!(m.opt_str("t").unwrap(), "20");
1000 panic!("test_reqopt failed (short arg)");
1006 fn test_reqopt_missing() {
1007 let args = vec!["blah".to_string()];
1008 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1009 let rs = getopts(&args, &opts);
1011 Err(OptionMissing(_)) => {}
1017 fn test_reqopt_no_arg() {
1018 let long_args = vec!["--test".to_string()];
1019 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1020 let rs = getopts(&long_args, &opts);
1022 Err(ArgumentMissing(_)) => {}
1025 let short_args = vec!["-t".to_string()];
1026 match getopts(&short_args, &opts) {
1027 Err(ArgumentMissing(_)) => {}
1033 fn test_reqopt_multi() {
1034 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1035 let opts = vec![reqopt("t", "test", "testing", "TEST")];
1036 let rs = getopts(&args, &opts);
1038 Err(OptionDuplicated(_)) => {}
1046 let long_args = vec!["--test=20".to_string()];
1047 let opts = vec![optopt("t", "test", "testing", "TEST")];
1048 let rs = getopts(&long_args, &opts);
1051 assert!(m.opt_present("test"));
1052 assert_eq!(m.opt_str("test").unwrap(), "20");
1053 assert!((m.opt_present("t")));
1054 assert_eq!(m.opt_str("t").unwrap(), "20");
1058 let short_args = vec!["-t".to_string(), "20".to_string()];
1059 match getopts(&short_args, &opts) {
1061 assert!((m.opt_present("test")));
1062 assert_eq!(m.opt_str("test").unwrap(), "20");
1063 assert!((m.opt_present("t")));
1064 assert_eq!(m.opt_str("t").unwrap(), "20");
1071 fn test_optopt_missing() {
1072 let args = vec!["blah".to_string()];
1073 let opts = vec![optopt("t", "test", "testing", "TEST")];
1074 let rs = getopts(&args, &opts);
1077 assert!(!m.opt_present("test"));
1078 assert!(!m.opt_present("t"));
1085 fn test_optopt_no_arg() {
1086 let long_args = vec!["--test".to_string()];
1087 let opts = vec![optopt("t", "test", "testing", "TEST")];
1088 let rs = getopts(&long_args, &opts);
1090 Err(ArgumentMissing(_)) => {}
1093 let short_args = vec!["-t".to_string()];
1094 match getopts(&short_args, &opts) {
1095 Err(ArgumentMissing(_)) => {}
1101 fn test_optopt_multi() {
1102 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1103 let opts = vec![optopt("t", "test", "testing", "TEST")];
1104 let rs = getopts(&args, &opts);
1106 Err(OptionDuplicated(_)) => {}
1111 // Tests for optflag
1114 let long_args = vec!["--test".to_string()];
1115 let opts = vec![optflag("t", "test", "testing")];
1116 let rs = getopts(&long_args, &opts);
1119 assert!(m.opt_present("test"));
1120 assert!(m.opt_present("t"));
1124 let short_args = vec!["-t".to_string()];
1125 match getopts(&short_args, &opts) {
1127 assert!(m.opt_present("test"));
1128 assert!(m.opt_present("t"));
1135 fn test_optflag_missing() {
1136 let args = vec!["blah".to_string()];
1137 let opts = vec![optflag("t", "test", "testing")];
1138 let rs = getopts(&args, &opts);
1141 assert!(!m.opt_present("test"));
1142 assert!(!m.opt_present("t"));
1149 fn test_optflag_long_arg() {
1150 let args = vec!["--test=20".to_string()];
1151 let opts = vec![optflag("t", "test", "testing")];
1152 let rs = getopts(&args, &opts);
1154 Err(UnexpectedArgument(_)) => {}
1160 fn test_optflag_multi() {
1161 let args = vec!["--test".to_string(), "-t".to_string()];
1162 let opts = vec![optflag("t", "test", "testing")];
1163 let rs = getopts(&args, &opts);
1165 Err(OptionDuplicated(_)) => {}
1171 fn test_optflag_short_arg() {
1172 let args = vec!["-t".to_string(), "20".to_string()];
1173 let opts = vec![optflag("t", "test", "testing")];
1174 let rs = getopts(&args, &opts);
1177 // The next variable after the flag is just a free argument
1179 assert!(m.free[0] == "20");
1185 // Tests for optflagmulti
1187 fn test_optflagmulti_short1() {
1188 let args = vec!["-v".to_string()];
1189 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1190 let rs = getopts(&args, &opts);
1193 assert_eq!(m.opt_count("v"), 1);
1200 fn test_optflagmulti_short2a() {
1201 let args = vec!["-v".to_string(), "-v".to_string()];
1202 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1203 let rs = getopts(&args, &opts);
1206 assert_eq!(m.opt_count("v"), 2);
1213 fn test_optflagmulti_short2b() {
1214 let args = vec!["-vv".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_long1() {
1227 let args = vec!["--verbose".to_string()];
1228 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1229 let rs = getopts(&args, &opts);
1232 assert_eq!(m.opt_count("verbose"), 1);
1239 fn test_optflagmulti_long2() {
1240 let args = vec!["--verbose".to_string(), "--verbose".to_string()];
1241 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1242 let rs = getopts(&args, &opts);
1245 assert_eq!(m.opt_count("verbose"), 2);
1252 fn test_optflagmulti_mix() {
1253 let args = vec!["--verbose".to_string(),
1256 "verbose".to_string()];
1257 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
1258 let rs = getopts(&args, &opts);
1261 assert_eq!(m.opt_count("verbose"), 4);
1262 assert_eq!(m.opt_count("v"), 4);
1268 // Tests for optmulti
1270 fn test_optmulti() {
1271 let long_args = vec!["--test=20".to_string()];
1272 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1273 let rs = getopts(&long_args, &opts);
1276 assert!((m.opt_present("test")));
1277 assert_eq!(m.opt_str("test").unwrap(), "20");
1278 assert!((m.opt_present("t")));
1279 assert_eq!(m.opt_str("t").unwrap(), "20");
1283 let short_args = vec!["-t".to_string(), "20".to_string()];
1284 match getopts(&short_args, &opts) {
1286 assert!((m.opt_present("test")));
1287 assert_eq!(m.opt_str("test").unwrap(), "20");
1288 assert!((m.opt_present("t")));
1289 assert_eq!(m.opt_str("t").unwrap(), "20");
1296 fn test_optmulti_missing() {
1297 let args = vec!["blah".to_string()];
1298 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1299 let rs = getopts(&args, &opts);
1302 assert!(!m.opt_present("test"));
1303 assert!(!m.opt_present("t"));
1310 fn test_optmulti_no_arg() {
1311 let long_args = vec!["--test".to_string()];
1312 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1313 let rs = getopts(&long_args, &opts);
1315 Err(ArgumentMissing(_)) => {}
1318 let short_args = vec!["-t".to_string()];
1319 match getopts(&short_args, &opts) {
1320 Err(ArgumentMissing(_)) => {}
1326 fn test_optmulti_multi() {
1327 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1328 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1329 let rs = getopts(&args, &opts);
1332 assert!(m.opt_present("test"));
1333 assert_eq!(m.opt_str("test").unwrap(), "20");
1334 assert!(m.opt_present("t"));
1335 assert_eq!(m.opt_str("t").unwrap(), "20");
1336 let pair = m.opt_strs("test");
1337 assert!(pair[0] == "20");
1338 assert!(pair[1] == "30");
1345 fn test_unrecognized_option() {
1346 let long_args = vec!["--untest".to_string()];
1347 let opts = vec![optmulti("t", "test", "testing", "TEST")];
1348 let rs = getopts(&long_args, &opts);
1350 Err(UnrecognizedOption(_)) => {}
1353 let short_args = vec!["-u".to_string()];
1354 match getopts(&short_args, &opts) {
1355 Err(UnrecognizedOption(_)) => {}
1361 fn test_combined() {
1362 let args = 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()];
1378 let opts = vec![optopt("s", "something", "something", "SOMETHING"),
1379 optflag("", "flag", "a flag"),
1380 reqopt("", "long", "hi", "LONG"),
1381 optflag("f", "", "another flag"),
1382 optmulti("m", "", "mmmmmm", "YUM"),
1383 optmulti("n", "", "nothing", "NOTHING"),
1384 optopt("", "notpresent", "nothing to see here", "NOPE")];
1385 let rs = getopts(&args, &opts);
1388 assert!(m.free[0] == "prog");
1389 assert!(m.free[1] == "free1");
1390 assert_eq!(m.opt_str("s").unwrap(), "20");
1391 assert!(m.free[2] == "free2");
1392 assert!((m.opt_present("flag")));
1393 assert_eq!(m.opt_str("long").unwrap(), "30");
1394 assert!((m.opt_present("f")));
1395 let pair = m.opt_strs("m");
1396 assert!(pair[0] == "40");
1397 assert!(pair[1] == "50");
1398 let pair = m.opt_strs("n");
1399 assert!(pair[0] == "-A B");
1400 assert!(pair[1] == "-60 70");
1401 assert!((!m.opt_present("notpresent")));
1409 let opts = vec![optopt("e", "", "encrypt", "ENCRYPT"),
1410 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1411 optopt("f", "", "flag", "FLAG")];
1413 let args_single = vec!["-e".to_string(), "foo".to_string()];
1414 let matches_single = &match getopts(&args_single, &opts) {
1415 result::Result::Ok(m) => m,
1416 result::Result::Err(_) => panic!(),
1418 assert!(matches_single.opts_present(&["e".to_string()]));
1419 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1420 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1421 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1422 assert!(!matches_single.opts_present(&["thing".to_string()]));
1423 assert!(!matches_single.opts_present(&[]));
1425 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1426 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1428 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1431 let args_both = vec!["-e".to_string(),
1433 "--encrypt".to_string(),
1435 let matches_both = &match getopts(&args_both, &opts) {
1436 result::Result::Ok(m) => m,
1437 result::Result::Err(_) => panic!(),
1439 assert!(matches_both.opts_present(&["e".to_string()]));
1440 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1441 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1442 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1443 assert!(!matches_both.opts_present(&["f".to_string()]));
1444 assert!(!matches_both.opts_present(&["thing".to_string()]));
1445 assert!(!matches_both.opts_present(&[]));
1447 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1448 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(),
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, &opts) {
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, &opts) {
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 {
1496 name: Name::Short('b'),
1497 hasarg: HasArg::Yes,
1499 aliases: Vec::new(),
1501 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1503 assert!(verbose.long_to_short() == short);
1507 fn test_aliases_long_and_short() {
1508 let opts = vec![optflagmulti("a", "apple", "Desc")];
1510 let args = vec!["-a".to_string(), "--apple".to_string(), "-a".to_string()];
1512 let matches = getopts(&args, &opts).unwrap();
1513 assert_eq!(3, matches.opt_count("a"));
1514 assert_eq!(3, matches.opt_count("apple"));
1519 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1520 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1521 optflag("k", "kiwi", "Desc"),
1522 optflagopt("p", "", "Desc", "VAL"),
1523 optmulti("l", "", "Desc", "VAL")];
1529 -b --banana VAL Desc
1530 -a --012345678901234567890123456789 VAL
1537 let generated_usage = usage("Usage: fruits", &optgroups);
1539 assert_eq!(generated_usage, expected);
1543 fn test_usage_description_wrapping() {
1544 // indentation should be 24 spaces
1545 // lines wrap after 78: or rather descriptions wrap after 54
1547 let optgroups = vec![optflag("k",
1550 "This is a long description which won't be wrapped..+.."),
1553 "This is a long description which _will_ be wrapped..+..")];
1559 -k --kiwi This is a long description which won't be wrapped..+..
1560 -a --apple This is a long description which _will_ be
1564 let usage = usage("Usage: fruits", &optgroups);
1566 assert!(usage == expected)
1570 fn test_usage_description_multibyte_handling() {
1571 let optgroups = vec![optflag("k",
1572 "k\u{2013}w\u{2013}",
1573 "The word kiwi is normally spelled with two i's"),
1576 "This \u{201C}description\u{201D} has some characters that \
1577 could confuse the line wrapping; an apple costs 0.51€ in \
1578 some parts of Europe.")];
1584 -k --k–w– The word kiwi is normally spelled with two i's
1585 -a --apple This “description” has some characters that could
1586 confuse the line wrapping; an apple costs 0.51€ in
1587 some parts of Europe.
1590 let usage = usage("Usage: fruits", &optgroups);
1592 assert!(usage == expected)
1596 fn test_short_usage() {
1597 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1598 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1599 optflag("k", "kiwi", "Desc"),
1600 optflagopt("p", "", "Desc", "VAL"),
1601 optmulti("l", "", "Desc", "VAL")];
1603 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1604 let generated_usage = short_usage("fruits", &optgroups);
1606 assert_eq!(generated_usage, expected);
1610 fn test_args_with_equals() {
1611 let args = vec!["--one".to_string(), "A=B".to_string(),
1612 "--two=C=D".to_string()];
1613 let opts = vec![optopt("o", "one", "One", "INFO"),
1614 optopt("t", "two", "Two", "INFO")];
1615 let matches = &match getopts(&args, &opts) {
1616 result::Result::Ok(m) => m,
1617 result::Result::Err(e) => panic!("{}", e)
1619 assert_eq!(matches.opts_str(&["o".to_string()]).unwrap(), "A=B");
1620 assert_eq!(matches.opts_str(&["t".to_string()]).unwrap(), "C=D");