]> git.proxmox.com Git - rustc.git/blob - src/vendor/getopts/src/lib.rs
New upstream version 1.22.1+dfsg1
[rustc.git] / src / vendor / getopts / src / lib.rs
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.
4 //
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.
10 //
11 // ignore-lexer-test FIXME #15677
12
13 //! Simple getopt alternative.
14 //!
15 //! Construct a vector of options, either by using `reqopt`, `optopt`, and
16 //! `optflag` or by building them from components yourself, and pass them to
17 //! `getopts`, along with a vector of actual arguments (not including
18 //! `argv[0]`). You'll either get a failure code back, or a match. You'll have
19 //! to verify whether the amount of 'free' arguments in the match is what you
20 //! expect. Use `opt_*` accessors to get argument values out of the matches
21 //! object.
22 //!
23 //! Single-character options are expected to appear on the command line with a
24 //! single preceding dash; multiple-character options are expected to be
25 //! proceeded by two dashes. Options that expect an argument accept their
26 //! argument following either a space or an equals sign. Single-character
27 //! options don't require the space.
28 //!
29 //! # Usage
30 //!
31 //! This crate is [on crates.io](https://crates.io/crates/getopts) and can be
32 //! used by adding `getopts` to the dependencies in your project's `Cargo.toml`.
33 //!
34 //! ```toml
35 //! [dependencies]
36 //! getopts = "0.2"
37 //! ```
38 //!
39 //! and this to your crate root:
40 //!
41 //! ```rust
42 //! extern crate getopts;
43 //! ```
44 //!
45 //! # Example
46 //!
47 //! The following example shows simple command line parsing for an application
48 //! that requires an input file to be specified, accepts an optional output file
49 //! name following `-o`, and accepts both `-h` and `--help` as optional flags.
50 //!
51 //! ```{.rust}
52 //! extern crate getopts;
53 //! use getopts::Options;
54 //! use std::env;
55 //!
56 //! fn do_work(inp: &str, out: Option<String>) {
57 //! println!("{}", inp);
58 //! match out {
59 //! Some(x) => println!("{}", x),
60 //! None => println!("No Output"),
61 //! }
62 //! }
63 //!
64 //! fn print_usage(program: &str, opts: Options) {
65 //! let brief = format!("Usage: {} FILE [options]", program);
66 //! print!("{}", opts.usage(&brief));
67 //! }
68 //!
69 //! fn main() {
70 //! let args: Vec<String> = env::args().collect();
71 //! let program = args[0].clone();
72 //!
73 //! let mut opts = Options::new();
74 //! opts.optopt("o", "", "set output file name", "NAME");
75 //! opts.optflag("h", "help", "print this help menu");
76 //! let matches = match opts.parse(&args[1..]) {
77 //! Ok(m) => { m }
78 //! Err(f) => { panic!(f.to_string()) }
79 //! };
80 //! if matches.opt_present("h") {
81 //! print_usage(&program, opts);
82 //! return;
83 //! }
84 //! let output = matches.opt_str("o");
85 //! let input = if !matches.free.is_empty() {
86 //! matches.free[0].clone()
87 //! } else {
88 //! print_usage(&program, opts);
89 //! return;
90 //! };
91 //! do_work(&input, output);
92 //! }
93 //! ```
94
95 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
96 html_favicon_url = "https://www.rust-lang.org/favicon.ico",
97 html_root_url = "https://doc.rust-lang.org/getopts/")]
98 #![deny(missing_docs)]
99 #![cfg_attr(test, deny(warnings))]
100 #![cfg_attr(rust_build, feature(staged_api))]
101 #![cfg_attr(rust_build, staged_api)]
102 #![cfg_attr(rust_build,
103 unstable(feature = "rustc_private",
104 reason = "use the crates.io `getopts` library instead"))]
105
106 #[cfg(test)] #[macro_use] extern crate log;
107
108 use self::Name::*;
109 use self::HasArg::*;
110 use self::Occur::*;
111 use self::Fail::*;
112 use self::Optval::*;
113 use self::SplitWithinState::*;
114 use self::Whitespace::*;
115 use self::LengthLimit::*;
116
117 use std::error::Error;
118 use std::ffi::OsStr;
119 use std::fmt;
120 use std::iter::{repeat, IntoIterator};
121 use std::result;
122
123 /// A description of the options that a program can handle.
124 pub struct Options {
125 grps: Vec<OptGroup>,
126 parsing_style : ParsingStyle,
127 long_only: bool
128 }
129
130 impl Options {
131 /// Create a blank set of options.
132 pub fn new() -> Options {
133 Options {
134 grps: Vec::new(),
135 parsing_style: ParsingStyle::FloatingFrees,
136 long_only: false
137 }
138 }
139
140 /// Set the parsing style.
141 pub fn parsing_style(&mut self, style: ParsingStyle) -> &mut Options {
142 self.parsing_style = style;
143 self
144 }
145
146 /// Set or clear "long options only" mode.
147 ///
148 /// In "long options only" mode, short options cannot be clustered
149 /// together, and long options can be given with either a single
150 /// "-" or the customary "--". This mode also changes the meaning
151 /// of "-a=b"; in the ordinary mode this will parse a short option
152 /// "-a" with argument "=b"; whereas in long-options-only mode the
153 /// argument will be simply "b".
154 pub fn long_only(&mut self, long_only: bool) -> &mut Options {
155 self.long_only = long_only;
156 self
157 }
158
159 /// Create a generic option group, stating all parameters explicitly.
160 pub fn opt(&mut self, short_name: &str, long_name: &str, desc: &str,
161 hint: &str, hasarg: HasArg, occur: Occur) -> &mut Options {
162 validate_names(short_name, long_name);
163 self.grps.push(OptGroup {
164 short_name: short_name.to_string(),
165 long_name: long_name.to_string(),
166 hint: hint.to_string(),
167 desc: desc.to_string(),
168 hasarg: hasarg,
169 occur: occur
170 });
171 self
172 }
173
174 /// Create a long option that is optional and does not take an argument.
175 ///
176 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
177 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
178 /// * `desc` - Description for usage help
179 pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str)
180 -> &mut Options {
181 validate_names(short_name, long_name);
182 self.grps.push(OptGroup {
183 short_name: short_name.to_string(),
184 long_name: long_name.to_string(),
185 hint: "".to_string(),
186 desc: desc.to_string(),
187 hasarg: No,
188 occur: Optional
189 });
190 self
191 }
192
193 /// Create a long option that can occur more than once and does not
194 /// take an argument.
195 ///
196 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
197 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
198 /// * `desc` - Description for usage help
199 pub fn optflagmulti(&mut self, short_name: &str, long_name: &str, desc: &str)
200 -> &mut Options {
201 validate_names(short_name, long_name);
202 self.grps.push(OptGroup {
203 short_name: short_name.to_string(),
204 long_name: long_name.to_string(),
205 hint: "".to_string(),
206 desc: desc.to_string(),
207 hasarg: No,
208 occur: Multi
209 });
210 self
211 }
212
213 /// Create a long option that is optional and takes an optional argument.
214 ///
215 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
216 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
217 /// * `desc` - Description for usage help
218 /// * `hint` - Hint that is used in place of the argument in the usage help,
219 /// e.g. `"FILE"` for a `-o FILE` option
220 pub fn optflagopt(&mut self, short_name: &str, long_name: &str, desc: &str,
221 hint: &str) -> &mut Options {
222 validate_names(short_name, long_name);
223 self.grps.push(OptGroup {
224 short_name: short_name.to_string(),
225 long_name: long_name.to_string(),
226 hint: hint.to_string(),
227 desc: desc.to_string(),
228 hasarg: Maybe,
229 occur: Optional
230 });
231 self
232 }
233
234 /// Create a long option that is optional, takes an argument, and may occur
235 /// multiple times.
236 ///
237 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
238 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
239 /// * `desc` - Description for usage help
240 /// * `hint` - Hint that is used in place of the argument in the usage help,
241 /// e.g. `"FILE"` for a `-o FILE` option
242 pub fn optmulti(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str)
243 -> &mut Options {
244 validate_names(short_name, long_name);
245 self.grps.push(OptGroup {
246 short_name: short_name.to_string(),
247 long_name: long_name.to_string(),
248 hint: hint.to_string(),
249 desc: desc.to_string(),
250 hasarg: Yes,
251 occur: Multi
252 });
253 self
254 }
255
256 /// Create a long option that is optional and takes an argument.
257 ///
258 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
259 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
260 /// * `desc` - Description for usage help
261 /// * `hint` - Hint that is used in place of the argument in the usage help,
262 /// e.g. `"FILE"` for a `-o FILE` option
263 pub fn optopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str)
264 -> &mut Options {
265 validate_names(short_name, long_name);
266 self.grps.push(OptGroup {
267 short_name: short_name.to_string(),
268 long_name: long_name.to_string(),
269 hint: hint.to_string(),
270 desc: desc.to_string(),
271 hasarg: Yes,
272 occur: Optional
273 });
274 self
275 }
276
277 /// Create a long option that is required and takes an argument.
278 ///
279 /// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
280 /// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
281 /// * `desc` - Description for usage help
282 /// * `hint` - Hint that is used in place of the argument in the usage help,
283 /// e.g. `"FILE"` for a `-o FILE` option
284 pub fn reqopt(&mut self, short_name: &str, long_name: &str, desc: &str, hint: &str)
285 -> &mut Options {
286 validate_names(short_name, long_name);
287 self.grps.push(OptGroup {
288 short_name: short_name.to_string(),
289 long_name: long_name.to_string(),
290 hint: hint.to_string(),
291 desc: desc.to_string(),
292 hasarg: Yes,
293 occur: Req
294 });
295 self
296 }
297
298 /// Parse command line arguments according to the provided options.
299 ///
300 /// On success returns `Ok(Matches)`. Use methods such as `opt_present`
301 /// `opt_str`, etc. to interrogate results.
302 /// # Panics
303 ///
304 /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail`
305 /// to display information about it.
306 pub fn parse<C: IntoIterator>(&self, args: C) -> Result
307 where C::Item: AsRef<OsStr>
308 {
309 let opts: Vec<Opt> = self.grps.iter().map(|x| x.long_to_short()).collect();
310 let n_opts = opts.len();
311
312 fn f(_x: usize) -> Vec<Optval> { return Vec::new(); }
313
314 let mut vals = (0 .. n_opts).map(f).collect::<Vec<_>>();
315 let mut free: Vec<String> = Vec::new();
316 let args = try!(args.into_iter().map(|i| {
317 i.as_ref().to_str().ok_or_else(|| {
318 Fail::UnrecognizedOption(format!("{:?}", i.as_ref()))
319 }).map(|s| s.to_owned())
320 }).collect::<::std::result::Result<Vec<_>, _>>());
321 let l = args.len();
322 let mut i = 0;
323 while i < l {
324 let cur = args[i].clone();
325 let curlen = cur.len();
326 if !is_arg(&cur) {
327 match self.parsing_style {
328 ParsingStyle::FloatingFrees => free.push(cur),
329 ParsingStyle::StopAtFirstFree => {
330 while i < l {
331 free.push(args[i].clone());
332 i += 1;
333 }
334 break;
335 }
336 }
337 } else if cur == "--" {
338 let mut j = i + 1;
339 while j < l { free.push(args[j].clone()); j += 1; }
340 break;
341 } else {
342 let mut names;
343 let mut i_arg = None;
344 let mut was_long = true;
345 if cur.as_bytes()[1] == b'-' || self.long_only {
346 let tail = if cur.as_bytes()[1] == b'-' {
347 &cur[2..curlen]
348 } else {
349 assert!(self.long_only);
350 &cur[1..curlen]
351 };
352 let tail_eq: Vec<&str> = tail.splitn(2, '=').collect();
353 if tail_eq.len() <= 1 {
354 names = vec!(Name::from_str(tail));
355 } else {
356 names =
357 vec!(Name::from_str(tail_eq[0]));
358 i_arg = Some(tail_eq[1].to_string());
359 }
360 } else {
361 was_long = false;
362 names = Vec::new();
363 for (j, ch) in cur.char_indices().skip(1) {
364 let opt = Short(ch);
365
366 /* In a series of potential options (eg. -aheJ), if we
367 see one which takes an argument, we assume all
368 subsequent characters make up the argument. This
369 allows options such as -L/usr/local/lib/foo to be
370 interpreted correctly
371 */
372
373 let opt_id = match find_opt(&opts, opt.clone()) {
374 Some(id) => id,
375 None => return Err(UnrecognizedOption(opt.to_string()))
376 };
377
378 names.push(opt);
379
380 let arg_follows = match opts[opt_id].hasarg {
381 Yes | Maybe => true,
382 No => false
383 };
384
385 if arg_follows {
386 let next = j + ch.len_utf8();
387 if next < curlen {
388 i_arg = Some(cur[next..curlen].to_string());
389 break;
390 }
391 }
392 }
393 }
394 let mut name_pos = 0;
395 for nm in names.iter() {
396 name_pos += 1;
397 let optid = match find_opt(&opts, (*nm).clone()) {
398 Some(id) => id,
399 None => return Err(UnrecognizedOption(nm.to_string()))
400 };
401 match opts[optid].hasarg {
402 No => {
403 if name_pos == names.len() && !i_arg.is_none() {
404 return Err(UnexpectedArgument(nm.to_string()));
405 }
406 vals[optid].push(Given);
407 }
408 Maybe => {
409 // Note that here we do not handle `--arg value`.
410 // This matches GNU getopt behavior; but also
411 // makes sense, because if this were accepted,
412 // then users could only write a "Maybe" long
413 // option at the end of the arguments when
414 // FloatingFrees is in use.
415 if !i_arg.is_none() {
416 vals[optid]
417 .push(Val((i_arg.clone())
418 .unwrap()));
419 } else if was_long || name_pos < names.len() || i + 1 == l ||
420 is_arg(&args[i + 1]) {
421 vals[optid].push(Given);
422 } else {
423 i += 1;
424 vals[optid].push(Val(args[i].clone()));
425 }
426 }
427 Yes => {
428 if !i_arg.is_none() {
429 vals[optid].push(Val(i_arg.clone().unwrap()));
430 } else if i + 1 == l {
431 return Err(ArgumentMissing(nm.to_string()));
432 } else {
433 i += 1;
434 vals[optid].push(Val(args[i].clone()));
435 }
436 }
437 }
438 }
439 }
440 i += 1;
441 }
442 for i in 0 .. n_opts {
443 let n = vals[i].len();
444 let occ = opts[i].occur;
445 if occ == Req && n == 0 {
446 return Err(OptionMissing(opts[i].name.to_string()));
447 }
448 if occ != Multi && n > 1 {
449 return Err(OptionDuplicated(opts[i].name.to_string()));
450 }
451 }
452 Ok(Matches {
453 opts: opts,
454 vals: vals,
455 free: free
456 })
457 }
458
459 /// Derive a short one-line usage summary from a set of long options.
460 pub fn short_usage(&self, program_name: &str) -> String {
461 let mut line = format!("Usage: {} ", program_name);
462 line.push_str(&self.grps.iter()
463 .map(format_option)
464 .collect::<Vec<String>>()
465 .join(" "));
466 line
467 }
468
469 /// Derive a usage message from a set of options.
470 pub fn usage(&self, brief: &str) -> String {
471 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
472
473 let any_short = self.grps.iter().any(|optref| {
474 optref.short_name.len() > 0
475 });
476
477 let rows = self.grps.iter().map(|optref| {
478 let OptGroup{short_name,
479 long_name,
480 hint,
481 desc,
482 hasarg,
483 ..} = (*optref).clone();
484
485 let mut row = " ".to_string();
486
487 // short option
488 match short_name.len() {
489 0 => {
490 if any_short {
491 row.push_str(" ");
492 }
493 }
494 1 => {
495 row.push('-');
496 row.push_str(&short_name);
497 if long_name.len() > 0 {
498 row.push_str(", ");
499 } else {
500 // Only a single space here, so that any
501 // argument is printed in the correct spot.
502 row.push(' ');
503 }
504 }
505 _ => panic!("the short name should only be 1 ascii char long"),
506 }
507
508 // long option
509 match long_name.len() {
510 0 => {}
511 _ => {
512 if self.long_only {
513 row.push('-');
514 } else {
515 row.push_str("--");
516 }
517 row.push_str(&long_name);
518 row.push(' ');
519 }
520 }
521
522 // arg
523 match hasarg {
524 No => {}
525 Yes => row.push_str(&hint),
526 Maybe => {
527 row.push('[');
528 row.push_str(&hint);
529 row.push(']');
530 }
531 }
532
533 // FIXME: #5516 should be graphemes not codepoints
534 // here we just need to indent the start of the description
535 let rowlen = row.chars().count();
536 if rowlen < 24 {
537 for _ in 0 .. 24 - rowlen {
538 row.push(' ');
539 }
540 } else {
541 row.push_str(&desc_sep)
542 }
543
544 // Normalize desc to contain words separated by one space character
545 let mut desc_normalized_whitespace = String::new();
546 for word in desc.split(|c: char| c.is_whitespace())
547 .filter(|s| !s.is_empty()) {
548 desc_normalized_whitespace.push_str(word);
549 desc_normalized_whitespace.push(' ');
550 }
551
552 // FIXME: #5516 should be graphemes not codepoints
553 let mut desc_rows = Vec::new();
554 each_split_within(&desc_normalized_whitespace,
555 54,
556 |substr| {
557 desc_rows.push(substr.to_string());
558 true
559 });
560
561 // FIXME: #5516 should be graphemes not codepoints
562 // wrapped description
563 row.push_str(&desc_rows.join(&desc_sep));
564
565 row
566 });
567
568 format!("{}\n\nOptions:\n{}\n", brief,
569 rows.collect::<Vec<String>>().join("\n"))
570 }
571 }
572
573 fn validate_names(short_name: &str, long_name: &str) {
574 let len = short_name.len();
575 assert!(len == 1 || len == 0,
576 "the short_name (first argument) should be a single character, \
577 or an empty string for none");
578 let len = long_name.len();
579 assert!(len == 0 || len > 1,
580 "the long_name (second argument) should be longer than a single \
581 character, or an empty string for none");
582 }
583
584 /// What parsing style to use when parsing arguments.
585 #[derive(Clone, Copy, PartialEq, Eq)]
586 pub enum ParsingStyle {
587 /// Flags and "free" arguments can be freely inter-mixed.
588 FloatingFrees,
589 /// As soon as a "free" argument (i.e. non-flag) is encountered, stop
590 /// considering any remaining arguments as flags.
591 StopAtFirstFree
592 }
593
594 /// Name of an option. Either a string or a single char.
595 #[derive(Clone, PartialEq, Eq)]
596 enum Name {
597 /// A string representing the long name of an option.
598 /// For example: "help"
599 Long(String),
600 /// A char representing the short name of an option.
601 /// For example: 'h'
602 Short(char),
603 }
604
605 /// Describes whether an option has an argument.
606 #[derive(Clone, Copy, PartialEq, Eq)]
607 pub enum HasArg {
608 /// The option requires an argument.
609 Yes,
610 /// The option takes no argument.
611 No,
612 /// The option argument is optional.
613 Maybe,
614 }
615
616 /// Describes how often an option may occur.
617 #[derive(Clone, Copy, PartialEq, Eq)]
618 pub enum Occur {
619 /// The option occurs once.
620 Req,
621 /// The option occurs at most once.
622 Optional,
623 /// The option occurs zero or more times.
624 Multi,
625 }
626
627 /// A description of a possible option.
628 #[derive(Clone, PartialEq, Eq)]
629 struct Opt {
630 /// Name of the option
631 name: Name,
632 /// Whether it has an argument
633 hasarg: HasArg,
634 /// How often it can occur
635 occur: Occur,
636 /// Which options it aliases
637 aliases: Vec<Opt>,
638 }
639
640 /// One group of options, e.g., both `-h` and `--help`, along with
641 /// their shared description and properties.
642 #[derive(Clone, PartialEq, Eq)]
643 struct OptGroup {
644 /// Short name of the option, e.g. `h` for a `-h` option
645 short_name: String,
646 /// Long name of the option, e.g. `help` for a `--help` option
647 long_name: String,
648 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
649 hint: String,
650 /// Description for usage help text
651 desc: String,
652 /// Whether option has an argument
653 hasarg: HasArg,
654 /// How often it can occur
655 occur: Occur
656 }
657
658 /// Describes whether an option is given at all or has a value.
659 #[derive(Clone, PartialEq, Eq)]
660 enum Optval {
661 Val(String),
662 Given,
663 }
664
665 /// The result of checking command line arguments. Contains a vector
666 /// of matches and a vector of free strings.
667 #[derive(Clone, PartialEq, Eq)]
668 pub struct Matches {
669 /// Options that matched
670 opts: Vec<Opt>,
671 /// Values of the Options that matched
672 vals: Vec<Vec<Optval>>,
673 /// Free string fragments
674 pub free: Vec<String>,
675 }
676
677 /// The type returned when the command line does not conform to the
678 /// expected format. Use the `Debug` implementation to output detailed
679 /// information.
680 #[derive(Clone, Debug, PartialEq, Eq)]
681 pub enum Fail {
682 /// The option requires an argument but none was passed.
683 ArgumentMissing(String),
684 /// The passed option is not declared among the possible options.
685 UnrecognizedOption(String),
686 /// A required option is not present.
687 OptionMissing(String),
688 /// A single occurrence option is being used multiple times.
689 OptionDuplicated(String),
690 /// There's an argument being passed to a non-argument option.
691 UnexpectedArgument(String),
692 }
693
694 impl Error for Fail {
695 fn description(&self) -> &str {
696 match *self {
697 ArgumentMissing(_) => "missing argument",
698 UnrecognizedOption(_) => "unrecognized option",
699 OptionMissing(_) => "missing option",
700 OptionDuplicated(_) => "duplicated option",
701 UnexpectedArgument(_) => "unexpected argument",
702 }
703 }
704 }
705
706 /// The result of parsing a command line with a set of options.
707 pub type Result = result::Result<Matches, Fail>;
708
709 impl Name {
710 fn from_str(nm: &str) -> Name {
711 if nm.len() == 1 {
712 Short(nm.as_bytes()[0] as char)
713 } else {
714 Long(nm.to_string())
715 }
716 }
717
718 fn to_string(&self) -> String {
719 match *self {
720 Short(ch) => ch.to_string(),
721 Long(ref s) => s.to_string()
722 }
723 }
724 }
725
726 impl OptGroup {
727 /// Translate OptGroup into Opt.
728 /// (Both short and long names correspond to different Opts).
729 fn long_to_short(&self) -> Opt {
730 let OptGroup {
731 short_name,
732 long_name,
733 hasarg,
734 occur,
735 ..
736 } = (*self).clone();
737
738 match (short_name.len(), long_name.len()) {
739 (0,0) => panic!("this long-format option was given no name"),
740 (0,_) => Opt {
741 name: Long((long_name)),
742 hasarg: hasarg,
743 occur: occur,
744 aliases: Vec::new()
745 },
746 (1,0) => Opt {
747 name: Short(short_name.as_bytes()[0] as char),
748 hasarg: hasarg,
749 occur: occur,
750 aliases: Vec::new()
751 },
752 (1,_) => Opt {
753 name: Long((long_name)),
754 hasarg: hasarg,
755 occur: occur,
756 aliases: vec!(
757 Opt {
758 name: Short(short_name.as_bytes()[0] as char),
759 hasarg: hasarg,
760 occur: occur,
761 aliases: Vec::new()
762 }
763 )
764 },
765 (_,_) => panic!("something is wrong with the long-form opt")
766 }
767 }
768 }
769
770 impl Matches {
771 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
772 match find_opt(&self.opts, Name::from_str(nm)) {
773 Some(id) => self.vals[id].clone(),
774 None => panic!("No option '{}' defined", nm)
775 }
776 }
777
778 fn opt_val(&self, nm: &str) -> Option<Optval> {
779 self.opt_vals(nm).into_iter().next()
780 }
781 /// Returns true if an option was defined
782 pub fn opt_defined(&self, nm: &str) -> bool {
783 find_opt(&self.opts, Name::from_str(nm)).is_some()
784 }
785
786 /// Returns true if an option was matched.
787 pub fn opt_present(&self, nm: &str) -> bool {
788 !self.opt_vals(nm).is_empty()
789 }
790
791 /// Returns the number of times an option was matched.
792 pub fn opt_count(&self, nm: &str) -> usize {
793 self.opt_vals(nm).len()
794 }
795
796 /// Returns true if any of several options were matched.
797 pub fn opts_present(&self, names: &[String]) -> bool {
798 names.iter().any(|nm| {
799 match find_opt(&self.opts, Name::from_str(&nm)) {
800 Some(id) if !self.vals[id].is_empty() => true,
801 _ => false,
802 }
803 })
804 }
805
806 /// Returns the string argument supplied to one of several matching options or `None`.
807 pub fn opts_str(&self, names: &[String]) -> Option<String> {
808 names.iter().filter_map(|nm| {
809 match self.opt_val(&nm) {
810 Some(Val(s)) => Some(s),
811 _ => None,
812 }
813 }).next()
814 }
815
816 /// Returns a vector of the arguments provided to all matches of the given
817 /// option.
818 ///
819 /// Used when an option accepts multiple values.
820 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
821 self.opt_vals(nm).into_iter().filter_map(|v| {
822 match v {
823 Val(s) => Some(s),
824 _ => None,
825 }
826 }).collect()
827 }
828
829 /// Returns the string argument supplied to a matching option or `None`.
830 pub fn opt_str(&self, nm: &str) -> Option<String> {
831 match self.opt_val(nm) {
832 Some(Val(s)) => Some(s),
833 _ => None,
834 }
835 }
836
837
838 /// Returns the matching string, a default, or `None`.
839 ///
840 /// Returns `None` if the option was not present, `def` if the option was
841 /// present but no argument was provided, and the argument if the option was
842 /// present and an argument was provided.
843 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
844 match self.opt_val(nm) {
845 Some(Val(s)) => Some(s),
846 Some(_) => Some(def.to_string()),
847 None => None,
848 }
849 }
850
851 }
852
853 fn is_arg(arg: &str) -> bool {
854 arg.as_bytes().get(0) == Some(&b'-') && arg.len() > 1
855 }
856
857 fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
858 // Search main options.
859 let pos = opts.iter().position(|opt| opt.name == nm);
860 if pos.is_some() {
861 return pos
862 }
863
864 // Search in aliases.
865 for candidate in opts.iter() {
866 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
867 return opts.iter().position(|opt| opt.name == candidate.name);
868 }
869 }
870
871 None
872 }
873
874 impl fmt::Display for Fail {
875 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
876 match *self {
877 ArgumentMissing(ref nm) => {
878 write!(f, "Argument to option '{}' missing", *nm)
879 }
880 UnrecognizedOption(ref nm) => {
881 write!(f, "Unrecognized option: '{}'", *nm)
882 }
883 OptionMissing(ref nm) => {
884 write!(f, "Required option '{}' missing", *nm)
885 }
886 OptionDuplicated(ref nm) => {
887 write!(f, "Option '{}' given more than once", *nm)
888 }
889 UnexpectedArgument(ref nm) => {
890 write!(f, "Option '{}' does not take an argument", *nm)
891 }
892 }
893 }
894 }
895
896 fn format_option(opt: &OptGroup) -> String {
897 let mut line = String::new();
898
899 if opt.occur != Req {
900 line.push('[');
901 }
902
903 // Use short_name if possible, but fall back to long_name.
904 if opt.short_name.len() > 0 {
905 line.push('-');
906 line.push_str(&opt.short_name);
907 } else {
908 line.push_str("--");
909 line.push_str(&opt.long_name);
910 }
911
912 if opt.hasarg != No {
913 line.push(' ');
914 if opt.hasarg == Maybe {
915 line.push('[');
916 }
917 line.push_str(&opt.hint);
918 if opt.hasarg == Maybe {
919 line.push(']');
920 }
921 }
922
923 if opt.occur != Req {
924 line.push(']');
925 }
926 if opt.occur == Multi {
927 line.push_str("..");
928 }
929
930 line
931 }
932
933 #[derive(Clone, Copy)]
934 enum SplitWithinState {
935 A, // leading whitespace, initial state
936 B, // words
937 C, // internal and trailing whitespace
938 }
939
940 #[derive(Clone, Copy)]
941 enum Whitespace {
942 Ws, // current char is whitespace
943 Cr // current char is not whitespace
944 }
945
946 #[derive(Clone, Copy)]
947 enum LengthLimit {
948 UnderLim, // current char makes current substring still fit in limit
949 OverLim // current char makes current substring no longer fit in limit
950 }
951
952
953 /// Splits a string into substrings with possibly internal whitespace,
954 /// each of them at most `lim` bytes long, if possible. The substrings
955 /// have leading and trailing whitespace removed, and are only cut at
956 /// whitespace boundaries.
957 ///
958 /// Note: Function was moved here from `std::str` because this module is the only place that
959 /// uses it, and because it was too specific for a general string function.
960 fn each_split_within<'a, F>(ss: &'a str, lim: usize, mut it: F)
961 -> bool where F: FnMut(&'a str) -> bool {
962 // Just for fun, let's write this as a state machine:
963
964 let mut slice_start = 0;
965 let mut last_start = 0;
966 let mut last_end = 0;
967 let mut state = A;
968 let mut fake_i = ss.len();
969 let mut lim = lim;
970
971 let mut cont = true;
972
973 // if the limit is larger than the string, lower it to save cycles
974 if lim >= fake_i {
975 lim = fake_i;
976 }
977
978 let mut machine = |cont: &mut bool, (i, c): (usize, char)| {
979 let whitespace = if c.is_whitespace() { Ws } else { Cr };
980 let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim };
981
982 state = match (state, whitespace, limit) {
983 (A, Ws, _) => { A }
984 (A, Cr, _) => { slice_start = i; last_start = i; B }
985
986 (B, Cr, UnderLim) => { B }
987 (B, Cr, OverLim) if (i - last_start + 1) > lim => {
988 // A single word has gone over the limit. In this
989 // case we just accept that the word will be too long.
990 B
991 }
992 (B, Cr, OverLim) => {
993 *cont = it(&ss[slice_start..last_end]);
994 slice_start = last_start;
995 B
996 }
997 (B, Ws, UnderLim) => {
998 last_end = i;
999 C
1000 }
1001 (B, Ws, OverLim) => {
1002 last_end = i;
1003 *cont = it(&ss[slice_start..last_end]);
1004 A
1005 }
1006
1007 (C, Cr, UnderLim) => {
1008 last_start = i;
1009 B
1010 }
1011 (C, Cr, OverLim) => {
1012 *cont = it(&ss[slice_start..last_end]);
1013 slice_start = i;
1014 last_start = i;
1015 last_end = i;
1016 B
1017 }
1018 (C, Ws, OverLim) => {
1019 *cont = it(&ss[slice_start..last_end]);
1020 A
1021 }
1022 (C, Ws, UnderLim) => {
1023 C
1024 }
1025 };
1026
1027 *cont
1028 };
1029
1030 ss.char_indices().all(|x| machine(&mut cont, x));
1031
1032 // Let the automaton 'run out' by supplying trailing whitespace
1033 while cont && match state { B | C => true, A => false } {
1034 machine(&mut cont, (fake_i, ' '));
1035 fake_i += 1;
1036 }
1037 return cont;
1038 }
1039
1040 #[test]
1041 fn test_split_within() {
1042 fn t(s: &str, i: usize, u: &[String]) {
1043 let mut v = Vec::new();
1044 each_split_within(s, i, |s| { v.push(s.to_string()); true });
1045 assert!(v.iter().zip(u.iter()).all(|(a,b)| a == b));
1046 }
1047 t("", 0, &[]);
1048 t("", 15, &[]);
1049 t("hello", 15, &["hello".to_string()]);
1050 t("\nMary had a little lamb\nLittle lamb\n", 15, &[
1051 "Mary had a".to_string(),
1052 "little lamb".to_string(),
1053 "Little lamb".to_string()
1054 ]);
1055 t("\nMary had a little lamb\nLittle lamb\n", ::std::usize::MAX,
1056 &["Mary had a little lamb\nLittle lamb".to_string()]);
1057 }
1058
1059 #[cfg(test)]
1060 mod tests {
1061 use super::{HasArg, Name, Occur, Opt, Options, ParsingStyle};
1062 use super::Fail::*;
1063
1064 // Tests for reqopt
1065 #[test]
1066 fn test_reqopt() {
1067 let long_args = vec!("--test=20".to_string());
1068 let mut opts = Options::new();
1069 opts.reqopt("t", "test", "testing", "TEST");
1070 match opts.parse(&long_args) {
1071 Ok(ref m) => {
1072 assert!(m.opt_present("test"));
1073 assert_eq!(m.opt_str("test").unwrap(), "20");
1074 assert!(m.opt_present("t"));
1075 assert_eq!(m.opt_str("t").unwrap(), "20");
1076 }
1077 _ => { panic!("test_reqopt failed (long arg)"); }
1078 }
1079 let short_args = vec!("-t".to_string(), "20".to_string());
1080 match opts.parse(&short_args) {
1081 Ok(ref m) => {
1082 assert!((m.opt_present("test")));
1083 assert_eq!(m.opt_str("test").unwrap(), "20");
1084 assert!((m.opt_present("t")));
1085 assert_eq!(m.opt_str("t").unwrap(), "20");
1086 }
1087 _ => { panic!("test_reqopt failed (short arg)"); }
1088 }
1089 }
1090
1091 #[test]
1092 fn test_reqopt_missing() {
1093 let args = vec!("blah".to_string());
1094 match Options::new()
1095 .reqopt("t", "test", "testing", "TEST")
1096 .parse(&args) {
1097 Err(OptionMissing(_)) => {},
1098 _ => panic!()
1099 }
1100 }
1101
1102 #[test]
1103 fn test_reqopt_no_arg() {
1104 let long_args = vec!("--test".to_string());
1105 let mut opts = Options::new();
1106 opts.reqopt("t", "test", "testing", "TEST");
1107 match opts.parse(&long_args) {
1108 Err(ArgumentMissing(_)) => {},
1109 _ => panic!()
1110 }
1111 let short_args = vec!("-t".to_string());
1112 match opts.parse(&short_args) {
1113 Err(ArgumentMissing(_)) => {},
1114 _ => panic!()
1115 }
1116 }
1117
1118 #[test]
1119 fn test_reqopt_multi() {
1120 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1121 match Options::new()
1122 .reqopt("t", "test", "testing", "TEST")
1123 .parse(&args) {
1124 Err(OptionDuplicated(_)) => {},
1125 _ => panic!()
1126 }
1127 }
1128
1129 // Tests for optopt
1130 #[test]
1131 fn test_optopt() {
1132 let long_args = vec!("--test=20".to_string());
1133 let mut opts = Options::new();
1134 opts.optopt("t", "test", "testing", "TEST");
1135 match opts.parse(&long_args) {
1136 Ok(ref m) => {
1137 assert!(m.opt_present("test"));
1138 assert_eq!(m.opt_str("test").unwrap(), "20");
1139 assert!((m.opt_present("t")));
1140 assert_eq!(m.opt_str("t").unwrap(), "20");
1141 }
1142 _ => panic!()
1143 }
1144 let short_args = vec!("-t".to_string(), "20".to_string());
1145 match opts.parse(&short_args) {
1146 Ok(ref m) => {
1147 assert!((m.opt_present("test")));
1148 assert_eq!(m.opt_str("test").unwrap(), "20");
1149 assert!((m.opt_present("t")));
1150 assert_eq!(m.opt_str("t").unwrap(), "20");
1151 }
1152 _ => panic!()
1153 }
1154 }
1155
1156 #[test]
1157 fn test_optopt_missing() {
1158 let args = vec!("blah".to_string());
1159 match Options::new()
1160 .optopt("t", "test", "testing", "TEST")
1161 .parse(&args) {
1162 Ok(ref m) => {
1163 assert!(!m.opt_present("test"));
1164 assert!(!m.opt_present("t"));
1165 }
1166 _ => panic!()
1167 }
1168 }
1169
1170 #[test]
1171 fn test_optopt_no_arg() {
1172 let long_args = vec!("--test".to_string());
1173 let mut opts = Options::new();
1174 opts.optopt("t", "test", "testing", "TEST");
1175 match opts.parse(&long_args) {
1176 Err(ArgumentMissing(_)) => {},
1177 _ => panic!()
1178 }
1179 let short_args = vec!("-t".to_string());
1180 match opts.parse(&short_args) {
1181 Err(ArgumentMissing(_)) => {},
1182 _ => panic!()
1183 }
1184 }
1185
1186 #[test]
1187 fn test_optopt_multi() {
1188 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1189 match Options::new()
1190 .optopt("t", "test", "testing", "TEST")
1191 .parse(&args) {
1192 Err(OptionDuplicated(_)) => {},
1193 _ => panic!()
1194 }
1195 }
1196
1197 // Tests for optflag
1198 #[test]
1199 fn test_optflag() {
1200 let long_args = vec!("--test".to_string());
1201 let mut opts = Options::new();
1202 opts.optflag("t", "test", "testing");
1203 match opts.parse(&long_args) {
1204 Ok(ref m) => {
1205 assert!(m.opt_present("test"));
1206 assert!(m.opt_present("t"));
1207 }
1208 _ => panic!()
1209 }
1210 let short_args = vec!("-t".to_string());
1211 match opts.parse(&short_args) {
1212 Ok(ref m) => {
1213 assert!(m.opt_present("test"));
1214 assert!(m.opt_present("t"));
1215 }
1216 _ => panic!()
1217 }
1218 }
1219
1220 #[test]
1221 fn test_optflag_missing() {
1222 let args = vec!("blah".to_string());
1223 match Options::new()
1224 .optflag("t", "test", "testing")
1225 .parse(&args) {
1226 Ok(ref m) => {
1227 assert!(!m.opt_present("test"));
1228 assert!(!m.opt_present("t"));
1229 }
1230 _ => panic!()
1231 }
1232 }
1233
1234 #[test]
1235 fn test_optflag_long_arg() {
1236 let args = vec!("--test=20".to_string());
1237 match Options::new()
1238 .optflag("t", "test", "testing")
1239 .parse(&args) {
1240 Err(UnexpectedArgument(_)) => {},
1241 _ => panic!()
1242 }
1243 }
1244
1245 #[test]
1246 fn test_optflag_multi() {
1247 let args = vec!("--test".to_string(), "-t".to_string());
1248 match Options::new()
1249 .optflag("t", "test", "testing")
1250 .parse(&args) {
1251 Err(OptionDuplicated(_)) => {},
1252 _ => panic!()
1253 }
1254 }
1255
1256 #[test]
1257 fn test_optflag_short_arg() {
1258 let args = vec!("-t".to_string(), "20".to_string());
1259 match Options::new()
1260 .optflag("t", "test", "testing")
1261 .parse(&args) {
1262 Ok(ref m) => {
1263 // The next variable after the flag is just a free argument
1264
1265 assert!(m.free[0] == "20");
1266 }
1267 _ => panic!()
1268 }
1269 }
1270
1271 // Tests for optflagmulti
1272 #[test]
1273 fn test_optflagmulti_short1() {
1274 let args = vec!("-v".to_string());
1275 match Options::new()
1276 .optflagmulti("v", "verbose", "verbosity")
1277 .parse(&args) {
1278 Ok(ref m) => {
1279 assert_eq!(m.opt_count("v"), 1);
1280 }
1281 _ => panic!()
1282 }
1283 }
1284
1285 #[test]
1286 fn test_optflagmulti_short2a() {
1287 let args = vec!("-v".to_string(), "-v".to_string());
1288 match Options::new()
1289 .optflagmulti("v", "verbose", "verbosity")
1290 .parse(&args) {
1291 Ok(ref m) => {
1292 assert_eq!(m.opt_count("v"), 2);
1293 }
1294 _ => panic!()
1295 }
1296 }
1297
1298 #[test]
1299 fn test_optflagmulti_short2b() {
1300 let args = vec!("-vv".to_string());
1301 match Options::new()
1302 .optflagmulti("v", "verbose", "verbosity")
1303 .parse(&args) {
1304 Ok(ref m) => {
1305 assert_eq!(m.opt_count("v"), 2);
1306 }
1307 _ => panic!()
1308 }
1309 }
1310
1311 #[test]
1312 fn test_optflagmulti_long1() {
1313 let args = vec!("--verbose".to_string());
1314 match Options::new()
1315 .optflagmulti("v", "verbose", "verbosity")
1316 .parse(&args) {
1317 Ok(ref m) => {
1318 assert_eq!(m.opt_count("verbose"), 1);
1319 }
1320 _ => panic!()
1321 }
1322 }
1323
1324 #[test]
1325 fn test_optflagmulti_long2() {
1326 let args = vec!("--verbose".to_string(), "--verbose".to_string());
1327 match Options::new()
1328 .optflagmulti("v", "verbose", "verbosity")
1329 .parse(&args) {
1330 Ok(ref m) => {
1331 assert_eq!(m.opt_count("verbose"), 2);
1332 }
1333 _ => panic!()
1334 }
1335 }
1336
1337 #[test]
1338 fn test_optflagmulti_mix() {
1339 let args = vec!("--verbose".to_string(), "-v".to_string(),
1340 "-vv".to_string(), "verbose".to_string());
1341 match Options::new()
1342 .optflagmulti("v", "verbose", "verbosity")
1343 .parse(&args) {
1344 Ok(ref m) => {
1345 assert_eq!(m.opt_count("verbose"), 4);
1346 assert_eq!(m.opt_count("v"), 4);
1347 }
1348 _ => panic!()
1349 }
1350 }
1351
1352 // Tests for optflagopt
1353 #[test]
1354 fn test_optflagopt() {
1355 let long_args = vec!("--test".to_string());
1356 let mut opts = Options::new();
1357 opts.optflagopt("t", "test", "testing", "ARG");
1358 match opts.parse(&long_args) {
1359 Ok(ref m) => {
1360 assert!(m.opt_present("test"));
1361 assert!(m.opt_present("t"));
1362 }
1363 _ => panic!()
1364 }
1365 let short_args = vec!("-t".to_string());
1366 match opts.parse(&short_args) {
1367 Ok(ref m) => {
1368 assert!(m.opt_present("test"));
1369 assert!(m.opt_present("t"));
1370 }
1371 _ => panic!()
1372 }
1373 let short_args = vec!("-t".to_string(), "x".to_string());
1374 match opts.parse(&short_args) {
1375 Ok(ref m) => {
1376 assert_eq!(m.opt_str("t").unwrap(), "x");
1377 assert_eq!(m.opt_str("test").unwrap(), "x");
1378 }
1379 _ => panic!()
1380 }
1381 let long_args = vec!("--test=x".to_string());
1382 match opts.parse(&long_args) {
1383 Ok(ref m) => {
1384 assert_eq!(m.opt_str("t").unwrap(), "x");
1385 assert_eq!(m.opt_str("test").unwrap(), "x");
1386 }
1387 _ => panic!()
1388 }
1389 let long_args = vec!("--test".to_string(), "x".to_string());
1390 match opts.parse(&long_args) {
1391 Ok(ref m) => {
1392 assert_eq!(m.opt_str("t"), None);
1393 assert_eq!(m.opt_str("test"), None);
1394 }
1395 _ => panic!()
1396 }
1397 let no_args: Vec<String> = vec!();
1398 match opts.parse(&no_args) {
1399 Ok(ref m) => {
1400 assert!(!m.opt_present("test"));
1401 assert!(!m.opt_present("t"));
1402 }
1403 _ => panic!()
1404 }
1405 }
1406
1407 // Tests for optmulti
1408 #[test]
1409 fn test_optmulti() {
1410 let long_args = vec!("--test=20".to_string());
1411 let mut opts = Options::new();
1412 opts.optmulti("t", "test", "testing", "TEST");
1413 match opts.parse(&long_args) {
1414 Ok(ref m) => {
1415 assert!((m.opt_present("test")));
1416 assert_eq!(m.opt_str("test").unwrap(), "20");
1417 assert!((m.opt_present("t")));
1418 assert_eq!(m.opt_str("t").unwrap(), "20");
1419 }
1420 _ => panic!()
1421 }
1422 let short_args = vec!("-t".to_string(), "20".to_string());
1423 match opts.parse(&short_args) {
1424 Ok(ref m) => {
1425 assert!((m.opt_present("test")));
1426 assert_eq!(m.opt_str("test").unwrap(), "20");
1427 assert!((m.opt_present("t")));
1428 assert_eq!(m.opt_str("t").unwrap(), "20");
1429 }
1430 _ => panic!()
1431 }
1432 }
1433
1434 #[test]
1435 fn test_optmulti_missing() {
1436 let args = vec!("blah".to_string());
1437 match Options::new()
1438 .optmulti("t", "test", "testing", "TEST")
1439 .parse(&args) {
1440 Ok(ref m) => {
1441 assert!(!m.opt_present("test"));
1442 assert!(!m.opt_present("t"));
1443 }
1444 _ => panic!()
1445 }
1446 }
1447
1448 #[test]
1449 fn test_optmulti_no_arg() {
1450 let long_args = vec!("--test".to_string());
1451 let mut opts = Options::new();
1452 opts.optmulti("t", "test", "testing", "TEST");
1453 match opts.parse(&long_args) {
1454 Err(ArgumentMissing(_)) => {},
1455 _ => panic!()
1456 }
1457 let short_args = vec!("-t".to_string());
1458 match opts.parse(&short_args) {
1459 Err(ArgumentMissing(_)) => {},
1460 _ => panic!()
1461 }
1462 }
1463
1464 #[test]
1465 fn test_optmulti_multi() {
1466 let args = vec!("--test=20".to_string(), "-t".to_string(), "30".to_string());
1467 match Options::new()
1468 .optmulti("t", "test", "testing", "TEST")
1469 .parse(&args) {
1470 Ok(ref m) => {
1471 assert!(m.opt_present("test"));
1472 assert_eq!(m.opt_str("test").unwrap(), "20");
1473 assert!(m.opt_present("t"));
1474 assert_eq!(m.opt_str("t").unwrap(), "20");
1475 let pair = m.opt_strs("test");
1476 assert!(pair[0] == "20");
1477 assert!(pair[1] == "30");
1478 }
1479 _ => panic!()
1480 }
1481 }
1482
1483 #[test]
1484 fn test_free_argument_is_hyphen() {
1485 let args = vec!("-".to_string());
1486 match Options::new().parse(&args) {
1487 Ok(ref m) => {
1488 assert_eq!(m.free.len(), 1);
1489 assert_eq!(m.free[0], "-");
1490 }
1491 _ => panic!()
1492 }
1493 }
1494
1495 #[test]
1496 fn test_unrecognized_option() {
1497 let long_args = vec!("--untest".to_string());
1498 let mut opts = Options::new();
1499 opts.optmulti("t", "test", "testing", "TEST");
1500 match opts.parse(&long_args) {
1501 Err(UnrecognizedOption(_)) => {},
1502 _ => panic!()
1503 }
1504 let short_args = vec!("-u".to_string());
1505 match opts.parse(&short_args) {
1506 Err(UnrecognizedOption(_)) => {},
1507 _ => panic!()
1508 }
1509 }
1510
1511 #[test]
1512 fn test_combined() {
1513 let args =
1514 vec!("prog".to_string(),
1515 "free1".to_string(),
1516 "-s".to_string(),
1517 "20".to_string(),
1518 "free2".to_string(),
1519 "--flag".to_string(),
1520 "--long=30".to_string(),
1521 "-f".to_string(),
1522 "-m".to_string(),
1523 "40".to_string(),
1524 "-m".to_string(),
1525 "50".to_string(),
1526 "-n".to_string(),
1527 "-A B".to_string(),
1528 "-n".to_string(),
1529 "-60 70".to_string());
1530 match Options::new()
1531 .optopt("s", "something", "something", "SOMETHING")
1532 .optflag("", "flag", "a flag")
1533 .reqopt("", "long", "hi", "LONG")
1534 .optflag("f", "", "another flag")
1535 .optmulti("m", "", "mmmmmm", "YUM")
1536 .optmulti("n", "", "nothing", "NOTHING")
1537 .optopt("", "notpresent", "nothing to see here", "NOPE")
1538 .parse(&args) {
1539 Ok(ref m) => {
1540 assert!(m.free[0] == "prog");
1541 assert!(m.free[1] == "free1");
1542 assert_eq!(m.opt_str("s").unwrap(), "20");
1543 assert!(m.free[2] == "free2");
1544 assert!((m.opt_present("flag")));
1545 assert_eq!(m.opt_str("long").unwrap(), "30");
1546 assert!((m.opt_present("f")));
1547 let pair = m.opt_strs("m");
1548 assert!(pair[0] == "40");
1549 assert!(pair[1] == "50");
1550 let pair = m.opt_strs("n");
1551 assert!(pair[0] == "-A B");
1552 assert!(pair[1] == "-60 70");
1553 assert!((!m.opt_present("notpresent")));
1554 }
1555 _ => panic!()
1556 }
1557 }
1558
1559 #[test]
1560 fn test_mixed_stop() {
1561 let args =
1562 vec!("-a".to_string(),
1563 "b".to_string(),
1564 "-c".to_string(),
1565 "d".to_string());
1566 match Options::new()
1567 .parsing_style(ParsingStyle::StopAtFirstFree)
1568 .optflag("a", "", "")
1569 .optopt("c", "", "", "")
1570 .parse(&args) {
1571 Ok(ref m) => {
1572 println!("{}", m.opt_present("c"));
1573 assert!(m.opt_present("a"));
1574 assert!(!m.opt_present("c"));
1575 assert_eq!(m.free.len(), 3);
1576 assert_eq!(m.free[0], "b");
1577 assert_eq!(m.free[1], "-c");
1578 assert_eq!(m.free[2], "d");
1579 }
1580 _ => panic!()
1581 }
1582 }
1583
1584 #[test]
1585 fn test_mixed_stop_hyphen() {
1586 let args =
1587 vec!("-a".to_string(),
1588 "-".to_string(),
1589 "-c".to_string(),
1590 "d".to_string());
1591 match Options::new()
1592 .parsing_style(ParsingStyle::StopAtFirstFree)
1593 .optflag("a", "", "")
1594 .optopt("c", "", "", "")
1595 .parse(&args) {
1596 Ok(ref m) => {
1597 println!("{}", m.opt_present("c"));
1598 assert!(m.opt_present("a"));
1599 assert!(!m.opt_present("c"));
1600 assert_eq!(m.free.len(), 3);
1601 assert_eq!(m.free[0], "-");
1602 assert_eq!(m.free[1], "-c");
1603 assert_eq!(m.free[2], "d");
1604 }
1605 _ => panic!()
1606 }
1607 }
1608
1609 #[test]
1610 fn test_multi() {
1611 let mut opts = Options::new();
1612 opts.optopt("e", "", "encrypt", "ENCRYPT");
1613 opts.optopt("", "encrypt", "encrypt", "ENCRYPT");
1614 opts.optopt("f", "", "flag", "FLAG");
1615
1616 let args_single = vec!("-e".to_string(), "foo".to_string());
1617 let matches_single = &match opts.parse(&args_single) {
1618 Ok(m) => m,
1619 Err(_) => panic!()
1620 };
1621 assert!(matches_single.opts_present(&["e".to_string()]));
1622 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1623 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1624 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1625 assert!(!matches_single.opts_present(&["thing".to_string()]));
1626 assert!(!matches_single.opts_present(&[]));
1627
1628 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1629 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1630 "foo");
1631 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1632 "foo");
1633
1634 let args_both = vec!("-e".to_string(), "foo".to_string(), "--encrypt".to_string(),
1635 "foo".to_string());
1636 let matches_both = &match opts.parse(&args_both) {
1637 Ok(m) => m,
1638 Err(_) => panic!()
1639 };
1640 assert!(matches_both.opts_present(&["e".to_string()]));
1641 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1642 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1643 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1644 assert!(!matches_both.opts_present(&["f".to_string()]));
1645 assert!(!matches_both.opts_present(&["thing".to_string()]));
1646 assert!(!matches_both.opts_present(&[]));
1647
1648 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
1649 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(), "foo");
1650 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1651 "foo");
1652 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1653 "foo");
1654 }
1655
1656 #[test]
1657 fn test_nospace() {
1658 let args = vec!("-Lfoo".to_string(), "-M.".to_string());
1659 let matches = &match Options::new()
1660 .optmulti("L", "", "library directory", "LIB")
1661 .optmulti("M", "", "something", "MMMM")
1662 .parse(&args) {
1663 Ok(m) => m,
1664 Err(_) => panic!()
1665 };
1666 assert!(matches.opts_present(&["L".to_string()]));
1667 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1668 assert!(matches.opts_present(&["M".to_string()]));
1669 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1670
1671 }
1672
1673 #[test]
1674 fn test_nospace_conflict() {
1675 let args = vec!("-vvLverbose".to_string(), "-v".to_string() );
1676 let matches = &match Options::new()
1677 .optmulti("L", "", "library directory", "LIB")
1678 .optflagmulti("v", "verbose", "Verbose")
1679 .parse(&args) {
1680 Ok(m) => m,
1681 Err(e) => panic!( "{}", e )
1682 };
1683 assert!(matches.opts_present(&["L".to_string()]));
1684 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1685 assert!(matches.opts_present(&["v".to_string()]));
1686 assert_eq!(3, matches.opt_count("v"));
1687 }
1688
1689 #[test]
1690 fn test_long_to_short() {
1691 let mut short = Opt {
1692 name: Name::Long("banana".to_string()),
1693 hasarg: HasArg::Yes,
1694 occur: Occur::Req,
1695 aliases: Vec::new(),
1696 };
1697 short.aliases = vec!(Opt { name: Name::Short('b'),
1698 hasarg: HasArg::Yes,
1699 occur: Occur::Req,
1700 aliases: Vec::new() });
1701 let mut opts = Options::new();
1702 opts.reqopt("b", "banana", "some bananas", "VAL");
1703 let ref verbose = opts.grps[0];
1704 assert!(verbose.long_to_short() == short);
1705 }
1706
1707 #[test]
1708 fn test_aliases_long_and_short() {
1709 let args = vec!("-a".to_string(), "--apple".to_string(), "-a".to_string());
1710
1711 let matches = Options::new()
1712 .optflagmulti("a", "apple", "Desc")
1713 .parse(&args)
1714 .unwrap();
1715 assert_eq!(3, matches.opt_count("a"));
1716 assert_eq!(3, matches.opt_count("apple"));
1717 }
1718
1719 #[test]
1720 fn test_usage() {
1721 let mut opts = Options::new();
1722 opts.reqopt("b", "banana", "Desc", "VAL");
1723 opts.optopt("a", "012345678901234567890123456789",
1724 "Desc", "VAL");
1725 opts.optflag("k", "kiwi", "Desc");
1726 opts.optflagopt("p", "", "Desc", "VAL");
1727 opts.optmulti("l", "", "Desc", "VAL");
1728 opts.optflag("", "starfruit", "Starfruit");
1729
1730 let expected =
1731 "Usage: fruits
1732
1733 Options:
1734 -b, --banana VAL Desc
1735 -a, --012345678901234567890123456789 VAL
1736 Desc
1737 -k, --kiwi Desc
1738 -p [VAL] Desc
1739 -l VAL Desc
1740 --starfruit Starfruit
1741 ";
1742
1743 let generated_usage = opts.usage("Usage: fruits");
1744
1745 debug!("expected: <<{}>>", expected);
1746 debug!("generated: <<{}>>", generated_usage);
1747 assert_eq!(generated_usage, expected);
1748 }
1749
1750 #[test]
1751 fn test_usage_description_wrapping() {
1752 // indentation should be 24 spaces
1753 // lines wrap after 78: or rather descriptions wrap after 54
1754
1755 let mut opts = Options::new();
1756 opts.optflag("k", "kiwi",
1757 "This is a long description which won't be wrapped..+.."); // 54
1758 opts.optflag("a", "apple",
1759 "This is a long description which _will_ be wrapped..+..");
1760 opts.optflag("b", "banana",
1761 "HereWeNeedOneSingleWordThatIsLongerThanTheWrappingLengthAndThisIsIt");
1762
1763 let expected =
1764 "Usage: fruits
1765
1766 Options:
1767 -k, --kiwi This is a long description which won't be wrapped..+..
1768 -a, --apple This is a long description which _will_ be
1769 wrapped..+..
1770 -b, --banana HereWeNeedOneSingleWordThatIsLongerThanTheWrappingLengthAndThisIsIt
1771 ";
1772
1773 let usage = opts.usage("Usage: fruits");
1774
1775 debug!("expected: <<{}>>", expected);
1776 debug!("generated: <<{}>>", usage);
1777 assert!(usage == expected)
1778 }
1779
1780 #[test]
1781 fn test_usage_description_multibyte_handling() {
1782 let mut opts = Options::new();
1783 opts.optflag("k", "k\u{2013}w\u{2013}",
1784 "The word kiwi is normally spelled with two i's");
1785 opts.optflag("a", "apple",
1786 "This \u{201C}description\u{201D} has some characters that could \
1787 confuse the line wrapping; an apple costs 0.51€ in some parts of Europe.");
1788
1789 let expected =
1790 "Usage: fruits
1791
1792 Options:
1793 -k, --k–w– The word kiwi is normally spelled with two i's
1794 -a, --apple This “description” has some characters that could
1795 confuse the line wrapping; an apple costs 0.51€ in
1796 some parts of Europe.
1797 ";
1798
1799 let usage = opts.usage("Usage: fruits");
1800
1801 debug!("expected: <<{}>>", expected);
1802 debug!("generated: <<{}>>", usage);
1803 assert!(usage == expected)
1804 }
1805
1806 #[test]
1807 fn test_usage_short_only() {
1808 let mut opts = Options::new();
1809 opts.optopt("k", "", "Kiwi", "VAL");
1810 opts.optflag("s", "", "Starfruit");
1811 opts.optflagopt("a", "", "Apple", "TYPE");
1812
1813 let expected =
1814 "Usage: fruits
1815
1816 Options:
1817 -k VAL Kiwi
1818 -s Starfruit
1819 -a [TYPE] Apple
1820 ";
1821
1822 let usage = opts.usage("Usage: fruits");
1823 debug!("expected: <<{}>>", expected);
1824 debug!("generated: <<{}>>", usage);
1825 assert!(usage == expected)
1826 }
1827
1828 #[test]
1829 fn test_usage_long_only() {
1830 let mut opts = Options::new();
1831 opts.optopt("", "kiwi", "Kiwi", "VAL");
1832 opts.optflag("", "starfruit", "Starfruit");
1833 opts.optflagopt("", "apple", "Apple", "TYPE");
1834
1835 let expected =
1836 "Usage: fruits
1837
1838 Options:
1839 --kiwi VAL Kiwi
1840 --starfruit Starfruit
1841 --apple [TYPE] Apple
1842 ";
1843
1844 let usage = opts.usage("Usage: fruits");
1845 debug!("expected: <<{}>>", expected);
1846 debug!("generated: <<{}>>", usage);
1847 assert!(usage == expected)
1848 }
1849
1850 #[test]
1851 fn test_short_usage() {
1852 let mut opts = Options::new();
1853 opts.reqopt("b", "banana", "Desc", "VAL");
1854 opts.optopt("a", "012345678901234567890123456789",
1855 "Desc", "VAL");
1856 opts.optflag("k", "kiwi", "Desc");
1857 opts.optflagopt("p", "", "Desc", "VAL");
1858 opts.optmulti("l", "", "Desc", "VAL");
1859
1860 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
1861 let generated_usage = opts.short_usage("fruits");
1862
1863 debug!("expected: <<{}>>", expected);
1864 debug!("generated: <<{}>>", generated_usage);
1865 assert_eq!(generated_usage, expected);
1866 }
1867 #[test]
1868 fn test_nonexistant_opt() {
1869 let mut opts = Options::new();
1870 opts.optflag("b", "bar", "Desc");
1871 let args: Vec<String> = Vec::new();
1872 let matches = opts.parse(&args).unwrap();
1873 assert_eq!(matches.opt_defined("foo"), false);
1874 assert_eq!(matches.opt_defined("bar"), true);
1875 }
1876 #[test]
1877 fn test_args_with_equals() {
1878 let mut opts = Options::new();
1879 opts.optopt("o", "one", "One", "INFO");
1880 opts.optopt("t", "two", "Two", "INFO");
1881
1882 let args = vec!("--one".to_string(), "A=B".to_string(),
1883 "--two=C=D".to_string());
1884 let matches = &match opts.parse(&args) {
1885 Ok(m) => m,
1886 Err(e) => panic!("{}", e)
1887 };
1888 assert_eq!(matches.opts_str(&["o".to_string()]).unwrap(), "A=B");
1889 assert_eq!(matches.opts_str(&["t".to_string()]).unwrap(), "C=D");
1890 }
1891
1892 #[test]
1893 fn test_long_only_usage() {
1894 let mut opts = Options::new();
1895 opts.long_only(true);
1896 opts.optflag("k", "kiwi", "Description");
1897 opts.optflag("a", "apple", "Description");
1898
1899 let expected =
1900 "Usage: fruits
1901
1902 Options:
1903 -k, -kiwi Description
1904 -a, -apple Description
1905 ";
1906
1907 let usage = opts.usage("Usage: fruits");
1908
1909 debug!("expected: <<{}>>", expected);
1910 debug!("generated: <<{}>>", usage);
1911 assert!(usage == expected)
1912 }
1913
1914 #[test]
1915 fn test_long_only_mode() {
1916 let mut opts = Options::new();
1917 opts.long_only(true);
1918 opts.optopt("a", "apple", "Description", "X");
1919 opts.optopt("b", "banana", "Description", "X");
1920 opts.optopt("c", "currant", "Description", "X");
1921 opts.optopt("", "durian", "Description", "X");
1922 opts.optopt("e", "", "Description", "X");
1923 opts.optopt("", "fruit", "Description", "X");
1924
1925 let args = vec!("-a", "A", "-b=B", "--c=C", "-durian", "D", "--e", "E",
1926 "-fruit=any");
1927 let matches = &match opts.parse(&args) {
1928 Ok(m) => m,
1929 Err(e) => panic!("{}", e)
1930 };
1931 assert_eq!(matches.opts_str(&["a".to_string()]).unwrap(), "A");
1932 assert_eq!(matches.opts_str(&["b".to_string()]).unwrap(), "B");
1933 assert_eq!(matches.opts_str(&["c".to_string()]).unwrap(), "C");
1934 assert_eq!(matches.opts_str(&["durian".to_string()]).unwrap(), "D");
1935 assert_eq!(matches.opts_str(&["e".to_string()]).unwrap(), "E");
1936 assert_eq!(matches.opts_str(&["fruit".to_string()]).unwrap(), "any");
1937 }
1938
1939 #[test]
1940 fn test_long_only_mode_no_short_parse() {
1941 let mut opts = Options::new();
1942 opts.long_only(true);
1943 opts.optflag("h", "help", "Description");
1944 opts.optflag("i", "ignore", "Description");
1945 opts.optflag("", "hi", "Description");
1946
1947 let args = vec!("-hi");
1948 let matches = &match opts.parse(&args) {
1949 Ok(m) => m,
1950 Err(e) => panic!("{}", e)
1951 };
1952 assert!(matches.opt_present("hi"));
1953 assert!(!matches.opt_present("h"));
1954 assert!(!matches.opt_present("i"));
1955 }
1956
1957 #[test]
1958 fn test_normal_mode_no_long_parse() {
1959 // Like test_long_only_mode_no_short_parse, but we make sure
1960 // that long_only can be disabled, and the right thing
1961 // happens.
1962 let mut opts = Options::new();
1963 opts.long_only(true);
1964 opts.optflag("h", "help", "Description");
1965 opts.optflag("i", "ignore", "Description");
1966 opts.optflag("", "hi", "Description");
1967 opts.long_only(false);
1968
1969 let args = vec!("-hi");
1970 let matches = &match opts.parse(&args) {
1971 Ok(m) => m,
1972 Err(e) => panic!("{}", e)
1973 };
1974 assert!(!matches.opt_present("hi"));
1975 assert!(matches.opt_present("h"));
1976 assert!(matches.opt_present("i"));
1977 }
1978
1979 #[test]
1980 #[should_panic]
1981 fn test_long_name_too_short() {
1982 let mut opts = Options::new();
1983 opts.optflag("", "a", "Oops, long option too short");
1984 }
1985 }