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<StrBuf>) {
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<StrBuf> = os::args().iter()
53 //! .map(|x| x.to_strbuf())
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://static.rust-lang.org/doc/master")]
88 #![feature(globs, phase)]
90 #![deny(deprecated_owned_vector)]
92 #[cfg(test)] #[phase(syntax, link)] extern crate log;
95 use std::result::{Err, Ok};
97 use std::strbuf::StrBuf;
99 /// Name of an option. Either a string or a single char.
100 #[deriving(Clone, Eq)]
102 /// A string representing the long name of an option.
103 /// For example: "help"
105 /// A char representing the short name of an option.
110 /// Describes whether an option has an argument.
111 #[deriving(Clone, Eq)]
113 /// The option requires an argument.
115 /// The option is just a flag, therefore no argument.
117 /// The option argument is optional and it could or not exist.
121 /// Describes how often an option may occur.
122 #[deriving(Clone, Eq)]
124 /// The option occurs once.
126 /// The option could or not occur.
128 /// The option occurs once or multiple times.
132 /// A description of a possible option.
133 #[deriving(Clone, Eq)]
135 /// Name of the option
137 /// Whether it has an argument
139 /// How often it can occur
141 /// Which options it aliases
142 pub aliases: Vec<Opt> ,
145 /// One group of options, e.g., both -h and --help, along with
146 /// their shared description and properties.
147 #[deriving(Clone, Eq)]
148 pub struct OptGroup {
149 /// Short Name of the `OptGroup`
150 pub short_name: StrBuf,
151 /// Long Name of the `OptGroup`
152 pub long_name: StrBuf,
157 /// Whether it has an argument
159 /// How often it can occur
163 /// Describes wether an option is given at all or has a value.
164 #[deriving(Clone, Eq)]
170 /// The result of checking command line arguments. Contains a vector
171 /// of matches and a vector of free strings.
172 #[deriving(Clone, Eq)]
174 /// Options that matched
176 /// Values of the Options that matched
177 vals: Vec<Vec<Optval> > ,
178 /// Free string fragments
179 pub free: Vec<StrBuf>,
182 /// The type returned when the command line does not conform to the
183 /// expected format. Call the `to_err_msg` method to retrieve the
184 /// error as a string.
185 #[deriving(Clone, Eq, Show)]
187 /// The option requires an argument but none was passed.
188 ArgumentMissing(StrBuf),
189 /// The passed option is not declared among the possible options.
190 UnrecognizedOption(StrBuf),
191 /// A required option is not present.
192 OptionMissing(StrBuf),
193 /// A single occurence option is being used multiple times.
194 OptionDuplicated(StrBuf),
195 /// There's an argument being passed to a non-argument option.
196 UnexpectedArgument(StrBuf),
199 /// The type of failure that occurred.
201 #[allow(missing_doc)]
210 /// The result of parsing a command line with a set of options.
211 pub type Result = result::Result<Matches, Fail_>;
214 fn from_str(nm: &str) -> Name {
216 Short(nm.char_at(0u))
222 fn to_str(&self) -> StrBuf {
224 Short(ch) => ch.to_str().to_strbuf(),
225 Long(ref s) => s.to_strbuf()
231 /// Translate OptGroup into Opt.
232 /// (Both short and long names correspond to different Opts).
233 pub fn long_to_short(&self) -> Opt {
235 short_name: short_name,
236 long_name: long_name,
242 match (short_name.len(), long_name.len()) {
243 (0,0) => fail!("this long-format option was given no name"),
245 name: Long((long_name)),
251 name: Short(short_name.as_slice().char_at(0)),
257 name: Long((long_name)),
262 name: Short(short_name.as_slice().char_at(0)),
269 (_,_) => fail!("something is wrong with the long-form opt")
275 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
276 match find_opt(self.opts.as_slice(), Name::from_str(nm)) {
277 Some(id) => (*self.vals.get(id)).clone(),
278 None => fail!("No option '{}' defined", nm)
282 fn opt_val(&self, nm: &str) -> Option<Optval> {
283 let vals = self.opt_vals(nm);
287 Some((*vals.get(0)).clone())
291 /// Returns true if an option was matched.
292 pub fn opt_present(&self, nm: &str) -> bool {
293 !self.opt_vals(nm).is_empty()
296 /// Returns the number of times an option was matched.
297 pub fn opt_count(&self, nm: &str) -> uint {
298 self.opt_vals(nm).len()
301 /// Returns true if any of several options were matched.
302 pub fn opts_present(&self, names: &[StrBuf]) -> bool {
303 for nm in names.iter() {
304 match find_opt(self.opts.as_slice(),
305 Name::from_str(nm.as_slice())) {
306 Some(id) if !self.vals.get(id).is_empty() => return true,
313 /// Returns the string argument supplied to one of several matching options or `None`.
314 pub fn opts_str(&self, names: &[StrBuf]) -> Option<StrBuf> {
315 for nm in names.iter() {
316 match self.opt_val(nm.as_slice()) {
317 Some(Val(ref s)) => return Some(s.clone()),
324 /// Returns a vector of the arguments provided to all matches of the given
327 /// Used when an option accepts multiple values.
328 pub fn opt_strs(&self, nm: &str) -> Vec<StrBuf> {
329 let mut acc: Vec<StrBuf> = Vec::new();
330 let r = self.opt_vals(nm);
333 Val(ref s) => acc.push((*s).clone()),
340 /// Returns the string argument supplied to a matching option or `None`.
341 pub fn opt_str(&self, nm: &str) -> Option<StrBuf> {
342 let vals = self.opt_vals(nm);
344 return None::<StrBuf>;
347 &Val(ref s) => Some((*s).clone()),
353 /// Returns the matching string, a default, or none.
355 /// Returns none if the option was not present, `def` if the option was
356 /// present but no argument was provided, and the argument if the option was
357 /// present and an argument was provided.
358 pub fn opt_default(&self, nm: &str, def: &str) -> Option<StrBuf> {
359 let vals = self.opt_vals(nm);
364 &Val(ref s) => Some((*s).clone()),
365 _ => Some(def.to_strbuf())
371 fn is_arg(arg: &str) -> bool {
372 arg.len() > 1 && arg[0] == '-' as u8
375 fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> {
376 // Search main options.
377 let pos = opts.iter().position(|opt| opt.name == nm);
382 // Search in aliases.
383 for candidate in opts.iter() {
384 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
385 return opts.iter().position(|opt| opt.name == candidate.name);
392 /// Create a long option that is required and takes an argument.
393 pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
394 let len = short_name.len();
395 assert!(len == 1 || len == 0);
397 short_name: short_name.to_strbuf(),
398 long_name: long_name.to_strbuf(),
399 hint: hint.to_strbuf(),
400 desc: desc.to_strbuf(),
406 /// Create a long option that is optional and takes an argument.
407 pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
408 let len = short_name.len();
409 assert!(len == 1 || len == 0);
411 short_name: short_name.to_strbuf(),
412 long_name: long_name.to_strbuf(),
413 hint: hint.to_strbuf(),
414 desc: desc.to_strbuf(),
420 /// Create a long option that is optional and does not take an argument.
421 pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
422 let len = short_name.len();
423 assert!(len == 1 || len == 0);
425 short_name: short_name.to_strbuf(),
426 long_name: long_name.to_strbuf(),
427 hint: "".to_strbuf(),
428 desc: desc.to_strbuf(),
434 /// Create a long option that can occur more than once and does not
435 /// take an argument.
436 pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
437 let len = short_name.len();
438 assert!(len == 1 || len == 0);
440 short_name: short_name.to_strbuf(),
441 long_name: long_name.to_strbuf(),
442 hint: "".to_strbuf(),
443 desc: desc.to_strbuf(),
449 /// Create a long option that is optional and takes an optional argument.
450 pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
451 let len = short_name.len();
452 assert!(len == 1 || len == 0);
454 short_name: short_name.to_strbuf(),
455 long_name: long_name.to_strbuf(),
456 hint: hint.to_strbuf(),
457 desc: desc.to_strbuf(),
463 /// Create a long option that is optional, takes an argument, and may occur
465 pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
466 let len = short_name.len();
467 assert!(len == 1 || len == 0);
469 short_name: short_name.to_strbuf(),
470 long_name: long_name.to_strbuf(),
471 hint: hint.to_strbuf(),
472 desc: desc.to_strbuf(),
478 /// Create a generic option group, stating all parameters explicitly
479 pub fn opt(short_name: &str,
484 occur: Occur) -> OptGroup {
485 let len = short_name.len();
486 assert!(len == 1 || len == 0);
488 short_name: short_name.to_strbuf(),
489 long_name: long_name.to_strbuf(),
490 hint: hint.to_strbuf(),
491 desc: desc.to_strbuf(),
498 /// Convert a `Fail_` enum into an error string.
499 pub fn to_err_msg(self) -> StrBuf {
501 ArgumentMissing(ref nm) => {
502 format_strbuf!("Argument to option '{}' missing.", *nm)
504 UnrecognizedOption(ref nm) => {
505 format_strbuf!("Unrecognized option: '{}'.", *nm)
507 OptionMissing(ref nm) => {
508 format_strbuf!("Required option '{}' missing.", *nm)
510 OptionDuplicated(ref nm) => {
511 format_strbuf!("Option '{}' given more than once.", *nm)
513 UnexpectedArgument(ref nm) => {
514 format_strbuf!("Option '{}' does not take an argument.", *nm)
520 /// Parse command line arguments according to the provided options.
522 /// On success returns `Ok(Opt)`. Use methods such as `opt_present`
523 /// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on failure.
524 /// Use `to_err_msg` to get an error message.
525 pub fn getopts(args: &[StrBuf], optgrps: &[OptGroup]) -> Result {
526 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
527 let n_opts = opts.len();
529 fn f(_x: uint) -> Vec<Optval> { return Vec::new(); }
531 let mut vals = Vec::from_fn(n_opts, f);
532 let mut free: Vec<StrBuf> = Vec::new();
536 let cur = args[i].clone();
537 let curlen = cur.len();
538 if !is_arg(cur.as_slice()) {
540 } else if cur.as_slice() == "--" {
542 while j < l { free.push(args[j].clone()); j += 1; }
546 let mut i_arg = None;
547 if cur.as_slice()[1] == '-' as u8 {
548 let tail = cur.as_slice().slice(2, curlen);
549 let tail_eq: Vec<&str> = tail.split('=').collect();
550 if tail_eq.len() <= 1 {
551 names = vec!(Long(tail.to_strbuf()));
554 vec!(Long((*tail_eq.get(0)).to_strbuf()));
555 i_arg = Some((*tail_eq.get(1)).to_strbuf());
559 let mut last_valid_opt_id = None;
562 let range = cur.as_slice().char_range_at(j);
563 let opt = Short(range.ch);
565 /* In a series of potential options (eg. -aheJ), if we
566 see one which takes an argument, we assume all
567 subsequent characters make up the argument. This
568 allows options such as -L/usr/local/lib/foo to be
569 interpreted correctly
572 match find_opt(opts.as_slice(), opt.clone()) {
573 Some(id) => last_valid_opt_id = Some(id),
576 last_valid_opt_id.is_some() &&
577 match opts.get(last_valid_opt_id.unwrap())
583 if arg_follows && j < curlen {
584 i_arg = Some(cur.as_slice()
585 .slice(j, curlen).to_strbuf());
588 last_valid_opt_id = None;
596 let mut name_pos = 0;
597 for nm in names.iter() {
599 let optid = match find_opt(opts.as_slice(), (*nm).clone()) {
601 None => return Err(UnrecognizedOption(nm.to_str()))
603 match opts.get(optid).hasarg {
605 if !i_arg.is_none() {
606 return Err(UnexpectedArgument(nm.to_str()));
608 vals.get_mut(optid).push(Given);
611 if !i_arg.is_none() {
613 .push(Val((i_arg.clone())
615 } else if name_pos < names.len() || i + 1 == l ||
616 is_arg(args[i + 1].as_slice()) {
617 vals.get_mut(optid).push(Given);
620 vals.get_mut(optid).push(Val(args[i].clone()));
624 if !i_arg.is_none() {
625 vals.get_mut(optid).push(Val(i_arg.clone().unwrap()));
626 } else if i + 1 == l {
627 return Err(ArgumentMissing(nm.to_str()));
630 vals.get_mut(optid).push(Val(args[i].clone()));
640 let n = vals.get(i).len();
641 let occ = opts.get(i).occur;
644 return Err(OptionMissing(opts.get(i).name.to_str()));
649 return Err(OptionDuplicated(opts.get(i).name.to_str()));
661 /// Derive a usage message from a set of long options.
662 pub fn usage(brief: &str, opts: &[OptGroup]) -> StrBuf {
664 let desc_sep = format!("\n{}", " ".repeat(24));
666 let mut rows = opts.iter().map(|optref| {
667 let OptGroup{short_name: short_name,
668 long_name: long_name,
672 ..} = (*optref).clone();
674 let mut row = StrBuf::from_owned_str(" ".repeat(4));
677 match short_name.len() {
681 row.push_str(short_name.as_slice());
684 _ => fail!("the short name should only be 1 ascii char long"),
688 match long_name.len() {
692 row.push_str(long_name.as_slice());
700 Yes => row.push_str(hint.as_slice()),
703 row.push_str(hint.as_slice());
708 // FIXME: #5516 should be graphemes not codepoints
709 // here we just need to indent the start of the description
710 let rowlen = row.as_slice().char_len();
712 for _ in range(0, 24 - rowlen) {
716 row.push_str(desc_sep.as_slice())
719 // Normalize desc to contain words separated by one space character
720 let mut desc_normalized_whitespace = StrBuf::new();
721 for word in desc.as_slice().words() {
722 desc_normalized_whitespace.push_str(word);
723 desc_normalized_whitespace.push_char(' ');
726 // FIXME: #5516 should be graphemes not codepoints
727 let mut desc_rows = Vec::new();
728 each_split_within(desc_normalized_whitespace.as_slice(),
731 desc_rows.push(substr.to_owned());
735 // FIXME: #5516 should be graphemes not codepoints
736 // wrapped description
737 row.push_str(desc_rows.connect(desc_sep.as_slice()).as_slice());
742 format_strbuf!("{}\n\nOptions:\n{}\n",
744 rows.collect::<Vec<StrBuf>>().connect("\n"))
747 fn format_option(opt: &OptGroup) -> StrBuf {
748 let mut line = StrBuf::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]) -> StrBuf {
786 let mut line = format_strbuf!("Usage: {} ", program_name);
787 line.push_str(opts.iter()
789 .collect::<Vec<StrBuf>>()
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: &[StrBuf]) {
902 let mut v = Vec::new();
903 each_split_within(s, i, |s| { v.push(s.to_strbuf()); true });
904 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
908 t("hello", 15, ["hello".to_strbuf()]);
909 t("\nMary had a little lamb\nLittle lamb\n", 15, [
910 "Mary had a".to_strbuf(),
911 "little lamb".to_strbuf(),
912 "Little lamb".to_strbuf()
914 t("\nMary had a little lamb\nLittle lamb\n", ::std::uint::MAX,
915 ["Mary had a little lamb\nLittle lamb".to_strbuf()]);
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_strbuf());
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_strbuf());
945 assert!(m.opt_present("t"));
946 assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
948 _ => { fail!("test_reqopt failed (long arg)"); }
950 let short_args = vec!("-t".to_strbuf(), "20".to_strbuf());
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_strbuf());
955 assert!((m.opt_present("t")));
956 assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
958 _ => { fail!("test_reqopt failed (short arg)"); }
963 fn test_reqopt_missing() {
964 let args = vec!("blah".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf(), "30".to_strbuf());
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_strbuf());
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_strbuf());
1010 assert!((m.opt_present("t")));
1011 assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1015 let short_args = vec!("-t".to_strbuf(), "20".to_strbuf());
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_strbuf());
1020 assert!((m.opt_present("t")));
1021 assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1028 fn test_optopt_missing() {
1029 let args = vec!("blah".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf(), "30".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf());
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_strbuf(), "20".to_strbuf());
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_strbuf());
1145 // Tests for optflagmulti
1147 fn test_optflagmulti_short1() {
1148 let args = vec!("-v".to_strbuf());
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_strbuf(), "-v".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "--verbose".to_strbuf());
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_strbuf(), "-v".to_strbuf(),
1214 "-vv".to_strbuf(), "verbose".to_strbuf());
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_strbuf());
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_strbuf());
1236 assert!((m.opt_present("t")));
1237 assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1241 let short_args = vec!("-t".to_strbuf(), "20".to_strbuf());
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_strbuf());
1246 assert!((m.opt_present("t")));
1247 assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1254 fn test_optmulti_missing() {
1255 let args = vec!("blah".to_strbuf());
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_strbuf());
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_strbuf());
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_strbuf(), "-t".to_strbuf(), "30".to_strbuf());
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_strbuf());
1292 assert!(m.opt_present("t"));
1293 assert_eq!(m.opt_str("t").unwrap(), "20".to_strbuf());
1294 let pair = m.opt_strs("test");
1295 assert!(*pair.get(0) == "20".to_strbuf());
1296 assert!(*pair.get(1) == "30".to_strbuf());
1303 fn test_unrecognized_option() {
1304 let long_args = vec!("--untest".to_strbuf());
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_strbuf());
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_strbuf(),
1322 "free1".to_strbuf(),
1325 "free2".to_strbuf(),
1326 "--flag".to_strbuf(),
1327 "--long=30".to_strbuf(),
1336 "-60 70".to_strbuf());
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_strbuf());
1349 assert!(*m.free.get(1) == "free1".to_strbuf());
1350 assert_eq!(m.opt_str("s").unwrap(), "20".to_strbuf());
1351 assert!(*m.free.get(2) == "free2".to_strbuf());
1352 assert!((m.opt_present("flag")));
1353 assert_eq!(m.opt_str("long").unwrap(), "30".to_strbuf());
1354 assert!((m.opt_present("f")));
1355 let pair = m.opt_strs("m");
1356 assert!(*pair.get(0) == "40".to_strbuf());
1357 assert!(*pair.get(1) == "50".to_strbuf());
1358 let pair = m.opt_strs("n");
1359 assert!(*pair.get(0) == "-A B".to_strbuf());
1360 assert!(*pair.get(1) == "-60 70".to_strbuf());
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_strbuf(), "foo".to_strbuf());
1374 let matches_single = &match getopts(args_single.as_slice(),
1377 result::Err(_) => fail!()
1379 assert!(matches_single.opts_present(["e".to_strbuf()]));
1380 assert!(matches_single.opts_present(["encrypt".to_strbuf(), "e".to_strbuf()]));
1381 assert!(matches_single.opts_present(["e".to_strbuf(), "encrypt".to_strbuf()]));
1382 assert!(!matches_single.opts_present(["encrypt".to_strbuf()]));
1383 assert!(!matches_single.opts_present(["thing".to_strbuf()]));
1384 assert!(!matches_single.opts_present([]));
1386 assert_eq!(matches_single.opts_str(["e".to_strbuf()]).unwrap(), "foo".to_strbuf());
1387 assert_eq!(matches_single.opts_str(["e".to_strbuf(), "encrypt".to_strbuf()]).unwrap(),
1389 assert_eq!(matches_single.opts_str(["encrypt".to_strbuf(), "e".to_strbuf()]).unwrap(),
1392 let args_both = vec!("-e".to_strbuf(), "foo".to_strbuf(), "--encrypt".to_strbuf(),
1394 let matches_both = &match getopts(args_both.as_slice(),
1397 result::Err(_) => fail!()
1399 assert!(matches_both.opts_present(["e".to_strbuf()]));
1400 assert!(matches_both.opts_present(["encrypt".to_strbuf()]));
1401 assert!(matches_both.opts_present(["encrypt".to_strbuf(), "e".to_strbuf()]));
1402 assert!(matches_both.opts_present(["e".to_strbuf(), "encrypt".to_strbuf()]));
1403 assert!(!matches_both.opts_present(["f".to_strbuf()]));
1404 assert!(!matches_both.opts_present(["thing".to_strbuf()]));
1405 assert!(!matches_both.opts_present([]));
1407 assert_eq!(matches_both.opts_str(["e".to_strbuf()]).unwrap(), "foo".to_strbuf());
1408 assert_eq!(matches_both.opts_str(["encrypt".to_strbuf()]).unwrap(), "foo".to_strbuf());
1409 assert_eq!(matches_both.opts_str(["e".to_strbuf(), "encrypt".to_strbuf()]).unwrap(),
1411 assert_eq!(matches_both.opts_str(["encrypt".to_strbuf(), "e".to_strbuf()]).unwrap(),
1417 let args = vec!("-Lfoo".to_strbuf(), "-M.".to_strbuf());
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_strbuf()]));
1425 assert_eq!(matches.opts_str(["L".to_strbuf()]).unwrap(), "foo".to_strbuf());
1426 assert!(matches.opts_present(["M".to_strbuf()]));
1427 assert_eq!(matches.opts_str(["M".to_strbuf()]).unwrap(), ".".to_strbuf());
1432 fn test_long_to_short() {
1433 let mut short = Opt {
1434 name: Long("banana".to_strbuf()),
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_strbuf(), "--apple".to_strbuf(), "-a".to_strbuf());
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_strbuf();
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);