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