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};
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 //! println!("Usage: {} [options]", program);
47 //! println!("-o\t\tOutput");
48 //! println!("-h --help\tUsage");
52 //! let args: Vec<String> = os::args().iter()
53 //! .map(|x| x.to_string())
56 //! let program = args.get(0).clone();
59 //! optopt("o", "", "set output file name", "NAME"),
60 //! optflag("h", "help", "print this help menu")
62 //! let matches = match getopts(args.tail(), opts) {
64 //! Err(f) => { fail!(f.to_err_msg()) }
66 //! if matches.opt_present("h") {
67 //! print_usage(program.as_slice(), opts);
70 //! let output = matches.opt_str("o");
71 //! let input = if !matches.free.is_empty() {
72 //! (*matches.free.get(0)).clone()
74 //! print_usage(program.as_slice(), opts);
77 //! do_work(input.as_slice(), output);
81 #![crate_id = "getopts#0.11.0-pre"]
82 #![crate_type = "rlib"]
83 #![crate_type = "dylib"]
84 #![license = "MIT/ASL2"]
85 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
86 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
87 html_root_url = "http://doc.rust-lang.org/")]
88 #![feature(globs, phase)]
90 #![deny(deprecated_owned_vector)]
92 #[cfg(test)] extern crate debug;
93 #[cfg(test)] #[phase(syntax, link)] extern crate log;
96 use std::result::{Err, Ok};
98 use std::string::String;
100 /// Name of an option. Either a string or a single char.
101 #[deriving(Clone, Eq)]
103 /// A string representing the long name of an option.
104 /// For example: "help"
106 /// A char representing the short name of an option.
111 /// Describes whether an option has an argument.
112 #[deriving(Clone, Eq)]
114 /// The option requires an argument.
116 /// The option is just a flag, therefore no argument.
118 /// The option argument is optional and it could or not exist.
122 /// Describes how often an option may occur.
123 #[deriving(Clone, Eq)]
125 /// The option occurs once.
127 /// The option could or not occur.
129 /// The option occurs once or multiple times.
133 /// A description of a possible option.
134 #[deriving(Clone, Eq)]
136 /// Name of the option
138 /// Whether it has an argument
140 /// How often it can occur
142 /// Which options it aliases
143 pub aliases: Vec<Opt> ,
146 /// One group of options, e.g., both -h and --help, along with
147 /// their shared description and properties.
148 #[deriving(Clone, Eq)]
149 pub struct OptGroup {
150 /// Short Name of the `OptGroup`
151 pub short_name: String,
152 /// Long Name of the `OptGroup`
153 pub long_name: String,
158 /// Whether it has an argument
160 /// How often it can occur
164 /// Describes wether an option is given at all or has a value.
165 #[deriving(Clone, Eq)]
171 /// The result of checking command line arguments. Contains a vector
172 /// of matches and a vector of free strings.
173 #[deriving(Clone, Eq)]
175 /// Options that matched
177 /// Values of the Options that matched
178 vals: Vec<Vec<Optval> > ,
179 /// Free string fragments
180 pub free: Vec<String>,
183 /// The type returned when the command line does not conform to the
184 /// expected format. Call the `to_err_msg` method to retrieve the
185 /// error as a string.
186 #[deriving(Clone, Eq, Show)]
188 /// The option requires an argument but none was passed.
189 ArgumentMissing(String),
190 /// The passed option is not declared among the possible options.
191 UnrecognizedOption(String),
192 /// A required option is not present.
193 OptionMissing(String),
194 /// A single occurrence option is being used multiple times.
195 OptionDuplicated(String),
196 /// There's an argument being passed to a non-argument option.
197 UnexpectedArgument(String),
200 /// The type of failure that occurred.
202 #[allow(missing_doc)]
211 /// The result of parsing a command line with a set of options.
212 pub type Result = result::Result<Matches, Fail_>;
215 fn from_str(nm: &str) -> Name {
217 Short(nm.char_at(0u))
223 fn to_str(&self) -> String {
225 Short(ch) => ch.to_str().to_string(),
226 Long(ref s) => s.to_string()
232 /// Translate OptGroup into Opt.
233 /// (Both short and long names correspond to different Opts).
234 pub fn long_to_short(&self) -> Opt {
236 short_name: short_name,
237 long_name: long_name,
243 match (short_name.len(), long_name.len()) {
244 (0,0) => fail!("this long-format option was given no name"),
246 name: Long((long_name)),
252 name: Short(short_name.as_slice().char_at(0)),
258 name: Long((long_name)),
263 name: Short(short_name.as_slice().char_at(0)),
270 (_,_) => fail!("something is wrong with the long-form opt")
276 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
277 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
278 Some(id) => (*self.vals.get(id)).clone(),
279 None => fail!("No option '{}' defined", nm)
283 fn opt_val(&self, nm: &str) -> Option<Optval> {
284 let vals = self.opt_vals(nm);
288 Some((*vals.get(0)).clone())
292 /// Returns true if an option was matched.
293 pub fn opt_present(&self, nm: &str) -> bool {
294 !self.opt_vals(nm).is_empty()
297 /// Returns the number of times an option was matched.
298 pub fn opt_count(&self, nm: &str) -> uint {
299 self.opt_vals(nm).len()
302 /// Returns true if any of several options were matched.
303 pub fn opts_present(&self, names: &[String]) -> bool {
304 for nm in names.iter() {
305 match find_opt(self.opts.as_slice(),
306 Name::from_str(nm.as_slice())) {
307 Some(id) if !self.vals.get(id).is_empty() => return true,
314 /// Returns the string argument supplied to one of several matching options or `None`.
315 pub fn opts_str(&self, names: &[String]) -> Option<String> {
316 for nm in names.iter() {
317 match self.opt_val(nm.as_slice()) {
318 Some(Val(ref s)) => return Some(s.clone()),
325 /// Returns a vector of the arguments provided to all matches of the given
328 /// Used when an option accepts multiple values.
329 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
330 let mut acc: Vec<String> = Vec::new();
331 let r = self.opt_vals(nm);
334 Val(ref s) => acc.push((*s).clone()),
341 /// Returns the string argument supplied to a matching option or `None`.
342 pub fn opt_str(&self, nm: &str) -> Option<String> {
343 let vals = self.opt_vals(nm);
345 return None::<String>;
348 &Val(ref s) => Some((*s).clone()),
354 /// Returns the matching string, a default, or none.
356 /// Returns none if the option was not present, `def` if the option was
357 /// present but no argument was provided, and the argument if the option was
358 /// present and an argument was provided.
359 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
360 let vals = self.opt_vals(nm);
365 &Val(ref s) => Some((*s).clone()),
366 _ => Some(def.to_string())
372 fn is_arg(arg: &str) -> bool {
373 arg.len() > 1 && arg[0] == '-' as u8
376 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
377 // Search main options.
378 let pos = opts.iter().position(|opt| opt.name == nm);
383 // Search in aliases.
384 for candidate in opts.iter() {
385 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
386 return opts.iter().position(|opt| opt.name == candidate.name);
393 /// Create a long option that is required and takes an argument.
394 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
395 let len = short_name.len();
396 assert!(len == 1 || len == 0);
398 short_name: short_name.to_string(),
399 long_name: long_name.to_string(),
400 hint: hint.to_string(),
401 desc: desc.to_string(),
407 /// Create a long option that is optional and takes an argument.
408 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
409 let len = short_name.len();
410 assert!(len == 1 || len == 0);
412 short_name: short_name.to_string(),
413 long_name: long_name.to_string(),
414 hint: hint.to_string(),
415 desc: desc.to_string(),
421 /// Create a long option that is optional and does not take an argument.
422 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
423 let len = short_name.len();
424 assert!(len == 1 || len == 0);
426 short_name: short_name.to_string(),
427 long_name: long_name.to_string(),
428 hint: "".to_string(),
429 desc: desc.to_string(),
435 /// Create a long option that can occur more than once and does not
436 /// take an argument.
437 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
438 let len = short_name.len();
439 assert!(len == 1 || len == 0);
441 short_name: short_name.to_string(),
442 long_name: long_name.to_string(),
443 hint: "".to_string(),
444 desc: desc.to_string(),
450 /// Create a long option that is optional and takes an optional argument.
451 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
452 let len = short_name.len();
453 assert!(len == 1 || len == 0);
455 short_name: short_name.to_string(),
456 long_name: long_name.to_string(),
457 hint: hint.to_string(),
458 desc: desc.to_string(),
464 /// Create a long option that is optional, takes an argument, and may occur
466 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
467 let len = short_name.len();
468 assert!(len == 1 || len == 0);
470 short_name: short_name.to_string(),
471 long_name: long_name.to_string(),
472 hint: hint.to_string(),
473 desc: desc.to_string(),
479 /// Create a generic option group, stating all parameters explicitly
480 pub fn opt(short_name: &str,
485 occur: Occur) -> OptGroup {
486 let len = short_name.len();
487 assert!(len == 1 || len == 0);
489 short_name: short_name.to_string(),
490 long_name: long_name.to_string(),
491 hint: hint.to_string(),
492 desc: desc.to_string(),
499 /// Convert a `Fail_` enum into an error string.
500 pub fn to_err_msg(self) -> String {
502 ArgumentMissing(ref nm) => {
503 format!("Argument to option '{}' missing.", *nm)
505 UnrecognizedOption(ref nm) => {
506 format!("Unrecognized option: '{}'.", *nm)
508 OptionMissing(ref nm) => {
509 format!("Required option '{}' missing.", *nm)
511 OptionDuplicated(ref nm) => {
512 format!("Option '{}' given more than once.", *nm)
514 UnexpectedArgument(ref nm) => {
515 format!("Option '{}' does not take an argument.", *nm)
521 /// Parse command line arguments according to the provided options.
523 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
524 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on failure.
525 /// Use `to_err_msg` to get an error message.
526 pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
527 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
528 let n_opts = opts.len();
530 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
532 let mut vals = Vec::from_fn(n_opts, f);
533 let mut free: Vec<String> = Vec::new();
537 let cur = args[i].clone();
538 let curlen = cur.len();
539 if !is_arg(cur.as_slice()) {
541 } else if cur.as_slice() == "--" {
543 while j < l { free.push(args[j].clone()); j += 1; }
547 let mut i_arg = None;
548 if cur.as_slice()[1] == '-' as u8 {
549 let tail = cur.as_slice().slice(2, curlen);
550 let tail_eq: Vec<&str> = tail.split('=').collect();
551 if tail_eq.len() <= 1 {
552 names = vec!(Long(tail.to_string()));
555 vec!(Long((*tail_eq.get(0)).to_string()));
556 i_arg = Some((*tail_eq.get(1)).to_string());
560 let mut last_valid_opt_id = None;
563 let range = cur.as_slice().char_range_at(j);
564 let opt = Short(range.ch);
566 /* In a series of potential options (eg. -aheJ), if we
567 see one which takes an argument, we assume all
568 subsequent characters make up the argument. This
569 allows options such as -L/usr/local/lib/foo to be
570 interpreted correctly
573 match find_opt(opts.as_slice(), opt.clone()) {
574 Some(id) => last_valid_opt_id = Some(id),
577 last_valid_opt_id.is_some() &&
578 match opts.get(last_valid_opt_id.unwrap())
584 if arg_follows && j < curlen {
585 i_arg = Some(cur.as_slice()
586 .slice(j, curlen).to_string());
589 last_valid_opt_id = None;
597 let mut name_pos = 0;
598 for nm in names.iter() {
600 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
602 None => return Err(UnrecognizedOption(nm.to_str()))
604 match opts.get(optid).hasarg {
606 if !i_arg.is_none() {
607 return Err(UnexpectedArgument(nm.to_str()));
609 vals.get_mut(optid).push(Given);
612 if !i_arg.is_none() {
614 .push(Val((i_arg.clone())
616 } else if name_pos < names.len() || i + 1 == l ||
617 is_arg(args[i + 1].as_slice()) {
618 vals.get_mut(optid).push(Given);
621 vals.get_mut(optid).push(Val(args[i].clone()));
625 if !i_arg.is_none() {
626 vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
627 } else if i + 1 == l {
628 return Err(ArgumentMissing(nm.to_str()));
631 vals.get_mut(optid).push(Val(args[i].clone()));
641 let n = vals.get(i).len();
642 let occ = opts.get(i).occur;
645 return Err(OptionMissing(opts.get(i).name.to_str()));
650 return Err(OptionDuplicated(opts.get(i).name.to_str()));
662 /// Derive a usage message from a set of long options.
663 pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
665 let desc_sep = format!("\n{}", " ".repeat(24));
667 let mut rows = opts.iter().map(|optref| {
668 let OptGroup{short_name: short_name,
669 long_name: long_name,
673 ..} = (*optref).clone();
675 let mut row = " ".repeat(4);
678 match short_name.len() {
682 row.push_str(short_name.as_slice());
685 _ => fail!("the short name should only be 1 ascii char long"),
689 match long_name.len() {
693 row.push_str(long_name.as_slice());
701 Yes => row.push_str(hint.as_slice()),
704 row.push_str(hint.as_slice());
709 // FIXME: #5516 should be graphemes not codepoints
710 // here we just need to indent the start of the description
711 let rowlen = row.as_slice().char_len();
713 for _ in range(0, 24 - rowlen) {
717 row.push_str(desc_sep.as_slice())
720 // Normalize desc to contain words separated by one space character
721 let mut desc_normalized_whitespace = String::new();
722 for word in desc.as_slice().words() {
723 desc_normalized_whitespace.push_str(word);
724 desc_normalized_whitespace.push_char(' ');
727 // FIXME: #5516 should be graphemes not codepoints
728 let mut desc_rows = Vec::new();
729 each_split_within(desc_normalized_whitespace.as_slice(),
732 desc_rows.push(substr.to_string());
736 // FIXME: #5516 should be graphemes not codepoints
737 // wrapped description
738 row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
743 format!("{}\n\nOptions:\n{}\n", brief,
744 rows.collect::<Vec<String>>().connect("\n"))
747 fn format_option(opt: &OptGroup) -> String {
748 let mut line = String::new();
750 if opt.occur != Req {
754 // Use short_name is possible, but fallback to long_name.
755 if opt.short_name.len() > 0 {
757 line.push_str(opt.short_name.as_slice());
760 line.push_str(opt.long_name.as_slice());
763 if opt.hasarg != No {
765 if opt.hasarg == Maybe {
768 line.push_str(opt.hint.as_slice());
769 if opt.hasarg == Maybe {
774 if opt.occur != Req {
777 if opt.occur == Multi {
784 /// Derive a short one-line usage summary from a set of long options.
785 pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
786 let mut line = format!("Usage: {} ", program_name);
787 line.push_str(opts.iter()
789 .collect::<Vec<String>>()
796 /// Splits a string into substrings with possibly internal whitespace,
797 /// each of them at most `lim` bytes long. The substrings have leading and trailing
798 /// whitespace removed, and are only cut at whitespace boundaries.
800 /// Note: Function was moved here from `std::str` because this module is the only place that
801 /// uses it, and because it was to specific for a general string function.
805 /// Fails during iteration if the string contains a non-whitespace
806 /// sequence longer than the limit.
807 fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool)
809 // Just for fun, let's write this as a state machine:
811 enum SplitWithinState {
812 A, // leading whitespace, initial state
814 C, // internal and trailing whitespace
817 Ws, // current char is whitespace
818 Cr // current char is not whitespace
821 UnderLim, // current char makes current substring still fit in limit
822 OverLim // current char makes current substring no longer fit in limit
825 let mut slice_start = 0;
826 let mut last_start = 0;
827 let mut last_end = 0;
829 let mut fake_i = ss.len();
834 // if the limit is larger than the string, lower it to save cycles
839 let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| {
840 let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr };
841 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
843 state = match (state, whitespace, limit) {
845 (A, Cr, _) => { slice_start = i; last_start = i; B }
847 (B, Cr, UnderLim) => { B }
848 (B, Cr, OverLim) if (i - last_start + 1) > lim
849 => fail!("word starting with {} longer than limit!",
850 ss.slice(last_start, i + 1)),
851 (B, Cr, OverLim) => {
852 *cont = it(ss.slice(slice_start, last_end));
853 slice_start = last_start;
856 (B, Ws, UnderLim) => {
860 (B, Ws, OverLim) => {
862 *cont = it(ss.slice(slice_start, last_end));
866 (C, Cr, UnderLim) => {
870 (C, Cr, OverLim) => {
871 *cont = it(ss.slice(slice_start, last_end));
877 (C, Ws, OverLim) => {
878 *cont = it(ss.slice(slice_start, last_end));
881 (C, Ws, UnderLim) => {
889 ss.char_indices().advance(|x| machine(&mut cont, x));
891 // Let the automaton 'run out' by supplying trailing whitespace
892 while cont && match state { B | C => true, A => false } {
893 machine(&mut cont, (fake_i, ' '));
900 fn test_split_within() {
901 fn t(s: &str, i: uint, u: &[String]) {
902 let mut v = Vec::new();
903 each_split_within(s, i, |s| { v.push(s.to_string()); true });
904 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
908 t("hello", 15, ["hello".to_string()]);
909 t("\nMary had a little lamb\nLittle lamb\n", 15, [
910 "Mary had a".to_string(),
911 "little lamb".to_string(),
912 "Little lamb".to_string()
914 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
915 ["Mary had a little lamb\nLittle lamb".to_string()]);
922 use std::result::{Err, Ok};
925 fn check_fail_type(f: Fail_, ft: FailType) {
927 ArgumentMissing(_) => assert!(ft == ArgumentMissing_),
928 UnrecognizedOption(_) => assert!(ft == UnrecognizedOption_),
929 OptionMissing(_) => assert!(ft == OptionMissing_),
930 OptionDuplicated(_) => assert!(ft == OptionDuplicated_),
931 UnexpectedArgument(_) => assert!(ft == UnexpectedArgument_)
938 let long_args = vec!("--test=20".to_string());
939 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
940 let rs = getopts(long_args.as_slice(), opts.as_slice());
943 assert!(m.opt_present("test"));
944 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
945 assert!(m.opt_present("t"));
946 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
948 _ => { fail!("test_reqopt failed (long arg)"); }
950 let short_args = vec!("-t".to_string(), "20".to_string());
951 match getopts(short_args.as_slice(), opts.as_slice()) {
953 assert!((m.opt_present("test")));
954 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
955 assert!((m.opt_present("t")));
956 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
958 _ => { fail!("test_reqopt failed (short arg)"); }
963 fn test_reqopt_missing() {
964 let args = vec!("blah".to_string());
965 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
966 let rs = getopts(args.as_slice(), opts.as_slice());
968 Err(f) => check_fail_type(f, OptionMissing_),
974 fn test_reqopt_no_arg() {
975 let long_args = vec!("--test".to_string());
976 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
977 let rs = getopts(long_args.as_slice(), opts.as_slice());
979 Err(f) => check_fail_type(f, ArgumentMissing_),
982 let short_args = vec!("-t".to_string());
983 match getopts(short_args.as_slice(), opts.as_slice()) {
984 Err(f) => check_fail_type(f, ArgumentMissing_),
990 fn test_reqopt_multi() {
991 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
992 let opts = vec!(reqopt("t", "test", "testing", "TEST"));
993 let rs = getopts(args.as_slice(), opts.as_slice());
995 Err(f) => check_fail_type(f, OptionDuplicated_),
1003 let long_args = vec!("--test=20".to_string());
1004 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1005 let rs = getopts(long_args.as_slice(), opts.as_slice());
1008 assert!(m.opt_present("test"));
1009 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1010 assert!((m.opt_present("t")));
1011 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1015 let short_args = vec!("-t".to_string(), "20".to_string());
1016 match getopts(short_args.as_slice(), opts.as_slice()) {
1018 assert!((m.opt_present("test")));
1019 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1020 assert!((m.opt_present("t")));
1021 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1028 fn test_optopt_missing() {
1029 let args = vec!("blah".to_string());
1030 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1031 let rs = getopts(args.as_slice(), opts.as_slice());
1034 assert!(!m.opt_present("test"));
1035 assert!(!m.opt_present("t"));
1042 fn test_optopt_no_arg() {
1043 let long_args = vec!("--test".to_string());
1044 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1045 let rs = getopts(long_args.as_slice(), opts.as_slice());
1047 Err(f) => check_fail_type(f, ArgumentMissing_),
1050 let short_args = vec!("-t".to_string());
1051 match getopts(short_args.as_slice(), opts.as_slice()) {
1052 Err(f) => check_fail_type(f, ArgumentMissing_),
1058 fn test_optopt_multi() {
1059 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1060 let opts = vec!(optopt("t", "test", "testing", "TEST"));
1061 let rs = getopts(args.as_slice(), opts.as_slice());
1063 Err(f) => check_fail_type(f, OptionDuplicated_),
1068 // Tests for optflag
1071 let long_args = vec!("--test".to_string());
1072 let opts = vec!(optflag("t", "test", "testing"));
1073 let rs = getopts(long_args.as_slice(), opts.as_slice());
1076 assert!(m.opt_present("test"));
1077 assert!(m.opt_present("t"));
1081 let short_args = vec!("-t".to_string());
1082 match getopts(short_args.as_slice(), opts.as_slice()) {
1084 assert!(m.opt_present("test"));
1085 assert!(m.opt_present("t"));
1092 fn test_optflag_missing() {
1093 let args = vec!("blah".to_string());
1094 let opts = vec!(optflag("t", "test", "testing"));
1095 let rs = getopts(args.as_slice(), opts.as_slice());
1098 assert!(!m.opt_present("test"));
1099 assert!(!m.opt_present("t"));
1106 fn test_optflag_long_arg() {
1107 let args = vec!("--test=20".to_string());
1108 let opts = vec!(optflag("t", "test", "testing"));
1109 let rs = getopts(args.as_slice(), opts.as_slice());
1112 error!("{:?}", f.clone().to_err_msg());
1113 check_fail_type(f, UnexpectedArgument_);
1120 fn test_optflag_multi() {
1121 let args = vec!("--test".to_string(), "-t".to_string());
1122 let opts = vec!(optflag("t", "test", "testing"));
1123 let rs = getopts(args.as_slice(), opts.as_slice());
1125 Err(f) => check_fail_type(f, OptionDuplicated_),
1131 fn test_optflag_short_arg() {
1132 let args = vec!("-t".to_string(), "20".to_string());
1133 let opts = vec!(optflag("t", "test", "testing"));
1134 let rs = getopts(args.as_slice(), opts.as_slice());
1137 // The next variable after the flag is just a free argument
1139 assert!(*m.free.get(0) == "20".to_string());
1145 // Tests for optflagmulti
1147 fn test_optflagmulti_short1() {
1148 let args = vec!("-v".to_string());
1149 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1150 let rs = getopts(args.as_slice(), opts.as_slice());
1153 assert_eq!(m.opt_count("v"), 1);
1160 fn test_optflagmulti_short2a() {
1161 let args = vec!("-v".to_string(), "-v".to_string());
1162 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1163 let rs = getopts(args.as_slice(), opts.as_slice());
1166 assert_eq!(m.opt_count("v"), 2);
1173 fn test_optflagmulti_short2b() {
1174 let args = vec!("-vv".to_string());
1175 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1176 let rs = getopts(args.as_slice(), opts.as_slice());
1179 assert_eq!(m.opt_count("v"), 2);
1186 fn test_optflagmulti_long1() {
1187 let args = vec!("--verbose".to_string());
1188 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1189 let rs = getopts(args.as_slice(), opts.as_slice());
1192 assert_eq!(m.opt_count("verbose"), 1);
1199 fn test_optflagmulti_long2() {
1200 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1201 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1202 let rs = getopts(args.as_slice(), opts.as_slice());
1205 assert_eq!(m.opt_count("verbose"), 2);
1212 fn test_optflagmulti_mix() {
1213 let args = vec!("--verbose".to_string(), "-v".to_string(),
1214 "-vv".to_string(), "verbose".to_string());
1215 let opts = vec!(optflagmulti("v", "verbose", "verbosity"));
1216 let rs = getopts(args.as_slice(), opts.as_slice());
1219 assert_eq!(m.opt_count("verbose"), 4);
1220 assert_eq!(m.opt_count("v"), 4);
1226 // Tests for optmulti
1228 fn test_optmulti() {
1229 let long_args = vec!("--test=20".to_string());
1230 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1231 let rs = getopts(long_args.as_slice(), opts.as_slice());
1234 assert!((m.opt_present("test")));
1235 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1236 assert!((m.opt_present("t")));
1237 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1241 let short_args = vec!("-t".to_string(), "20".to_string());
1242 match getopts(short_args.as_slice(), opts.as_slice()) {
1244 assert!((m.opt_present("test")));
1245 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1246 assert!((m.opt_present("t")));
1247 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1254 fn test_optmulti_missing() {
1255 let args = vec!("blah".to_string());
1256 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1257 let rs = getopts(args.as_slice(), opts.as_slice());
1260 assert!(!m.opt_present("test"));
1261 assert!(!m.opt_present("t"));
1268 fn test_optmulti_no_arg() {
1269 let long_args = vec!("--test".to_string());
1270 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1271 let rs = getopts(long_args.as_slice(), opts.as_slice());
1273 Err(f) => check_fail_type(f, ArgumentMissing_),
1276 let short_args = vec!("-t".to_string());
1277 match getopts(short_args.as_slice(), opts.as_slice()) {
1278 Err(f) => check_fail_type(f, ArgumentMissing_),
1284 fn test_optmulti_multi() {
1285 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1286 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1287 let rs = getopts(args.as_slice(), opts.as_slice());
1290 assert!(m.opt_present("test"));
1291 assert_eq!(m.opt_str("test").unwrap(), "20".to_string());
1292 assert!(m.opt_present("t"));
1293 assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
1294 let pair = m.opt_strs("test");
1295 assert!(*pair.get(0) == "20".to_string());
1296 assert!(*pair.get(1) == "30".to_string());
1303 fn test_unrecognized_option() {
1304 let long_args = vec!("--untest".to_string());
1305 let opts = vec!(optmulti("t", "test", "testing", "TEST"));
1306 let rs = getopts(long_args.as_slice(), opts.as_slice());
1308 Err(f) => check_fail_type(f, UnrecognizedOption_),
1311 let short_args = vec!("-u".to_string());
1312 match getopts(short_args.as_slice(), opts.as_slice()) {
1313 Err(f) => check_fail_type(f, UnrecognizedOption_),
1319 fn test_combined() {
1321 vec!("prog".to_string(),
1322 "free1".to_string(),
1325 "free2".to_string(),
1326 "--flag".to_string(),
1327 "--long=30".to_string(),
1336 "-60 70".to_string());
1338 vec!(optopt("s", "something", "something", "SOMETHING"),
1339 optflag("", "flag", "a flag"),
1340 reqopt("", "long", "hi", "LONG"),
1341 optflag("f", "", "another flag"),
1342 optmulti("m", "", "mmmmmm", "YUM"),
1343 optmulti("n", "", "nothing", "NOTHING"),
1344 optopt("", "notpresent", "nothing to see here", "NOPE"));
1345 let rs = getopts(args.as_slice(), opts.as_slice());
1348 assert!(*m.free.get(0) == "prog".to_string());
1349 assert!(*m.free.get(1) == "free1".to_string());
1350 assert_eq!(m.opt_str("s").unwrap(), "20".to_string());
1351 assert!(*m.free.get(2) == "free2".to_string());
1352 assert!((m.opt_present("flag")));
1353 assert_eq!(m.opt_str("long").unwrap(), "30".to_string());
1354 assert!((m.opt_present("f")));
1355 let pair = m.opt_strs("m");
1356 assert!(*pair.get(0) == "40".to_string());
1357 assert!(*pair.get(1) == "50".to_string());
1358 let pair = m.opt_strs("n");
1359 assert!(*pair.get(0) == "-A B".to_string());
1360 assert!(*pair.get(1) == "-60 70".to_string());
1361 assert!((!m.opt_present("notpresent")));
1369 let opts = vec!(optopt("e", "", "encrypt", "ENCRYPT"),
1370 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1371 optopt("f", "", "flag", "FLAG"));
1373 let args_single = vec!("-e".to_string(), "foo".to_string());
1374 let matches_single = &match getopts(args_single.as_slice(),
1377 result::Err(_) => fail!()
1379 assert!(matches_single.opts_present(["e".to_string()]));
1380 assert!(matches_single.opts_present(["encrypt".to_string(), "e".to_string()]));
1381 assert!(matches_single.opts_present(["e".to_string(), "encrypt".to_string()]));
1382 assert!(!matches_single.opts_present(["encrypt".to_string()]));
1383 assert!(!matches_single.opts_present(["thing".to_string()]));
1384 assert!(!matches_single.opts_present([]));
1386 assert_eq!(matches_single.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1387 assert_eq!(matches_single.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1389 assert_eq!(matches_single.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1392 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1394 let matches_both = &match getopts(args_both.as_slice(),
1397 result::Err(_) => fail!()
1399 assert!(matches_both.opts_present(["e".to_string()]));
1400 assert!(matches_both.opts_present(["encrypt".to_string()]));
1401 assert!(matches_both.opts_present(["encrypt".to_string(), "e".to_string()]));
1402 assert!(matches_both.opts_present(["e".to_string(), "encrypt".to_string()]));
1403 assert!(!matches_both.opts_present(["f".to_string()]));
1404 assert!(!matches_both.opts_present(["thing".to_string()]));
1405 assert!(!matches_both.opts_present([]));
1407 assert_eq!(matches_both.opts_str(["e".to_string()]).unwrap(), "foo".to_string());
1408 assert_eq!(matches_both.opts_str(["encrypt".to_string()]).unwrap(), "foo".to_string());
1409 assert_eq!(matches_both.opts_str(["e".to_string(), "encrypt".to_string()]).unwrap(),
1411 assert_eq!(matches_both.opts_str(["encrypt".to_string(), "e".to_string()]).unwrap(),
1417 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1418 let opts = vec!(optmulti("L", "", "library directory", "LIB"),
1419 optmulti("M", "", "something", "MMMM"));
1420 let matches = &match getopts(args.as_slice(), opts.as_slice()) {
1422 result::Err(_) => fail!()
1424 assert!(matches.opts_present(["L".to_string()]));
1425 assert_eq!(matches.opts_str(["L".to_string()]).unwrap(), "foo".to_string());
1426 assert!(matches.opts_present(["M".to_string()]));
1427 assert_eq!(matches.opts_str(["M".to_string()]).unwrap(), ".".to_string());
1432 fn test_long_to_short() {
1433 let mut short = Opt {
1434 name: Long("banana".to_string()),
1437 aliases: Vec::new(),
1439 short.aliases = vec!(Opt { name: Short('b'),
1442 aliases: Vec::new() });
1443 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1445 assert!(verbose.long_to_short() == short);
1449 fn test_aliases_long_and_short() {
1451 optflagmulti("a", "apple", "Desc"));
1453 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1455 let matches = getopts(args.as_slice(), opts.as_slice()).unwrap();
1456 assert_eq!(3, matches.opt_count("a"));
1457 assert_eq!(3, matches.opt_count("apple"));
1462 let optgroups = vec!(
1463 reqopt("b", "banana", "Desc", "VAL"),
1464 optopt("a", "012345678901234567890123456789",
1466 optflag("k", "kiwi", "Desc"),
1467 optflagopt("p", "", "Desc", "VAL"),
1468 optmulti("l", "", "Desc", "VAL"));
1474 -b --banana VAL Desc
1475 -a --012345678901234567890123456789 VAL
1482 let generated_usage = usage("Usage: fruits", optgroups.as_slice());
1484 debug!("expected: <<{}>>", expected);
1485 debug!("generated: <<{}>>", generated_usage);
1486 assert_eq!(generated_usage, expected);
1490 fn test_usage_description_wrapping() {
1491 // indentation should be 24 spaces
1492 // lines wrap after 78: or rather descriptions wrap after 54
1494 let optgroups = vec!(
1495 optflag("k", "kiwi",
1496 "This is a long description which won't be wrapped..+.."), // 54
1497 optflag("a", "apple",
1498 "This is a long description which _will_ be wrapped..+.."));
1504 -k --kiwi This is a long description which won't be wrapped..+..
1505 -a --apple This is a long description which _will_ be
1509 let usage = usage("Usage: fruits", optgroups.as_slice());
1511 debug!("expected: <<{}>>", expected);
1512 debug!("generated: <<{}>>", usage);
1513 assert!(usage == expected)
1517 fn test_usage_description_multibyte_handling() {
1518 let optgroups = vec!(
1519 optflag("k", "k\u2013w\u2013",
1520 "The word kiwi is normally spelled with two i's"),
1521 optflag("a", "apple",
1522 "This \u201Cdescription\u201D has some characters that could \
1523 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe."));
1529 -k --k–w– The word kiwi is normally spelled with two i's
1530 -a --apple This “description” has some characters that could
1531 confuse the line wrapping; an apple costs 0.51€ in
1532 some parts of Europe.
1535 let usage = usage("Usage: fruits", optgroups.as_slice());
1537 debug!("expected: <<{}>>", expected);
1538 debug!("generated: <<{}>>", usage);
1539 assert!(usage == expected)
1543 fn test_short_usage() {
1544 let optgroups = vec!(
1545 reqopt("b", "banana", "Desc", "VAL"),
1546 optopt("a", "012345678901234567890123456789",
1548 optflag("k", "kiwi", "Desc"),
1549 optflagopt("p", "", "Desc", "VAL"),
1550 optmulti("l", "", "Desc", "VAL"));
1552 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1553 let generated_usage = short_usage("fruits", optgroups.as_slice());
1555 debug!("expected: <<{}>>", expected);
1556 debug!("generated: <<{}>>", generated_usage);
1557 assert_eq!(generated_usage, expected);