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