]> git.proxmox.com Git - rustc.git/blame - src/libgetopts/lib.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libgetopts / lib.rs
CommitLineData
1a4d82fc
JJ
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.
1a4d82fc
JJ
10
11//! Simple getopt alternative.
12//!
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.
19//!
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.
25//!
26//! # Example
27//!
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.
31//!
32//! ```{.rust}
92a42be0
SL
33//! #![feature(rustc_private)]
34//!
1a4d82fc
JJ
35//! extern crate getopts;
36//! use getopts::{optopt,optflag,getopts,OptGroup,usage};
92a42be0 37//! use std::env;
1a4d82fc
JJ
38//!
39//! fn do_work(inp: &str, out: Option<String>) {
40//! println!("{}", inp);
41//! match out {
42//! Some(x) => println!("{}", x),
43//! None => println!("No Output"),
44//! }
45//! }
46//!
47//! fn print_usage(program: &str, opts: &[OptGroup]) {
48//! let brief = format!("Usage: {} [options]", program);
92a42be0 49//! print!("{}", usage(&brief, opts));
1a4d82fc
JJ
50//! }
51//!
52//! fn main() {
92a42be0 53//! let args: Vec<String> = env::args().collect();
1a4d82fc
JJ
54//!
55//! let program = args[0].clone();
56//!
57//! let opts = &[
58//! optopt("o", "", "set output file name", "NAME"),
59//! optflag("h", "help", "print this help menu")
60//! ];
92a42be0 61//! let matches = match getopts(&args[1..], opts) {
1a4d82fc
JJ
62//! Ok(m) => { m }
63//! Err(f) => { panic!(f.to_string()) }
64//! };
65//! if matches.opt_present("h") {
92a42be0 66//! print_usage(&program, opts);
1a4d82fc
JJ
67//! return;
68//! }
69//! let output = matches.opt_str("o");
70//! let input = if !matches.free.is_empty() {
71//! matches.free[0].clone()
72//! } else {
92a42be0 73//! print_usage(&program, opts);
1a4d82fc
JJ
74//! return;
75//! };
92a42be0 76//! do_work(&input, output);
1a4d82fc
JJ
77//! }
78//! ```
79
80#![crate_name = "getopts"]
85aaf69f 81#![unstable(feature = "rustc_private",
e9174d1e
SL
82 reason = "use the crates.io `getopts` library instead",
83 issue = "27812")]
1a4d82fc
JJ
84#![crate_type = "rlib"]
85#![crate_type = "dylib"]
e9174d1e 86#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
62682a34 87 html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
e9174d1e 88 html_root_url = "https://doc.rust-lang.org/nightly/",
92a42be0
SL
89 html_playground_url = "https://play.rust-lang.org/",
90 test(attr(deny(warnings))))]
85aaf69f 91
1a4d82fc 92#![deny(missing_docs)]
85aaf69f 93#![feature(staged_api)]
1a4d82fc
JJ
94
95use self::Name::*;
96use self::HasArg::*;
97use self::Occur::*;
98use self::Fail::*;
99use self::Optval::*;
100use self::SplitWithinState::*;
101use self::Whitespace::*;
102use self::LengthLimit::*;
103
104use std::fmt;
105use std::iter::repeat;
106use std::result;
107
108/// Name of an option. Either a string or a single char.
85aaf69f 109#[derive(Clone, PartialEq, Eq, Debug)]
1a4d82fc
JJ
110pub enum Name {
111 /// A string representing the long name of an option.
112 /// For example: "help"
113 Long(String),
114 /// A char representing the short name of an option.
115 /// For example: 'h'
116 Short(char),
117}
118
119/// Describes whether an option has an argument.
85aaf69f 120#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1a4d82fc
JJ
121pub enum HasArg {
122 /// The option requires an argument.
123 Yes,
124 /// The option takes no argument.
125 No,
126 /// The option argument is optional.
127 Maybe,
128}
129
130/// Describes how often an option may occur.
85aaf69f 131#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1a4d82fc
JJ
132pub enum Occur {
133 /// The option occurs once.
134 Req,
135 /// The option occurs at most once.
136 Optional,
137 /// The option occurs zero or more times.
138 Multi,
139}
140
141/// A description of a possible option.
85aaf69f 142#[derive(Clone, PartialEq, Eq, Debug)]
1a4d82fc
JJ
143pub struct Opt {
144 /// Name of the option
145 pub name: Name,
146 /// Whether it has an argument
147 pub hasarg: HasArg,
148 /// How often it can occur
149 pub occur: Occur,
150 /// Which options it aliases
151 pub aliases: Vec<Opt>,
152}
153
154/// One group of options, e.g., both `-h` and `--help`, along with
155/// their shared description and properties.
85aaf69f 156#[derive(Clone, PartialEq, Eq, Debug)]
1a4d82fc
JJ
157pub struct OptGroup {
158 /// Short name of the option, e.g. `h` for a `-h` option
159 pub short_name: String,
160 /// Long name of the option, e.g. `help` for a `--help` option
161 pub long_name: String,
162 /// Hint for argument, e.g. `FILE` for a `-o FILE` option
163 pub hint: String,
164 /// Description for usage help text
165 pub desc: String,
166 /// Whether option has an argument
167 pub hasarg: HasArg,
168 /// How often it can occur
92a42be0 169 pub occur: Occur,
1a4d82fc
JJ
170}
171
172/// Describes whether an option is given at all or has a value.
85aaf69f 173#[derive(Clone, PartialEq, Eq, Debug)]
1a4d82fc
JJ
174enum Optval {
175 Val(String),
176 Given,
177}
178
179/// The result of checking command line arguments. Contains a vector
180/// of matches and a vector of free strings.
85aaf69f 181#[derive(Clone, PartialEq, Eq, Debug)]
1a4d82fc
JJ
182pub struct Matches {
183 /// Options that matched
184 opts: Vec<Opt>,
185 /// Values of the Options that matched
186 vals: Vec<Vec<Optval>>,
187 /// Free string fragments
188 pub free: Vec<String>,
189}
190
191/// The type returned when the command line does not conform to the
85aaf69f 192/// expected format. Use the `Debug` implementation to output detailed
1a4d82fc 193/// information.
85aaf69f 194#[derive(Clone, PartialEq, Eq, Debug)]
1a4d82fc
JJ
195pub enum Fail {
196 /// The option requires an argument but none was passed.
197 ArgumentMissing(String),
198 /// The passed option is not declared among the possible options.
199 UnrecognizedOption(String),
200 /// A required option is not present.
201 OptionMissing(String),
202 /// A single occurrence option is being used multiple times.
203 OptionDuplicated(String),
204 /// There's an argument being passed to a non-argument option.
205 UnexpectedArgument(String),
206}
207
208/// The type of failure that occurred.
c34b1796 209#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1a4d82fc
JJ
210#[allow(missing_docs)]
211pub enum FailType {
212 ArgumentMissing_,
213 UnrecognizedOption_,
214 OptionMissing_,
215 OptionDuplicated_,
216 UnexpectedArgument_,
217}
218
219/// The result of parsing a command line with a set of options.
220pub type Result = result::Result<Matches, Fail>;
221
222impl Name {
223 fn from_str(nm: &str) -> Name {
85aaf69f 224 if nm.len() == 1 {
54a0048b 225 Short(nm.chars().next().unwrap())
1a4d82fc 226 } else {
e9174d1e 227 Long(nm.to_owned())
1a4d82fc
JJ
228 }
229 }
230
231 fn to_string(&self) -> String {
232 match *self {
233 Short(ch) => ch.to_string(),
92a42be0 234 Long(ref s) => s.to_owned(),
1a4d82fc
JJ
235 }
236 }
237}
238
239impl OptGroup {
240 /// Translate OptGroup into Opt.
241 /// (Both short and long names correspond to different Opts).
242 pub fn long_to_short(&self) -> Opt {
243 let OptGroup {
244 short_name,
245 long_name,
246 hasarg,
247 occur,
248 ..
249 } = (*self).clone();
250
251 match (short_name.len(), long_name.len()) {
92a42be0
SL
252 (0, 0) => panic!("this long-format option was given no name"),
253 (0, _) => {
254 Opt {
255 name: Long((long_name)),
256 hasarg: hasarg,
257 occur: occur,
258 aliases: Vec::new(),
259 }
260 }
261 (1, 0) => {
262 Opt {
54a0048b 263 name: Short(short_name.chars().next().unwrap()),
92a42be0
SL
264 hasarg: hasarg,
265 occur: occur,
266 aliases: Vec::new(),
267 }
268 }
269 (1, _) => {
270 Opt {
271 name: Long((long_name)),
272 hasarg: hasarg,
273 occur: occur,
274 aliases: vec![Opt {
54a0048b 275 name: Short(short_name.chars().next().unwrap()),
92a42be0
SL
276 hasarg: hasarg,
277 occur: occur,
278 aliases: Vec::new(),
279 }],
280 }
281 }
282 (_, _) => panic!("something is wrong with the long-form opt"),
1a4d82fc
JJ
283 }
284 }
285}
286
287impl Matches {
288 fn opt_vals(&self, nm: &str) -> Vec<Optval> {
85aaf69f 289 match find_opt(&self.opts[..], Name::from_str(nm)) {
1a4d82fc 290 Some(id) => self.vals[id].clone(),
92a42be0 291 None => panic!("No option '{}' defined", nm),
1a4d82fc
JJ
292 }
293 }
294
295 fn opt_val(&self, nm: &str) -> Option<Optval> {
296 let vals = self.opt_vals(nm);
297 if vals.is_empty() {
298 None
299 } else {
300 Some(vals[0].clone())
301 }
302 }
303
304 /// Returns true if an option was matched.
305 pub fn opt_present(&self, nm: &str) -> bool {
306 !self.opt_vals(nm).is_empty()
307 }
308
309 /// Returns the number of times an option was matched.
c34b1796 310 pub fn opt_count(&self, nm: &str) -> usize {
1a4d82fc
JJ
311 self.opt_vals(nm).len()
312 }
313
314 /// Returns true if any of several options were matched.
315 pub fn opts_present(&self, names: &[String]) -> bool {
85aaf69f
SL
316 for nm in names {
317 match find_opt(&self.opts, Name::from_str(&**nm)) {
1a4d82fc
JJ
318 Some(id) if !self.vals[id].is_empty() => return true,
319 _ => (),
320 };
321 }
322 false
323 }
324
325 /// Returns the string argument supplied to one of several matching options or `None`.
326 pub fn opts_str(&self, names: &[String]) -> Option<String> {
85aaf69f 327 for nm in names {
7453a54e
SL
328 if let Some(Val(ref s)) = self.opt_val(&nm[..]) {
329 return Some(s.clone())
1a4d82fc
JJ
330 }
331 }
332 None
333 }
334
335 /// Returns a vector of the arguments provided to all matches of the given
336 /// option.
337 ///
338 /// Used when an option accepts multiple values.
339 pub fn opt_strs(&self, nm: &str) -> Vec<String> {
340 let mut acc: Vec<String> = Vec::new();
341 let r = self.opt_vals(nm);
85aaf69f 342 for v in &r {
1a4d82fc
JJ
343 match *v {
344 Val(ref s) => acc.push((*s).clone()),
92a42be0 345 _ => (),
1a4d82fc
JJ
346 }
347 }
348 acc
349 }
350
351 /// Returns the string argument supplied to a matching option or `None`.
352 pub fn opt_str(&self, nm: &str) -> Option<String> {
353 let vals = self.opt_vals(nm);
354 if vals.is_empty() {
355 return None::<String>;
356 }
357 match vals[0] {
358 Val(ref s) => Some((*s).clone()),
92a42be0 359 _ => None,
1a4d82fc
JJ
360 }
361 }
362
363
364 /// Returns the matching string, a default, or none.
365 ///
366 /// Returns none if the option was not present, `def` if the option was
367 /// present but no argument was provided, and the argument if the option was
368 /// present and an argument was provided.
369 pub fn opt_default(&self, nm: &str, def: &str) -> Option<String> {
370 let vals = self.opt_vals(nm);
371 if vals.is_empty() {
372 None
373 } else {
374 match vals[0] {
375 Val(ref s) => Some((*s).clone()),
92a42be0 376 _ => Some(def.to_owned()),
1a4d82fc
JJ
377 }
378 }
379 }
1a4d82fc
JJ
380}
381
382fn is_arg(arg: &str) -> bool {
383 arg.len() > 1 && arg.as_bytes()[0] == b'-'
384}
385
c34b1796 386fn find_opt(opts: &[Opt], nm: Name) -> Option<usize> {
1a4d82fc
JJ
387 // Search main options.
388 let pos = opts.iter().position(|opt| opt.name == nm);
389 if pos.is_some() {
92a42be0 390 return pos;
1a4d82fc
JJ
391 }
392
393 // Search in aliases.
85aaf69f 394 for candidate in opts {
1a4d82fc
JJ
395 if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() {
396 return opts.iter().position(|opt| opt.name == candidate.name);
397 }
398 }
399
400 None
401}
402
403/// Create a long option that is required and takes an argument.
404///
405/// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
406/// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
407/// * `desc` - Description for usage help
408/// * `hint` - Hint that is used in place of the argument in the usage help,
409/// e.g. `"FILE"` for a `-o FILE` option
410pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
411 let len = short_name.len();
412 assert!(len == 1 || len == 0);
413 OptGroup {
e9174d1e
SL
414 short_name: short_name.to_owned(),
415 long_name: long_name.to_owned(),
416 hint: hint.to_owned(),
417 desc: desc.to_owned(),
1a4d82fc 418 hasarg: Yes,
92a42be0 419 occur: Req,
1a4d82fc
JJ
420 }
421}
422
423/// Create a long option that is optional and takes an argument.
424///
425/// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
426/// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
427/// * `desc` - Description for usage help
428/// * `hint` - Hint that is used in place of the argument in the usage help,
429/// e.g. `"FILE"` for a `-o FILE` option
430pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
431 let len = short_name.len();
432 assert!(len == 1 || len == 0);
433 OptGroup {
e9174d1e
SL
434 short_name: short_name.to_owned(),
435 long_name: long_name.to_owned(),
436 hint: hint.to_owned(),
437 desc: desc.to_owned(),
1a4d82fc 438 hasarg: Yes,
92a42be0 439 occur: Optional,
1a4d82fc
JJ
440 }
441}
442
443/// Create a long option that is optional and does not take an argument.
444///
445/// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
446/// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
447/// * `desc` - Description for usage help
448pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
449 let len = short_name.len();
450 assert!(len == 1 || len == 0);
451 OptGroup {
e9174d1e
SL
452 short_name: short_name.to_owned(),
453 long_name: long_name.to_owned(),
454 hint: "".to_owned(),
455 desc: desc.to_owned(),
1a4d82fc 456 hasarg: No,
92a42be0 457 occur: Optional,
1a4d82fc
JJ
458 }
459}
460
461/// Create a long option that can occur more than once and does not
462/// take an argument.
463///
464/// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
465/// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
466/// * `desc` - Description for usage help
467pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup {
468 let len = short_name.len();
469 assert!(len == 1 || len == 0);
470 OptGroup {
e9174d1e
SL
471 short_name: short_name.to_owned(),
472 long_name: long_name.to_owned(),
473 hint: "".to_owned(),
474 desc: desc.to_owned(),
1a4d82fc 475 hasarg: No,
92a42be0 476 occur: Multi,
1a4d82fc
JJ
477 }
478}
479
480/// Create a long option that is optional and takes an optional argument.
481///
482/// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
483/// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
484/// * `desc` - Description for usage help
485/// * `hint` - Hint that is used in place of the argument in the usage help,
486/// e.g. `"FILE"` for a `-o FILE` option
487pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
488 let len = short_name.len();
489 assert!(len == 1 || len == 0);
490 OptGroup {
e9174d1e
SL
491 short_name: short_name.to_owned(),
492 long_name: long_name.to_owned(),
493 hint: hint.to_owned(),
494 desc: desc.to_owned(),
1a4d82fc 495 hasarg: Maybe,
92a42be0 496 occur: Optional,
1a4d82fc
JJ
497 }
498}
499
500/// Create a long option that is optional, takes an argument, and may occur
501/// multiple times.
502///
503/// * `short_name` - e.g. `"h"` for a `-h` option, or `""` for none
504/// * `long_name` - e.g. `"help"` for a `--help` option, or `""` for none
505/// * `desc` - Description for usage help
506/// * `hint` - Hint that is used in place of the argument in the usage help,
507/// e.g. `"FILE"` for a `-o FILE` option
508pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup {
509 let len = short_name.len();
510 assert!(len == 1 || len == 0);
511 OptGroup {
e9174d1e
SL
512 short_name: short_name.to_owned(),
513 long_name: long_name.to_owned(),
514 hint: hint.to_owned(),
515 desc: desc.to_owned(),
1a4d82fc 516 hasarg: Yes,
92a42be0 517 occur: Multi,
1a4d82fc
JJ
518 }
519}
520
521/// Create a generic option group, stating all parameters explicitly
522pub fn opt(short_name: &str,
523 long_name: &str,
524 desc: &str,
525 hint: &str,
526 hasarg: HasArg,
92a42be0
SL
527 occur: Occur)
528 -> OptGroup {
1a4d82fc
JJ
529 let len = short_name.len();
530 assert!(len == 1 || len == 0);
531 OptGroup {
e9174d1e
SL
532 short_name: short_name.to_owned(),
533 long_name: long_name.to_owned(),
534 hint: hint.to_owned(),
535 desc: desc.to_owned(),
1a4d82fc 536 hasarg: hasarg,
92a42be0 537 occur: occur,
1a4d82fc
JJ
538 }
539}
540
85aaf69f 541impl fmt::Display for Fail {
1a4d82fc
JJ
542 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
543 match *self {
92a42be0
SL
544 ArgumentMissing(ref nm) => write!(f, "Argument to option '{}' missing.", *nm),
545 UnrecognizedOption(ref nm) => write!(f, "Unrecognized option: '{}'.", *nm),
546 OptionMissing(ref nm) => write!(f, "Required option '{}' missing.", *nm),
547 OptionDuplicated(ref nm) => write!(f, "Option '{}' given more than once.", *nm),
548 UnexpectedArgument(ref nm) => write!(f, "Option '{}' does not take an argument.", *nm),
1a4d82fc
JJ
549 }
550 }
551}
552
553/// Parse command line arguments according to the provided options.
554///
555/// On success returns `Ok(Matches)`. Use methods such as `opt_present`
556/// `opt_str`, etc. to interrogate results.
557/// # Panics
558///
85aaf69f 559/// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` to display
1a4d82fc
JJ
560/// information about it.
561pub fn getopts(args: &[String], optgrps: &[OptGroup]) -> Result {
562 let opts: Vec<Opt> = optgrps.iter().map(|x| x.long_to_short()).collect();
563 let n_opts = opts.len();
564
92a42be0
SL
565 fn f(_x: usize) -> Vec<Optval> {
566 Vec::new()
567 }
1a4d82fc 568
85aaf69f 569 let mut vals: Vec<_> = (0..n_opts).map(f).collect();
1a4d82fc
JJ
570 let mut free: Vec<String> = Vec::new();
571 let l = args.len();
572 let mut i = 0;
573 while i < l {
574 let cur = args[i].clone();
575 let curlen = cur.len();
85aaf69f 576 if !is_arg(&cur[..]) {
1a4d82fc
JJ
577 free.push(cur);
578 } else if cur == "--" {
579 let mut j = i + 1;
92a42be0
SL
580 while j < l {
581 free.push(args[j].clone());
582 j += 1;
583 }
1a4d82fc
JJ
584 break;
585 } else {
586 let mut names;
587 let mut i_arg = None;
588 if cur.as_bytes()[1] == b'-' {
589 let tail = &cur[2..curlen];
92a42be0 590 let tail_eq: Vec<&str> = tail.splitn(2, '=').collect();
1a4d82fc 591 if tail_eq.len() <= 1 {
92a42be0 592 names = vec![Long(tail.to_owned())];
1a4d82fc 593 } else {
92a42be0 594 names = vec![Long(tail_eq[0].to_owned())];
e9174d1e 595 i_arg = Some(tail_eq[1].to_owned());
1a4d82fc
JJ
596 }
597 } else {
598 let mut j = 1;
599 names = Vec::new();
600 while j < curlen {
54a0048b 601 let ch = cur[j..].chars().next().unwrap();
c34b1796 602 let opt = Short(ch);
1a4d82fc 603
92a42be0
SL
604 // In a series of potential options (eg. -aheJ), if we
605 // see one which takes an argument, we assume all
606 // subsequent characters make up the argument. This
607 // allows options such as -L/usr/local/lib/foo to be
608 // interpreted correctly
1a4d82fc 609
85aaf69f 610 let opt_id = match find_opt(&opts, opt.clone()) {
92a42be0
SL
611 Some(id) => id,
612 None => return Err(UnrecognizedOption(opt.to_string())),
1a4d82fc
JJ
613 };
614
615 names.push(opt);
616
617 let arg_follows = match opts[opt_id].hasarg {
618 Yes | Maybe => true,
92a42be0 619 No => false,
1a4d82fc
JJ
620 };
621
c34b1796
AL
622 let next = j + ch.len_utf8();
623 if arg_follows && next < curlen {
e9174d1e 624 i_arg = Some((&cur[next..curlen]).to_owned());
1a4d82fc
JJ
625 break;
626 }
627
c34b1796 628 j = next;
1a4d82fc
JJ
629 }
630 }
631 let mut name_pos = 0;
85aaf69f 632 for nm in &names {
1a4d82fc 633 name_pos += 1;
85aaf69f 634 let optid = match find_opt(&opts, (*nm).clone()) {
92a42be0
SL
635 Some(id) => id,
636 None => return Err(UnrecognizedOption(nm.to_string())),
1a4d82fc
JJ
637 };
638 match opts[optid].hasarg {
92a42be0
SL
639 No => {
640 if name_pos == names.len() && !i_arg.is_none() {
641 return Err(UnexpectedArgument(nm.to_string()));
642 }
1a4d82fc
JJ
643 let v = &mut vals[optid];
644 v.push(Given);
1a4d82fc 645 }
92a42be0
SL
646 Maybe => {
647 if !i_arg.is_none() {
648 let v = &mut vals[optid];
649 v.push(Val((i_arg.clone()).unwrap()));
650 } else if name_pos < names.len() || i + 1 == l || is_arg(&args[i + 1][..]) {
651 let v = &mut vals[optid];
652 v.push(Given);
653 } else {
654 i += 1;
655 let v = &mut vals[optid];
656 v.push(Val(args[i].clone()));
657 }
658 }
659 Yes => {
660 if !i_arg.is_none() {
661 let v = &mut vals[optid];
662 v.push(Val(i_arg.clone().unwrap()));
663 } else if i + 1 == l {
664 return Err(ArgumentMissing(nm.to_string()));
665 } else {
666 i += 1;
667 let v = &mut vals[optid];
668 v.push(Val(args[i].clone()));
669 }
1a4d82fc 670 }
1a4d82fc
JJ
671 }
672 }
673 }
674 i += 1;
675 }
85aaf69f 676 for i in 0..n_opts {
1a4d82fc
JJ
677 let n = vals[i].len();
678 let occ = opts[i].occur;
679 if occ == Req && n == 0 {
680 return Err(OptionMissing(opts[i].name.to_string()));
681 }
682 if occ != Multi && n > 1 {
683 return Err(OptionDuplicated(opts[i].name.to_string()));
684 }
685 }
686 Ok(Matches {
687 opts: opts,
688 vals: vals,
92a42be0 689 free: free,
1a4d82fc
JJ
690 })
691}
692
693/// Derive a usage message from a set of long options.
694pub fn usage(brief: &str, opts: &[OptGroup]) -> String {
695
696 let desc_sep = format!("\n{}", repeat(" ").take(24).collect::<String>());
697
698 let rows = opts.iter().map(|optref| {
699 let OptGroup{short_name,
700 long_name,
701 hint,
702 desc,
703 hasarg,
704 ..} = (*optref).clone();
705
706 let mut row = repeat(" ").take(4).collect::<String>();
707
708 // short option
709 match short_name.len() {
710 0 => {}
711 1 => {
712 row.push('-');
85aaf69f 713 row.push_str(&short_name[..]);
1a4d82fc
JJ
714 row.push(' ');
715 }
716 _ => panic!("the short name should only be 1 ascii char long"),
717 }
718
719 // long option
720 match long_name.len() {
721 0 => {}
722 _ => {
723 row.push_str("--");
85aaf69f 724 row.push_str(&long_name[..]);
1a4d82fc
JJ
725 row.push(' ');
726 }
727 }
728
729 // arg
730 match hasarg {
731 No => {}
85aaf69f 732 Yes => row.push_str(&hint[..]),
1a4d82fc
JJ
733 Maybe => {
734 row.push('[');
85aaf69f 735 row.push_str(&hint[..]);
1a4d82fc
JJ
736 row.push(']');
737 }
738 }
739
740 // FIXME: #5516 should be graphemes not codepoints
741 // here we just need to indent the start of the description
742 let rowlen = row.chars().count();
743 if rowlen < 24 {
85aaf69f 744 for _ in 0..24 - rowlen {
1a4d82fc
JJ
745 row.push(' ');
746 }
747 } else {
85aaf69f 748 row.push_str(&desc_sep[..]);
1a4d82fc
JJ
749 }
750
751 // Normalize desc to contain words separated by one space character
752 let mut desc_normalized_whitespace = String::new();
d9579d0f 753 for word in desc.split_whitespace() {
1a4d82fc
JJ
754 desc_normalized_whitespace.push_str(word);
755 desc_normalized_whitespace.push(' ');
756 }
757
758 // FIXME: #5516 should be graphemes not codepoints
759 let mut desc_rows = Vec::new();
85aaf69f 760 each_split_within(&desc_normalized_whitespace[..], 54, |substr| {
e9174d1e 761 desc_rows.push(substr.to_owned());
1a4d82fc
JJ
762 true
763 });
764
765 // FIXME: #5516 should be graphemes not codepoints
766 // wrapped description
c1a9b12d 767 row.push_str(&desc_rows.join(&desc_sep[..]));
1a4d82fc
JJ
768
769 row
770 });
771
92a42be0
SL
772 format!("{}\n\nOptions:\n{}\n",
773 brief,
c1a9b12d 774 rows.collect::<Vec<String>>().join("\n"))
1a4d82fc
JJ
775}
776
777fn format_option(opt: &OptGroup) -> String {
778 let mut line = String::new();
779
780 if opt.occur != Req {
781 line.push('[');
782 }
783
784 // Use short_name is possible, but fallback to long_name.
9346a6ac 785 if !opt.short_name.is_empty() {
1a4d82fc 786 line.push('-');
85aaf69f 787 line.push_str(&opt.short_name[..]);
1a4d82fc
JJ
788 } else {
789 line.push_str("--");
85aaf69f 790 line.push_str(&opt.long_name[..]);
1a4d82fc
JJ
791 }
792
793 if opt.hasarg != No {
794 line.push(' ');
795 if opt.hasarg == Maybe {
796 line.push('[');
797 }
85aaf69f 798 line.push_str(&opt.hint[..]);
1a4d82fc
JJ
799 if opt.hasarg == Maybe {
800 line.push(']');
801 }
802 }
803
804 if opt.occur != Req {
805 line.push(']');
806 }
807 if opt.occur == Multi {
808 line.push_str("..");
809 }
810
811 line
812}
813
814/// Derive a short one-line usage summary from a set of long options.
815pub fn short_usage(program_name: &str, opts: &[OptGroup]) -> String {
816 let mut line = format!("Usage: {} ", program_name);
817 line.push_str(&opts.iter()
818 .map(format_option)
819 .collect::<Vec<String>>()
c1a9b12d 820 .join(" ")[..]);
1a4d82fc
JJ
821 line
822}
823
c34b1796 824#[derive(Copy, Clone)]
1a4d82fc 825enum SplitWithinState {
92a42be0
SL
826 A, // leading whitespace, initial state
827 B, // words
828 C, // internal and trailing whitespace
1a4d82fc 829}
c34b1796 830#[derive(Copy, Clone)]
1a4d82fc
JJ
831enum Whitespace {
832 Ws, // current char is whitespace
92a42be0 833 Cr, // current char is not whitespace
1a4d82fc 834}
c34b1796 835#[derive(Copy, Clone)]
1a4d82fc
JJ
836enum LengthLimit {
837 UnderLim, // current char makes current substring still fit in limit
92a42be0 838 OverLim, // current char makes current substring no longer fit in limit
1a4d82fc
JJ
839}
840
841
842/// Splits a string into substrings with possibly internal whitespace,
843/// each of them at most `lim` bytes long. The substrings have leading and trailing
844/// whitespace removed, and are only cut at whitespace boundaries.
845///
846/// Note: Function was moved here from `std::str` because this module is the only place that
847/// uses it, and because it was too specific for a general string function.
848///
849/// # Panics
850///
851/// Panics during iteration if the string contains a non-whitespace
852/// sequence longer than the limit.
92a42be0
SL
853fn each_split_within<F>(ss: &str, lim: usize, mut it: F) -> bool
854 where F: FnMut(&str) -> bool
1a4d82fc
JJ
855{
856 // Just for fun, let's write this as a state machine:
857
858 let mut slice_start = 0;
859 let mut last_start = 0;
860 let mut last_end = 0;
861 let mut state = A;
862 let mut fake_i = ss.len();
863 let mut lim = lim;
864
865 let mut cont = true;
866
867 // if the limit is larger than the string, lower it to save cycles
868 if lim >= fake_i {
869 lim = fake_i;
870 }
871
c34b1796 872 let mut machine = |cont: &mut bool, (i, c): (usize, char)| -> bool {
92a42be0
SL
873 let whitespace = if c.is_whitespace() {
874 Ws
875 } else {
876 Cr
877 };
878 let limit = if (i - slice_start + 1) <= lim {
879 UnderLim
880 } else {
881 OverLim
882 };
1a4d82fc
JJ
883
884 state = match (state, whitespace, limit) {
92a42be0
SL
885 (A, Ws, _) => A,
886 (A, Cr, _) => {
887 slice_start = i;
888 last_start = i;
889 B
890 }
891
892 (B, Cr, UnderLim) => B,
893 (B, Cr, OverLim) if (i - last_start + 1) > lim => {
894 panic!("word starting with {} longer than limit!",
895 &ss[last_start..i + 1])
896 }
897 (B, Cr, OverLim) => {
1a4d82fc
JJ
898 *cont = it(&ss[slice_start..last_end]);
899 slice_start = last_start;
900 B
901 }
902 (B, Ws, UnderLim) => {
903 last_end = i;
904 C
905 }
92a42be0 906 (B, Ws, OverLim) => {
1a4d82fc
JJ
907 last_end = i;
908 *cont = it(&ss[slice_start..last_end]);
909 A
910 }
911
912 (C, Cr, UnderLim) => {
913 last_start = i;
914 B
915 }
92a42be0 916 (C, Cr, OverLim) => {
1a4d82fc
JJ
917 *cont = it(&ss[slice_start..last_end]);
918 slice_start = i;
919 last_start = i;
920 last_end = i;
921 B
922 }
92a42be0 923 (C, Ws, OverLim) => {
1a4d82fc
JJ
924 *cont = it(&ss[slice_start..last_end]);
925 A
926 }
92a42be0 927 (C, Ws, UnderLim) => C,
1a4d82fc
JJ
928 };
929
930 *cont
931 };
932
933 ss.char_indices().all(|x| machine(&mut cont, x));
934
935 // Let the automaton 'run out' by supplying trailing whitespace
92a42be0
SL
936 while cont &&
937 match state {
938 B | C => true,
939 A => false,
940 } {
1a4d82fc
JJ
941 machine(&mut cont, (fake_i, ' '));
942 fake_i += 1;
943 }
e9174d1e 944 cont
1a4d82fc
JJ
945}
946
947#[test]
948fn test_split_within() {
c34b1796 949 fn t(s: &str, i: usize, u: &[String]) {
1a4d82fc 950 let mut v = Vec::new();
92a42be0
SL
951 each_split_within(s, i, |s| {
952 v.push(s.to_string());
953 true
954 });
955 assert!(v.iter().zip(u).all(|(a, b)| a == b));
1a4d82fc
JJ
956 }
957 t("", 0, &[]);
958 t("", 15, &[]);
959 t("hello", 15, &["hello".to_string()]);
92a42be0
SL
960 t("\nMary had a little lamb\nLittle lamb\n",
961 15,
962 &["Mary had a".to_string(), "little lamb".to_string(), "Little lamb".to_string()]);
963 t("\nMary had a little lamb\nLittle lamb\n",
964 ::std::usize::MAX,
965 &["Mary had a little lamb\nLittle lamb".to_string()]);
1a4d82fc
JJ
966}
967
968#[cfg(test)]
969mod tests {
970 use super::*;
971 use super::Fail::*;
972
973 use std::result::Result::{Err, Ok};
974 use std::result;
975
976 // Tests for reqopt
977 #[test]
978 fn test_reqopt() {
92a42be0
SL
979 let long_args = vec!["--test=20".to_string()];
980 let opts = vec![reqopt("t", "test", "testing", "TEST")];
85aaf69f 981 let rs = getopts(&long_args, &opts);
1a4d82fc 982 match rs {
92a42be0
SL
983 Ok(ref m) => {
984 assert!(m.opt_present("test"));
985 assert_eq!(m.opt_str("test").unwrap(), "20");
986 assert!(m.opt_present("t"));
987 assert_eq!(m.opt_str("t").unwrap(), "20");
988 }
989 _ => {
990 panic!("test_reqopt failed (long arg)");
991 }
1a4d82fc 992 }
92a42be0 993 let short_args = vec!["-t".to_string(), "20".to_string()];
85aaf69f 994 match getopts(&short_args, &opts) {
92a42be0
SL
995 Ok(ref m) => {
996 assert!((m.opt_present("test")));
997 assert_eq!(m.opt_str("test").unwrap(), "20");
998 assert!((m.opt_present("t")));
999 assert_eq!(m.opt_str("t").unwrap(), "20");
1000 }
1001 _ => {
1002 panic!("test_reqopt failed (short arg)");
1003 }
1a4d82fc
JJ
1004 }
1005 }
1006
1007 #[test]
1008 fn test_reqopt_missing() {
92a42be0
SL
1009 let args = vec!["blah".to_string()];
1010 let opts = vec![reqopt("t", "test", "testing", "TEST")];
85aaf69f 1011 let rs = getopts(&args, &opts);
1a4d82fc 1012 match rs {
92a42be0
SL
1013 Err(OptionMissing(_)) => {}
1014 _ => panic!(),
1a4d82fc
JJ
1015 }
1016 }
1017
1018 #[test]
1019 fn test_reqopt_no_arg() {
92a42be0
SL
1020 let long_args = vec!["--test".to_string()];
1021 let opts = vec![reqopt("t", "test", "testing", "TEST")];
85aaf69f 1022 let rs = getopts(&long_args, &opts);
1a4d82fc 1023 match rs {
92a42be0
SL
1024 Err(ArgumentMissing(_)) => {}
1025 _ => panic!(),
1a4d82fc 1026 }
92a42be0 1027 let short_args = vec!["-t".to_string()];
85aaf69f 1028 match getopts(&short_args, &opts) {
92a42be0
SL
1029 Err(ArgumentMissing(_)) => {}
1030 _ => panic!(),
1a4d82fc
JJ
1031 }
1032 }
1033
1034 #[test]
1035 fn test_reqopt_multi() {
92a42be0
SL
1036 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1037 let opts = vec![reqopt("t", "test", "testing", "TEST")];
85aaf69f 1038 let rs = getopts(&args, &opts);
1a4d82fc 1039 match rs {
92a42be0
SL
1040 Err(OptionDuplicated(_)) => {}
1041 _ => panic!(),
1a4d82fc
JJ
1042 }
1043 }
1044
1045 // Tests for optopt
1046 #[test]
1047 fn test_optopt() {
92a42be0
SL
1048 let long_args = vec!["--test=20".to_string()];
1049 let opts = vec![optopt("t", "test", "testing", "TEST")];
85aaf69f 1050 let rs = getopts(&long_args, &opts);
1a4d82fc 1051 match rs {
92a42be0
SL
1052 Ok(ref m) => {
1053 assert!(m.opt_present("test"));
1054 assert_eq!(m.opt_str("test").unwrap(), "20");
1055 assert!((m.opt_present("t")));
1056 assert_eq!(m.opt_str("t").unwrap(), "20");
1057 }
1058 _ => panic!(),
1a4d82fc 1059 }
92a42be0 1060 let short_args = vec!["-t".to_string(), "20".to_string()];
85aaf69f 1061 match getopts(&short_args, &opts) {
92a42be0
SL
1062 Ok(ref m) => {
1063 assert!((m.opt_present("test")));
1064 assert_eq!(m.opt_str("test").unwrap(), "20");
1065 assert!((m.opt_present("t")));
1066 assert_eq!(m.opt_str("t").unwrap(), "20");
1067 }
1068 _ => panic!(),
1a4d82fc
JJ
1069 }
1070 }
1071
1072 #[test]
1073 fn test_optopt_missing() {
92a42be0
SL
1074 let args = vec!["blah".to_string()];
1075 let opts = vec![optopt("t", "test", "testing", "TEST")];
85aaf69f 1076 let rs = getopts(&args, &opts);
1a4d82fc 1077 match rs {
92a42be0
SL
1078 Ok(ref m) => {
1079 assert!(!m.opt_present("test"));
1080 assert!(!m.opt_present("t"));
1081 }
1082 _ => panic!(),
1a4d82fc
JJ
1083 }
1084 }
1085
1086 #[test]
1087 fn test_optopt_no_arg() {
92a42be0
SL
1088 let long_args = vec!["--test".to_string()];
1089 let opts = vec![optopt("t", "test", "testing", "TEST")];
85aaf69f 1090 let rs = getopts(&long_args, &opts);
1a4d82fc 1091 match rs {
92a42be0
SL
1092 Err(ArgumentMissing(_)) => {}
1093 _ => panic!(),
1a4d82fc 1094 }
92a42be0 1095 let short_args = vec!["-t".to_string()];
85aaf69f 1096 match getopts(&short_args, &opts) {
92a42be0
SL
1097 Err(ArgumentMissing(_)) => {}
1098 _ => panic!(),
1a4d82fc
JJ
1099 }
1100 }
1101
1102 #[test]
1103 fn test_optopt_multi() {
92a42be0
SL
1104 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1105 let opts = vec![optopt("t", "test", "testing", "TEST")];
85aaf69f 1106 let rs = getopts(&args, &opts);
1a4d82fc 1107 match rs {
92a42be0
SL
1108 Err(OptionDuplicated(_)) => {}
1109 _ => panic!(),
1a4d82fc
JJ
1110 }
1111 }
1112
1113 // Tests for optflag
1114 #[test]
1115 fn test_optflag() {
92a42be0
SL
1116 let long_args = vec!["--test".to_string()];
1117 let opts = vec![optflag("t", "test", "testing")];
85aaf69f 1118 let rs = getopts(&long_args, &opts);
1a4d82fc 1119 match rs {
92a42be0
SL
1120 Ok(ref m) => {
1121 assert!(m.opt_present("test"));
1122 assert!(m.opt_present("t"));
1123 }
1124 _ => panic!(),
1a4d82fc 1125 }
92a42be0 1126 let short_args = vec!["-t".to_string()];
85aaf69f 1127 match getopts(&short_args, &opts) {
92a42be0
SL
1128 Ok(ref m) => {
1129 assert!(m.opt_present("test"));
1130 assert!(m.opt_present("t"));
1131 }
1132 _ => panic!(),
1a4d82fc
JJ
1133 }
1134 }
1135
1136 #[test]
1137 fn test_optflag_missing() {
92a42be0
SL
1138 let args = vec!["blah".to_string()];
1139 let opts = vec![optflag("t", "test", "testing")];
85aaf69f 1140 let rs = getopts(&args, &opts);
1a4d82fc 1141 match rs {
92a42be0
SL
1142 Ok(ref m) => {
1143 assert!(!m.opt_present("test"));
1144 assert!(!m.opt_present("t"));
1145 }
1146 _ => panic!(),
1a4d82fc
JJ
1147 }
1148 }
1149
1150 #[test]
1151 fn test_optflag_long_arg() {
92a42be0
SL
1152 let args = vec!["--test=20".to_string()];
1153 let opts = vec![optflag("t", "test", "testing")];
85aaf69f 1154 let rs = getopts(&args, &opts);
1a4d82fc 1155 match rs {
92a42be0
SL
1156 Err(UnexpectedArgument(_)) => {}
1157 _ => panic!(),
1a4d82fc
JJ
1158 }
1159 }
1160
1161 #[test]
1162 fn test_optflag_multi() {
92a42be0
SL
1163 let args = vec!["--test".to_string(), "-t".to_string()];
1164 let opts = vec![optflag("t", "test", "testing")];
85aaf69f 1165 let rs = getopts(&args, &opts);
1a4d82fc 1166 match rs {
92a42be0
SL
1167 Err(OptionDuplicated(_)) => {}
1168 _ => panic!(),
1a4d82fc
JJ
1169 }
1170 }
1171
1172 #[test]
1173 fn test_optflag_short_arg() {
92a42be0
SL
1174 let args = vec!["-t".to_string(), "20".to_string()];
1175 let opts = vec![optflag("t", "test", "testing")];
85aaf69f 1176 let rs = getopts(&args, &opts);
1a4d82fc 1177 match rs {
92a42be0
SL
1178 Ok(ref m) => {
1179 // The next variable after the flag is just a free argument
1a4d82fc 1180
92a42be0
SL
1181 assert!(m.free[0] == "20");
1182 }
1183 _ => panic!(),
1a4d82fc
JJ
1184 }
1185 }
1186
1187 // Tests for optflagmulti
1188 #[test]
1189 fn test_optflagmulti_short1() {
92a42be0
SL
1190 let args = vec!["-v".to_string()];
1191 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
85aaf69f 1192 let rs = getopts(&args, &opts);
1a4d82fc 1193 match rs {
92a42be0
SL
1194 Ok(ref m) => {
1195 assert_eq!(m.opt_count("v"), 1);
1196 }
1197 _ => panic!(),
1a4d82fc
JJ
1198 }
1199 }
1200
1201 #[test]
1202 fn test_optflagmulti_short2a() {
92a42be0
SL
1203 let args = vec!["-v".to_string(), "-v".to_string()];
1204 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
85aaf69f 1205 let rs = getopts(&args, &opts);
1a4d82fc 1206 match rs {
92a42be0
SL
1207 Ok(ref m) => {
1208 assert_eq!(m.opt_count("v"), 2);
1209 }
1210 _ => panic!(),
1a4d82fc
JJ
1211 }
1212 }
1213
1214 #[test]
1215 fn test_optflagmulti_short2b() {
92a42be0
SL
1216 let args = vec!["-vv".to_string()];
1217 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
85aaf69f 1218 let rs = getopts(&args, &opts);
1a4d82fc 1219 match rs {
92a42be0
SL
1220 Ok(ref m) => {
1221 assert_eq!(m.opt_count("v"), 2);
1222 }
1223 _ => panic!(),
1a4d82fc
JJ
1224 }
1225 }
1226
1227 #[test]
1228 fn test_optflagmulti_long1() {
92a42be0
SL
1229 let args = vec!["--verbose".to_string()];
1230 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
85aaf69f 1231 let rs = getopts(&args, &opts);
1a4d82fc 1232 match rs {
92a42be0
SL
1233 Ok(ref m) => {
1234 assert_eq!(m.opt_count("verbose"), 1);
1235 }
1236 _ => panic!(),
1a4d82fc
JJ
1237 }
1238 }
1239
1240 #[test]
1241 fn test_optflagmulti_long2() {
92a42be0
SL
1242 let args = vec!["--verbose".to_string(), "--verbose".to_string()];
1243 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
85aaf69f 1244 let rs = getopts(&args, &opts);
1a4d82fc 1245 match rs {
92a42be0
SL
1246 Ok(ref m) => {
1247 assert_eq!(m.opt_count("verbose"), 2);
1248 }
1249 _ => panic!(),
1a4d82fc
JJ
1250 }
1251 }
1252
1253 #[test]
1254 fn test_optflagmulti_mix() {
92a42be0
SL
1255 let args = vec!["--verbose".to_string(),
1256 "-v".to_string(),
1257 "-vv".to_string(),
1258 "verbose".to_string()];
1259 let opts = vec![optflagmulti("v", "verbose", "verbosity")];
85aaf69f 1260 let rs = getopts(&args, &opts);
1a4d82fc 1261 match rs {
92a42be0
SL
1262 Ok(ref m) => {
1263 assert_eq!(m.opt_count("verbose"), 4);
1264 assert_eq!(m.opt_count("v"), 4);
1265 }
1266 _ => panic!(),
1a4d82fc
JJ
1267 }
1268 }
1269
1270 // Tests for optmulti
1271 #[test]
1272 fn test_optmulti() {
92a42be0
SL
1273 let long_args = vec!["--test=20".to_string()];
1274 let opts = vec![optmulti("t", "test", "testing", "TEST")];
85aaf69f 1275 let rs = getopts(&long_args, &opts);
1a4d82fc 1276 match rs {
92a42be0
SL
1277 Ok(ref m) => {
1278 assert!((m.opt_present("test")));
1279 assert_eq!(m.opt_str("test").unwrap(), "20");
1280 assert!((m.opt_present("t")));
1281 assert_eq!(m.opt_str("t").unwrap(), "20");
1282 }
1283 _ => panic!(),
1a4d82fc 1284 }
92a42be0 1285 let short_args = vec!["-t".to_string(), "20".to_string()];
85aaf69f 1286 match getopts(&short_args, &opts) {
92a42be0
SL
1287 Ok(ref m) => {
1288 assert!((m.opt_present("test")));
1289 assert_eq!(m.opt_str("test").unwrap(), "20");
1290 assert!((m.opt_present("t")));
1291 assert_eq!(m.opt_str("t").unwrap(), "20");
1292 }
1293 _ => panic!(),
1a4d82fc
JJ
1294 }
1295 }
1296
1297 #[test]
1298 fn test_optmulti_missing() {
92a42be0
SL
1299 let args = vec!["blah".to_string()];
1300 let opts = vec![optmulti("t", "test", "testing", "TEST")];
85aaf69f 1301 let rs = getopts(&args, &opts);
1a4d82fc 1302 match rs {
92a42be0
SL
1303 Ok(ref m) => {
1304 assert!(!m.opt_present("test"));
1305 assert!(!m.opt_present("t"));
1306 }
1307 _ => panic!(),
1a4d82fc
JJ
1308 }
1309 }
1310
1311 #[test]
1312 fn test_optmulti_no_arg() {
92a42be0
SL
1313 let long_args = vec!["--test".to_string()];
1314 let opts = vec![optmulti("t", "test", "testing", "TEST")];
85aaf69f 1315 let rs = getopts(&long_args, &opts);
1a4d82fc 1316 match rs {
92a42be0
SL
1317 Err(ArgumentMissing(_)) => {}
1318 _ => panic!(),
1a4d82fc 1319 }
92a42be0 1320 let short_args = vec!["-t".to_string()];
85aaf69f 1321 match getopts(&short_args, &opts) {
92a42be0
SL
1322 Err(ArgumentMissing(_)) => {}
1323 _ => panic!(),
1a4d82fc
JJ
1324 }
1325 }
1326
1327 #[test]
1328 fn test_optmulti_multi() {
92a42be0
SL
1329 let args = vec!["--test=20".to_string(), "-t".to_string(), "30".to_string()];
1330 let opts = vec![optmulti("t", "test", "testing", "TEST")];
85aaf69f 1331 let rs = getopts(&args, &opts);
1a4d82fc 1332 match rs {
92a42be0
SL
1333 Ok(ref m) => {
1334 assert!(m.opt_present("test"));
1335 assert_eq!(m.opt_str("test").unwrap(), "20");
1336 assert!(m.opt_present("t"));
1337 assert_eq!(m.opt_str("t").unwrap(), "20");
1338 let pair = m.opt_strs("test");
1339 assert!(pair[0] == "20");
1340 assert!(pair[1] == "30");
1341 }
1342 _ => panic!(),
1a4d82fc
JJ
1343 }
1344 }
1345
1346 #[test]
1347 fn test_unrecognized_option() {
92a42be0
SL
1348 let long_args = vec!["--untest".to_string()];
1349 let opts = vec![optmulti("t", "test", "testing", "TEST")];
85aaf69f 1350 let rs = getopts(&long_args, &opts);
1a4d82fc 1351 match rs {
92a42be0
SL
1352 Err(UnrecognizedOption(_)) => {}
1353 _ => panic!(),
1a4d82fc 1354 }
92a42be0 1355 let short_args = vec!["-u".to_string()];
85aaf69f 1356 match getopts(&short_args, &opts) {
92a42be0
SL
1357 Err(UnrecognizedOption(_)) => {}
1358 _ => panic!(),
1a4d82fc
JJ
1359 }
1360 }
1361
1362 #[test]
1363 fn test_combined() {
92a42be0
SL
1364 let args = vec!["prog".to_string(),
1365 "free1".to_string(),
1366 "-s".to_string(),
1367 "20".to_string(),
1368 "free2".to_string(),
1369 "--flag".to_string(),
1370 "--long=30".to_string(),
1371 "-f".to_string(),
1372 "-m".to_string(),
1373 "40".to_string(),
1374 "-m".to_string(),
1375 "50".to_string(),
1376 "-n".to_string(),
1377 "-A B".to_string(),
1378 "-n".to_string(),
1379 "-60 70".to_string()];
1380 let opts = vec![optopt("s", "something", "something", "SOMETHING"),
1381 optflag("", "flag", "a flag"),
1382 reqopt("", "long", "hi", "LONG"),
1383 optflag("f", "", "another flag"),
1384 optmulti("m", "", "mmmmmm", "YUM"),
1385 optmulti("n", "", "nothing", "NOTHING"),
1386 optopt("", "notpresent", "nothing to see here", "NOPE")];
85aaf69f 1387 let rs = getopts(&args, &opts);
1a4d82fc 1388 match rs {
92a42be0
SL
1389 Ok(ref m) => {
1390 assert!(m.free[0] == "prog");
1391 assert!(m.free[1] == "free1");
1392 assert_eq!(m.opt_str("s").unwrap(), "20");
1393 assert!(m.free[2] == "free2");
1394 assert!((m.opt_present("flag")));
1395 assert_eq!(m.opt_str("long").unwrap(), "30");
1396 assert!((m.opt_present("f")));
1397 let pair = m.opt_strs("m");
1398 assert!(pair[0] == "40");
1399 assert!(pair[1] == "50");
1400 let pair = m.opt_strs("n");
1401 assert!(pair[0] == "-A B");
1402 assert!(pair[1] == "-60 70");
1403 assert!((!m.opt_present("notpresent")));
1404 }
1405 _ => panic!(),
1a4d82fc
JJ
1406 }
1407 }
1408
1409 #[test]
1410 fn test_multi() {
92a42be0
SL
1411 let opts = vec![optopt("e", "", "encrypt", "ENCRYPT"),
1412 optopt("", "encrypt", "encrypt", "ENCRYPT"),
1413 optopt("f", "", "flag", "FLAG")];
1a4d82fc 1414
92a42be0 1415 let args_single = vec!["-e".to_string(), "foo".to_string()];
85aaf69f 1416 let matches_single = &match getopts(&args_single, &opts) {
92a42be0
SL
1417 result::Result::Ok(m) => m,
1418 result::Result::Err(_) => panic!(),
1a4d82fc
JJ
1419 };
1420 assert!(matches_single.opts_present(&["e".to_string()]));
1421 assert!(matches_single.opts_present(&["encrypt".to_string(), "e".to_string()]));
1422 assert!(matches_single.opts_present(&["e".to_string(), "encrypt".to_string()]));
1423 assert!(!matches_single.opts_present(&["encrypt".to_string()]));
1424 assert!(!matches_single.opts_present(&["thing".to_string()]));
1425 assert!(!matches_single.opts_present(&[]));
1426
1427 assert_eq!(matches_single.opts_str(&["e".to_string()]).unwrap(), "foo");
1428 assert_eq!(matches_single.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1429 "foo");
1430 assert_eq!(matches_single.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1431 "foo");
1432
92a42be0
SL
1433 let args_both = vec!["-e".to_string(),
1434 "foo".to_string(),
1435 "--encrypt".to_string(),
1436 "foo".to_string()];
85aaf69f 1437 let matches_both = &match getopts(&args_both, &opts) {
92a42be0
SL
1438 result::Result::Ok(m) => m,
1439 result::Result::Err(_) => panic!(),
1a4d82fc
JJ
1440 };
1441 assert!(matches_both.opts_present(&["e".to_string()]));
1442 assert!(matches_both.opts_present(&["encrypt".to_string()]));
1443 assert!(matches_both.opts_present(&["encrypt".to_string(), "e".to_string()]));
1444 assert!(matches_both.opts_present(&["e".to_string(), "encrypt".to_string()]));
1445 assert!(!matches_both.opts_present(&["f".to_string()]));
1446 assert!(!matches_both.opts_present(&["thing".to_string()]));
1447 assert!(!matches_both.opts_present(&[]));
1448
1449 assert_eq!(matches_both.opts_str(&["e".to_string()]).unwrap(), "foo");
92a42be0
SL
1450 assert_eq!(matches_both.opts_str(&["encrypt".to_string()]).unwrap(),
1451 "foo");
1a4d82fc
JJ
1452 assert_eq!(matches_both.opts_str(&["e".to_string(), "encrypt".to_string()]).unwrap(),
1453 "foo");
1454 assert_eq!(matches_both.opts_str(&["encrypt".to_string(), "e".to_string()]).unwrap(),
1455 "foo");
1456 }
1457
1458 #[test]
1459 fn test_nospace() {
92a42be0
SL
1460 let args = vec!["-Lfoo".to_string(), "-M.".to_string()];
1461 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1462 optmulti("M", "", "something", "MMMM")];
85aaf69f 1463 let matches = &match getopts(&args, &opts) {
92a42be0
SL
1464 result::Result::Ok(m) => m,
1465 result::Result::Err(_) => panic!(),
1a4d82fc
JJ
1466 };
1467 assert!(matches.opts_present(&["L".to_string()]));
1468 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "foo");
1469 assert!(matches.opts_present(&["M".to_string()]));
1470 assert_eq!(matches.opts_str(&["M".to_string()]).unwrap(), ".");
1471
1472 }
1473
1474 #[test]
1475 fn test_nospace_conflict() {
92a42be0
SL
1476 let args = vec!["-vvLverbose".to_string(), "-v".to_string()];
1477 let opts = vec![optmulti("L", "", "library directory", "LIB"),
1478 optflagmulti("v", "verbose", "Verbose")];
85aaf69f 1479 let matches = &match getopts(&args, &opts) {
92a42be0
SL
1480 result::Result::Ok(m) => m,
1481 result::Result::Err(e) => panic!("{}", e),
1a4d82fc
JJ
1482 };
1483 assert!(matches.opts_present(&["L".to_string()]));
1484 assert_eq!(matches.opts_str(&["L".to_string()]).unwrap(), "verbose");
1485 assert!(matches.opts_present(&["v".to_string()]));
1486 assert_eq!(3, matches.opt_count("v"));
1487 }
1488
1489 #[test]
1490 fn test_long_to_short() {
1491 let mut short = Opt {
1492 name: Name::Long("banana".to_string()),
1493 hasarg: HasArg::Yes,
1494 occur: Occur::Req,
1495 aliases: Vec::new(),
1496 };
92a42be0
SL
1497 short.aliases = vec![Opt {
1498 name: Name::Short('b'),
1499 hasarg: HasArg::Yes,
1500 occur: Occur::Req,
1501 aliases: Vec::new(),
1502 }];
1a4d82fc
JJ
1503 let verbose = reqopt("b", "banana", "some bananas", "VAL");
1504
1505 assert!(verbose.long_to_short() == short);
1506 }
1507
1508 #[test]
1509 fn test_aliases_long_and_short() {
92a42be0 1510 let opts = vec![optflagmulti("a", "apple", "Desc")];
1a4d82fc 1511
92a42be0 1512 let args = vec!["-a".to_string(), "--apple".to_string(), "-a".to_string()];
1a4d82fc 1513
85aaf69f 1514 let matches = getopts(&args, &opts).unwrap();
1a4d82fc
JJ
1515 assert_eq!(3, matches.opt_count("a"));
1516 assert_eq!(3, matches.opt_count("apple"));
1517 }
1518
1519 #[test]
1520 fn test_usage() {
92a42be0
SL
1521 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1522 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1523 optflag("k", "kiwi", "Desc"),
1524 optflagopt("p", "", "Desc", "VAL"),
1525 optmulti("l", "", "Desc", "VAL")];
1a4d82fc
JJ
1526
1527 let expected =
1528"Usage: fruits
1529
1530Options:
1531 -b --banana VAL Desc
1532 -a --012345678901234567890123456789 VAL
1533 Desc
1534 -k --kiwi Desc
1535 -p [VAL] Desc
1536 -l VAL Desc
1537";
1538
85aaf69f 1539 let generated_usage = usage("Usage: fruits", &optgroups);
1a4d82fc 1540
1a4d82fc
JJ
1541 assert_eq!(generated_usage, expected);
1542 }
1543
1544 #[test]
1545 fn test_usage_description_wrapping() {
1546 // indentation should be 24 spaces
1547 // lines wrap after 78: or rather descriptions wrap after 54
1548
92a42be0
SL
1549 let optgroups = vec![optflag("k",
1550 "kiwi",
1551 // 54
1552 "This is a long description which won't be wrapped..+.."),
1553 optflag("a",
1554 "apple",
1555 "This is a long description which _will_ be wrapped..+..")];
1a4d82fc
JJ
1556
1557 let expected =
1558"Usage: fruits
1559
1560Options:
1561 -k --kiwi This is a long description which won't be wrapped..+..
1562 -a --apple This is a long description which _will_ be
1563 wrapped..+..
1564";
1565
85aaf69f 1566 let usage = usage("Usage: fruits", &optgroups);
1a4d82fc 1567
1a4d82fc
JJ
1568 assert!(usage == expected)
1569 }
1570
1571 #[test]
1572 fn test_usage_description_multibyte_handling() {
92a42be0
SL
1573 let optgroups = vec![optflag("k",
1574 "k\u{2013}w\u{2013}",
1575 "The word kiwi is normally spelled with two i's"),
1576 optflag("a",
1577 "apple",
1578 "This \u{201C}description\u{201D} has some characters that \
1579 could confuse the line wrapping; an apple costs 0.51€ in \
1580 some parts of Europe.")];
1a4d82fc
JJ
1581
1582 let expected =
1583"Usage: fruits
1584
1585Options:
1586 -k --k–w– The word kiwi is normally spelled with two i's
1587 -a --apple This “description” has some characters that could
1588 confuse the line wrapping; an apple costs 0.51€ in
1589 some parts of Europe.
1590";
1591
85aaf69f 1592 let usage = usage("Usage: fruits", &optgroups);
1a4d82fc 1593
1a4d82fc
JJ
1594 assert!(usage == expected)
1595 }
1596
1597 #[test]
1598 fn test_short_usage() {
92a42be0
SL
1599 let optgroups = vec![reqopt("b", "banana", "Desc", "VAL"),
1600 optopt("a", "012345678901234567890123456789", "Desc", "VAL"),
1601 optflag("k", "kiwi", "Desc"),
1602 optflagopt("p", "", "Desc", "VAL"),
1603 optmulti("l", "", "Desc", "VAL")];
1a4d82fc
JJ
1604
1605 let expected = "Usage: fruits -b VAL [-a VAL] [-k] [-p [VAL]] [-l VAL]..".to_string();
85aaf69f 1606 let generated_usage = short_usage("fruits", &optgroups);
1a4d82fc 1607
1a4d82fc
JJ
1608 assert_eq!(generated_usage, expected);
1609 }
92a42be0
SL
1610
1611 #[test]
1612 fn test_args_with_equals() {
1613 let args = vec!("--one".to_string(), "A=B".to_string(),
1614 "--two=C=D".to_string());
1615 let opts = vec![optopt("o", "one", "One", "INFO"),
1616 optopt("t", "two", "Two", "INFO")];
1617 let matches = &match getopts(&args, &opts) {
1618 result::Result::Ok(m) => m,
1619 result::Result::Err(e) => panic!("{}", e)
1620 };
1621 assert_eq!(matches.opts_str(&["o".to_string()]).unwrap(), "A=B");
1622 assert_eq!(matches.opts_str(&["t".to_string()]).unwrap(), "C=D");
1623 }
1a4d82fc 1624}