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