just_getopt/lib.rs
1#![warn(missing_docs)]
2
3//! # Introduction
4//!
5//! This library crate implements a Posix `getopt`-like command-line
6//! option parser with simple programming interface. More specifically
7//! the parser is like `getopt`'s GNU extension called `getopt_long`
8//! which is familiar command-line option format for users of
9//! Linux-based operating systems.
10//!
11//! The name is `just_getopt` because this is *just a getopt parser* and
12//! (almost) nothing more. The intent is to provide the parsed output
13//! and basic methods for examining the output. There will not be
14//! anything for interpreting the output or for printing messages to
15//! program's user. The responsibility of interpretation is left to your
16//! program.
17//!
18//! In getopt logic there are two types of command-line options:
19//!
20//! 1. short options with a single letter name (`-f`)
21//! 2. long options with more than one letter as their name (`--file`).
22//!
23//! Both option types may accept an optional value or they may require a
24//! value. Values are given after the option. See the section **Parsing
25//! Rules** below for more information.
26//!
27//! Programming examples are in the **Examples** section below and in
28//! the source code repository's "examples" directory.
29//!
30//! # Parsing Rules
31//!
32//! By default, all options are expected to come first in the command
33//! line. Other arguments (non-options) come after options. Therefore
34//! the first argument that does not look like an option stops option
35//! parsing and the rest of the command line is parsed as non-options.
36//! This default can be changed, so that options and non-options can be
37//! mixed in their order in the command line. See [`OptSpecs::flag`]
38//! method for more information.
39//!
40//! In command line the "pseudo option" `--` (two dashes) always stops
41//! the option parser. Then the rest of the command line is parsed as
42//! regular arguments (non-options).
43//!
44//! ## Short Options
45//!
46//! Short options in the command line start with the `-` character which
47//! is followed by option's name character (`-c`), usually a letter.
48//!
49//! If option requires a value the value must be entered either directly
50//! after the option character (`-cVALUE`) or as the next command-line
51//! argument (`-c VALUE`). In the latter case anything that follows `-c`
52//! will be parsed as option's value.
53//!
54//! If option accepts an optional value the value must always be entered
55//! directly after the option character (`-cVALUE`). Otherwise there is
56//! no value for this option.
57//!
58//! Several short options can be entered together after one `-`
59//! character (`-abc`) but then only the last option in the series may
60//! have required or optional value.
61//!
62//! ## Long Options
63//!
64//! Long options start with `--` characters and the option name comes
65//! directly after it (`--foo`). The name must be at least two
66//! characters long.
67//!
68//! If option requires a value the value must be entered either directly
69//! after the option name and `=` character (`--foo=VALUE`) or as the
70//! next command-line argument (`--foo VALUE`). In the latter case
71//! anything that follows `--foo` will be parsed as option's value.
72//!
73//! If option accepts an optional value the value must always be entered
74//! directly after the option name and `=` character (`--foo=VALUE`).
75//! Otherwise there is no value for this option.
76//!
77//! Option `--foo=` is valid format when the option requires a value or
78//! accepts an optional value. It means that the value is empty string.
79//! It is not valid format when the option does not accept a value.
80//!
81//! # Examples
82//!
83//! Following examples will guide through a typical use of this library
84//! crate and command-line parsing.
85//!
86//! ## Prepare
87//!
88//! First we bring some important paths into the scope of our program.
89//!
90//! ```
91//! use just_getopt::{OptFlags, OptSpecs, OptValueType};
92//! ```
93//!
94//! Then we define which command-line options are valid for the program.
95//! We do this by creating an instance of [`OptSpecs`] struct by calling
96//! function [`OptSpecs::new`]. Then we modify the struct instance with
97//! [`option`](OptSpecs::option) and [`flag`](OptSpecs::flag) methods.
98//!
99//! ```
100//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
101//! let specs = OptSpecs::new()
102//! .option("help", "h", OptValueType::None) // Arguments: (id, name, value_type)
103//! .option("help", "help", OptValueType::None)
104//! .option("file", "f", OptValueType::Required)
105//! .option("file", "file", OptValueType::Required)
106//! .option("verbose", "v", OptValueType::Optional)
107//! .option("verbose", "verbose", OptValueType::Optional)
108//! .flag(OptFlags::OptionsEverywhere);
109//! ```
110//!
111//! Each [`option`](OptSpecs::option) method above adds a single option
112//! information to the option specification. Method's arguments are:
113//!
114//! 1. `id`: Programmer's identifier string for the option. The same
115//! identifier is used later to check if this particular option was
116//! present in the command line. Several options may have the same
117//! `id`. This makes sense when short option and long option have
118//! the same meaning, like `-h` and `--help` for printing help.
119//!
120//! 2. `name`: Option's name string in the command line, without
121//! prefix. A single-character name (like `h`) defines a short
122//! option which is entered like `-h` in the command line. Longer
123//! name defines a long option which is entered like `--help` in the
124//! command line.
125//!
126//! 3. `value_type`: Whether or not this option accepts a value and if
127//! the value is optional or required. The argument is a variant of
128//! enum [`OptValueType`].
129//!
130//! The [`flag`](OptSpecs::flag) method above adds a configuration flag
131//! for the command-line parser. It is a variant of enum [`OptFlags`].
132//! This variant [`OptionsEverywhere`](OptFlags::OptionsEverywhere)
133//! changes the command-line parser to accept options and other
134//! arguments in mixed order in the command line. That is, options can
135//! come after non-option arguments.
136//!
137//! For better explanation see the documentation of [`OptSpecs`] struct
138//! and its methods [`option`](OptSpecs::option) and
139//! [`flag`](OptSpecs::flag). Also see methods
140//! [`limit_options`](OptSpecs::limit_options),
141//! [`limit_other_args`](OptSpecs::limit_other_args) and
142//! [`limit_unknown_options`](OptSpecs::limit_unknown_options).
143//!
144//! ## Parse the Command Line
145//!
146//! We are ready to parse program's command-line arguments. We do this
147//! with [`OptSpecs::getopt`] method. Arguments we get from
148//! [`std::env::args`] function which returns an iterator.
149//!
150//! ```
151//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
152//! # let specs = OptSpecs::new();
153//! // Get arguments iterator from operating system and skip the first item
154//! let args = std::env::args().skip(1); // which is this program's file path.
155//! let parsed = specs.getopt(args); // Getopt! Use the "specs" variable defined above.
156//! ```
157//!
158//! If you want to try [`getopt`](OptSpecs::getopt) method without
159//! program's real command-line arguments you can also run it with other
160//! iterator argument or with a vector or an array as an argument. Like
161//! this:
162//!
163//! ```
164//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
165//! # let specs = OptSpecs::new();
166//! let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
167//! ```
168//!
169//! ## Examine the Parsed Output
170//!
171//! The command line is now parsed and the variable `parsed` (see above)
172//! owns an [`Args`] struct which represents the parsed output in
173//! organized form. It is a public struct and it can be examined
174//! manually. There are some methods for convenience, and some of them
175//! are shown in the following examples.
176//!
177//! At this stage it is useful to see the returned [`Args`] struct. One
178//! of its fields may contain some [`Opt`] structs too if the parser
179//! found valid command-line options. Let's print it:
180//!
181//! ```
182//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
183//! # let specs = OptSpecs::new();
184//! # let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
185//! eprintln!("{:#?}", parsed);
186//! ```
187//!
188//! That could print something like this:
189//!
190//! ```text
191//! Args {
192//! options: [
193//! Opt {
194//! id: "file",
195//! name: "file",
196//! value_required: true,
197//! value: Some(
198//! "123",
199//! ),
200//! },
201//! Opt {
202//! id: "file",
203//! name: "f",
204//! value_required: true,
205//! value: Some(
206//! "456",
207//! ),
208//! },
209//! Opt {
210//! id: "verbose",
211//! name: "v",
212//! value_required: false,
213//! value: None,
214//! },
215//! ],
216//! other: [
217//! "foo",
218//! "bar",
219//! ],
220//! unknown: [
221//! "a",
222//! ],
223//! }
224//! ```
225//!
226//! ### Unknown Options
227//!
228//! We probably want to tell program's user if there were unknown
229//! options. An error message to [`std::io::stderr`] stream is usually
230//! enough. No need to panic.
231//!
232//! ```
233//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
234//! # let specs = OptSpecs::new();
235//! # let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
236//! for u in &parsed.unknown {
237//! eprintln!("Unknown option: {}", u);
238//! }
239//! ```
240//!
241//! ### Required Value Missing
242//!
243//! More serious error is a missing value to an option which requires a
244//! value (like `file` option in our example, see above). That can be a
245//! good reason to exit the program.
246//!
247//! ```no_run
248//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
249//! # let specs = OptSpecs::new();
250//! # let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
251//! for o in &parsed.required_value_missing() {
252//! eprintln!("Value is required for option '{}'.", o.name);
253//! std::process::exit(1);
254//! }
255//! ```
256//!
257//! ### Print Help Message
258//!
259//! Command-line programs always have `-h` or `--help` option for
260//! printing a friendly help message. The following example shows how to
261//! detect that option.
262//!
263//! ```no_run
264//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
265//! # let specs = OptSpecs::new();
266//! # let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
267//! if parsed.option_exists("help") {
268//! println!("Print friendly help about program's usage.");
269//! std::process::exit(2);
270//! }
271//! ```
272//!
273//! The `"help"` string in the first line above is the identifier string
274//! (`id`) for the option. It was defined with [`OptSpecs::option`]
275//! method in the example code earlier. Identifier strings are used to
276//! find if a specific option was given in the command line.
277//!
278//! ### Collect Values and Other Arguments
279//!
280//! The rest depends very much on individual program's needs. Probably
281//! often we would collect what values were given to options. In our
282//! example program there are `-f` and `--file` options that require a
283//! value. We could collect all those values next.
284//!
285//! ```
286//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
287//! # let specs = OptSpecs::new();
288//! # let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
289//! for f in &parsed.options_value_all("file") {
290//! println!("File name: {:?}", f);
291//! }
292//! ```
293//!
294//! Notice if `-v` or `--verbose` was given, even without a value. Then
295//! collect all (optional) values for the option.
296//!
297//! ```
298//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
299//! # let specs = OptSpecs::new();
300//! # let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
301//! if parsed.option_exists("verbose") {
302//! println!("Option 'verbose' was given.");
303//!
304//! for v in &parsed.options_value_all("verbose") {
305//! println!("Verbose level: {:?}", v);
306//! }
307//! }
308//! ```
309//!
310//! Finally, our example program will handle all other arguments, that
311//! is, non-option arguments.
312//!
313//! ```
314//! # use just_getopt::{OptFlags, OptSpecs, OptValueType};
315//! # let specs = OptSpecs::new();
316//! # let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);
317//! for o in &parsed.other {
318//! println!("Other argument: {:?}", o);
319//! }
320//! ```
321//!
322//! # More Help
323//!
324//! A complete working example code -- very similar to previous examples
325//! -- is in the source code repository's "examples" directory. It can
326//! be run with command `cargo run --example basic -- your arguments`.
327//! Try it with different command-line arguments.
328//!
329//! Further reading:
330//!
331//! - [`OptSpecs`] struct and its methods.
332//! - [`Args`] struct and its methods.
333
334mod parser;
335
336/// Specification for program's valid command-line options.
337///
338/// An instance of this struct is needed before command-line options can
339/// be parsed. Instances are created with function [`OptSpecs::new`] and
340/// they are modified with [`option`](OptSpecs::option) and other
341/// methods
342///
343/// The struct instance is used when parsing the command line given by
344/// program's user. The parser methods is [`getopt`](OptSpecs::getopt).
345
346#[derive(Debug, PartialEq)]
347pub struct OptSpecs {
348 options: Vec<OptSpec>,
349 flags: Vec<OptFlags>,
350 option_limit: u32,
351 other_limit: u32,
352 unknown_limit: u32,
353}
354
355const COUNTER_LIMIT: u32 = u32::MAX;
356
357#[derive(Debug, PartialEq)]
358struct OptSpec {
359 id: String,
360 name: String,
361 value_type: OptValueType,
362}
363
364/// Option's value type.
365///
366/// See [`OptSpecs::option`] method for more information.
367
368#[derive(Debug, PartialEq)]
369pub enum OptValueType {
370 /// Option does not accept a value.
371 None,
372 /// Option accepts an optional value.
373 Optional,
374 /// Option requires a value.
375 Required,
376}
377
378/// Flags for changing command-line parser's behavior.
379///
380/// See [`OptSpecs::flag`] method for more information.
381
382#[derive(Debug, PartialEq)]
383#[non_exhaustive]
384pub enum OptFlags {
385 /// Accept command-line options and other arguments in mixed order
386 /// in the command line. That is, options can come after non-option
387 /// arguments.
388 ///
389 /// This is not the default behavior. By default the first
390 /// non-option argument in the command line stops option parsing and
391 /// the rest of the command line is parsed as non-options (other
392 /// arguments), even if they look like options.
393 OptionsEverywhere,
394
395 /// Long options don't need to be written in full in the command
396 /// line. They can be shortened as long as there are enough
397 /// characters to find a unique prefix match. If there are more than
398 /// one match the option given in the command line is classified as
399 /// unknown.
400 PrefixMatchLongOptions,
401}
402
403impl OptSpecs {
404 /// Create and return a new instance of [`OptSpecs`] struct.
405 ///
406 /// The created instance is "empty" and does not contain any
407 /// specifications for command-line options. Apply
408 /// [`option`](OptSpecs::option) or other methods to make it useful
409 /// for parsing command-line.
410 pub fn new() -> Self {
411 Self {
412 options: Vec::new(),
413 flags: Vec::new(),
414 option_limit: COUNTER_LIMIT,
415 other_limit: COUNTER_LIMIT,
416 unknown_limit: COUNTER_LIMIT,
417 }
418 }
419
420 /// Add an option specification for [`OptSpecs`].
421 ///
422 /// The method requires three arguments:
423 ///
424 /// 1. `id`: Programmer's identifier string for the option. Later,
425 /// after parsing the command line, the identifier is used to
426 /// match if this particular option was present in the
427 /// command-line.
428 ///
429 /// Several options may have the same identifier string. This
430 /// makes sense when different option names in the command line
431 /// represent the same meaning, like `-h` and `--help` for
432 /// printing program's help message.
433 ///
434 /// 2. `name`: Option's name string in the command line (without
435 /// prefix). If the string is a single character (like `h`) it
436 /// defines a short option which is entered as `-h` in the
437 /// command line. If there are more than one character in the
438 /// string it defines a long option name (like `help`) which is
439 /// entered as `--help` in the command line.
440 ///
441 /// All options must have a unique `name` string. This method
442 /// will panic if the same `name` is added twice. The method
443 /// will also panic if the `name` string contains illegal
444 /// characters. Space characters are not accepted. A short
445 /// option name can't be `-` and long option names can't have
446 /// any `=` characters nor `-` as their first character.
447 ///
448 /// 3. `value_type`: A variant of enum [`OptValueType`] which
449 /// defines if this option accepts a value. If not, use
450 /// [`OptValueType::None`] as method's argument. If an optional
451 /// value is accepted, use [`OptValueType::Optional`]. If the
452 /// option requires a value, use [`OptValueType::Required`].
453 ///
454 /// The return value is the same struct instance which was modified.
455 pub fn option(mut self, id: &str, name: &str, value_type: OptValueType) -> Self {
456 assert!(
457 id.chars().count() > 0,
458 "Option's \"id\" must be at least 1 character long."
459 );
460
461 let name_count = name.chars().count();
462
463 if name_count == 1 {
464 assert!(
465 parser::is_valid_short_option_name(name),
466 "Not a valid short option name."
467 );
468 } else if name_count >= 2 {
469 assert!(
470 parser::is_valid_long_option_name(name),
471 "Not a valid long option name."
472 );
473 } else {
474 panic!("Option's \"name\" must be at least 1 character long.");
475 }
476
477 for e in &self.options {
478 assert!(
479 e.name != name,
480 "No duplicates allowed for option's \"name\"."
481 );
482 }
483
484 self.options.push(OptSpec {
485 id: id.to_string(),
486 name: name.to_string(),
487 value_type,
488 });
489 self
490 }
491
492 /// Add a flag that changes parser's behavior.
493 ///
494 /// Method's only argument `flag` is a variant of enum [`OptFlags`].
495 /// Their names and meanings are:
496 ///
497 /// - [`OptFlags::OptionsEverywhere`]: Accept command-line options
498 /// and other arguments in mixed order in the command line. That
499 /// is, options can come after non-option arguments.
500 ///
501 /// This is not the default behavior. By default the first
502 /// non-option argument in the command line stops option parsing
503 /// and the rest of the command line is parsed as non-options
504 /// (other arguments), even if they look like options.
505 ///
506 /// - [`OptFlags::PrefixMatchLongOptions`]: With this flag long
507 /// options don't need to be written in full in the command
508 /// line. They can be shortened as long as there are enough
509 /// characters to find a unique prefix match. If there are more
510 /// than one match the option which was given in the command
511 /// line is classified as unknown.
512 ///
513 /// The return value is the same struct instance which was modified.
514 pub fn flag(mut self, flag: OptFlags) -> Self {
515 if !self.flags.contains(&flag) {
516 self.flags.push(flag);
517 }
518 self
519 }
520
521 fn is_flag(&self, flag: OptFlags) -> bool {
522 self.flags.contains(&flag)
523 }
524
525 /// Maximum number of valid options.
526 ///
527 /// Method's argument `limit` sets the maximum number of valid
528 /// options to collect from the command line. The rest is ignored.
529 /// This doesn't include unknown options (see
530 /// [`limit_unknown_options`](OptSpecs::limit_unknown_options)).
531 ///
532 /// The return value is the same struct instance which was modified.
533 pub fn limit_options(mut self, limit: u32) -> Self {
534 self.option_limit = limit;
535 self
536 }
537
538 /// Maximum number of other command-line arguments.
539 ///
540 /// Method's argument `limit` sets the maximum number of other
541 /// (non-option) arguments to collect from the command line. The
542 /// rest is ignored.
543 ///
544 /// Note: If your program accepts *n* number of command-line
545 /// argument (apart from options) you could set this limit to *n +
546 /// 1*. This way you know if there were more arguments than needed
547 /// and can inform program's user about that. There is no need to
548 /// collect more arguments.
549 ///
550 /// The return value is the same struct instance which was modified.
551 pub fn limit_other_args(mut self, limit: u32) -> Self {
552 self.other_limit = limit;
553 self
554 }
555
556 /// Maximum number of unknown options.
557 ///
558 /// Method's argument `limit` sets the maximum number of unique
559 /// unknown options to collect from the command line. Duplicates are
560 /// not collected.
561 ///
562 /// Note: If you want to stop your program if it notices just one
563 /// unknown option you can set this limit to 1. There is probably no
564 /// need to collect more of them.
565 ///
566 /// The return value is the same struct instance which was modified.
567 pub fn limit_unknown_options(mut self, limit: u32) -> Self {
568 self.unknown_limit = limit;
569 self
570 }
571
572 /// Getopt-parse an iterable item as command line arguments.
573 ///
574 /// This method's argument `args` is of any type that implements
575 /// trait [`IntoIterator`] and that has items of type that
576 /// implements trait [`ToString`]. For example, argument `args` can
577 /// be a vector or an iterator such as command-line arguments
578 /// returned by [`std::env::args`].
579 ///
580 /// The return value is an [`Args`] struct which represents the
581 /// command-line information in organized form.
582 pub fn getopt<I, S>(&self, args: I) -> Args
583 where
584 I: IntoIterator<Item = S>,
585 S: ToString,
586 {
587 parser::parse(self, args.into_iter().map(|i| i.to_string()))
588 }
589
590 fn get_short_option_match(&self, name: &str) -> Option<&OptSpec> {
591 if name.chars().count() != 1 {
592 return None;
593 }
594 self.options.iter().find(|e| e.name == name)
595 }
596
597 fn get_long_option_match(&self, name: &str) -> Option<&OptSpec> {
598 if name.chars().count() < 2 {
599 return None;
600 }
601 self.options.iter().find(|e| e.name == name)
602 }
603
604 fn get_long_option_prefix_match(&self, name: &str) -> Option<&OptSpec> {
605 if name.chars().count() < 2 {
606 return None;
607 }
608
609 if let Some(exact) = self.get_long_option_match(name) {
610 return Some(exact);
611 }
612
613 let mut result = None;
614
615 for e in &self.options {
616 if e.name.starts_with(name) {
617 if result.is_none() {
618 result = Some(e);
619 } else {
620 return None;
621 }
622 }
623 }
624 result
625 }
626}
627
628impl Default for OptSpecs {
629 fn default() -> Self {
630 Self::new()
631 }
632}
633
634/// Parsed command line in organized form.
635///
636/// Instances of this struct are usually created with
637/// [`OptSpecs::getopt`] method and an instance represents the parsed
638/// output in organized form. See each field's documentation for more
639/// information.
640///
641/// Programmers can use the parsed output ([`Args`] struct) any way they
642/// like. There are some methods for convenience.
643
644#[derive(Debug, PartialEq)]
645pub struct Args {
646 /// A vector of valid command-line options.
647 ///
648 /// Elements of this vector are [`Opt`] structs which each
649 /// represents a single command-line option. Elements are in the
650 /// same order as given (by program's user) in the command line. The
651 /// vector is empty if the parser didn't find any valid command-line
652 /// options.
653 pub options: Vec<Opt>,
654
655 /// A vector of other arguments (non-options).
656 ///
657 /// Each element of the vector is a single non-option argument
658 /// string in the same order as given (by program's user) in the
659 /// command line. The vector is empty if the parser didn't find any
660 /// non-option arguments.
661 pub other: Vec<String>,
662
663 /// Unknown options.
664 ///
665 /// Command-line arguments that look like options but were not part
666 /// of [`OptSpecs`] specification are classified as unknown. They
667 /// are listed in this vector. Possible duplicate unknown options
668 /// have been filtered out.
669 ///
670 /// Each element is the name string for the option (without `-` or
671 /// `--` prefix). For unknown short options the element is a
672 /// single-character string. For unknown long options the string has
673 /// more than one character. The whole vector is empty if there were
674 /// no unknown options.
675 ///
676 /// If a long option does not accept a value (that is, its value
677 /// type is [`OptValueType::None`]) but user gives it a value with
678 /// equal sign notation (`--foo=`), that option is classified as
679 /// unknown and it will be in this field's vector with name `foo=`.
680 pub unknown: Vec<String>,
681}
682
683impl Args {
684 fn new() -> Self {
685 Args {
686 options: Vec::new(),
687 other: Vec::new(),
688 unknown: Vec::new(),
689 }
690 }
691
692 /// Find options with missing required value.
693 ///
694 /// This method finds all (otherwise valid) options which require a
695 /// value but the value is missing. That is, [`OptSpecs`] struct
696 /// specification defined that an option requires a value but
697 /// program's user didn't give one in the command line. Such thing
698 /// can happen if an option like `--file` is the last argument in
699 /// the command line and that option requires a value. Empty string
700 /// `""` is not classified as missing value because it can be valid
701 /// user input in many situations.
702 ///
703 /// This method returns a vector (possibly empty) and each element
704 /// is a reference to an [`Opt`] struct in the original
705 /// [`Args::options`] field contents.
706 pub fn required_value_missing(&self) -> Vec<&Opt> {
707 self.options
708 .iter()
709 .filter(|opt| opt.value_required && opt.value.is_none())
710 .collect()
711 }
712
713 /// Return boolean whether option with the given `id` exists.
714 ///
715 /// This is functionally the same as
716 /// [`options_first`](Args::options_first)`(id).is_some()`.
717 pub fn option_exists(&self, id: &str) -> bool {
718 self.options.iter().any(|opt| opt.id == id)
719 }
720
721 /// Find all options with the given `id`.
722 ///
723 /// Find all options which have the identifier `id`. (Option
724 /// identifiers have been defined in [`OptSpecs`] struct before
725 /// parsing.) The return value is a vector (possibly empty, if no
726 /// matches) and each element is a reference to [`Opt`] struct in
727 /// the original [`Args`] struct. Elements in the vector are in the
728 /// same order as in the parsed command line.
729 pub fn options_all(&self, id: &str) -> Vec<&Opt> {
730 self.options.iter().filter(|opt| opt.id == id).collect()
731 }
732
733 /// Find the first option with the given `id`.
734 ///
735 /// Find and return the first match for option `id` in command-line
736 /// arguments' order. (Options' identifiers have been defined in
737 /// [`OptSpecs`] struct before parsing.)
738 ///
739 /// The return value is a variant of enum [`Option`]. Their
740 /// meanings:
741 ///
742 /// - `None`: No options found with the given `id`.
743 ///
744 /// - `Some(&Opt)`: An option was found with the given `id` and a
745 /// reference is provided to its [`Opt`] struct in the original
746 /// [`Args::options`] field.
747 pub fn options_first(&self, id: &str) -> Option<&Opt> {
748 self.options.iter().find(|opt| opt.id == id)
749 }
750
751 /// Find the last option with the given `id`.
752 ///
753 /// This is similar to [`options_first`](Args::options_first) method
754 /// but this returns the last match in command-line arguments'
755 /// order.
756 pub fn options_last(&self, id: &str) -> Option<&Opt> {
757 self.options.iter().rev().find(|opt| opt.id == id)
758 }
759
760 /// Find and return all values for options with the given `id`.
761 ///
762 /// Find all options which match the identifier `id` and which also
763 /// have a value assigned. (Options' identifiers have been defined
764 /// in [`OptSpecs`] struct before parsing.) Collect options' values
765 /// into a new vector in the same order as they were given in the
766 /// command line. Vector's elements are references to the value
767 /// strings in the original [`Args`] struct. The returned vector is
768 /// empty if there were no matches.
769 pub fn options_value_all(&self, id: &str) -> Vec<&String> {
770 self.options
771 .iter()
772 .filter_map(|opt| {
773 if opt.id == id {
774 opt.value.as_ref()
775 } else {
776 None
777 }
778 })
779 .collect()
780 }
781
782 /// Find the first option with a value for given option `id`.
783 ///
784 /// Find the first option with the identifier `id` and which has a
785 /// value assigned. (Options' identifiers have been defined in
786 /// [`OptSpecs`] struct before parsing.) Method's return value is a
787 /// variant of enum [`Option`] which are:
788 ///
789 /// - `None`: No options found with the given `id` and a value
790 /// assigned. Note that there could be options for the same `id`
791 /// but they don't have a value.
792 ///
793 /// - `Some(&String)`: An option was found with the given `id` and
794 /// the option has a value assigned. A reference is provided to
795 /// the string value in the [`Opt::value`] field in the original
796 /// [`Args::options`] field.
797 pub fn options_value_first(&self, id: &str) -> Option<&String> {
798 match self
799 .options
800 .iter()
801 .find(|opt| opt.id == id && opt.value.is_some())
802 {
803 Some(o) => o.value.as_ref(),
804 None => None,
805 }
806 }
807
808 /// Find the last option with a value for given option `id`.
809 ///
810 /// This is similar to
811 /// [`options_value_first`](Args::options_value_first) method but
812 /// this method finds and returns the last option's value.
813 ///
814 /// Note: Program's user may give the same option several times in
815 /// the command line. If the option accepts a value it may be
816 /// suitable to consider only the last value relevant. (Or the
817 /// first, or maybe print an error message for providing several,
818 /// possibly conflicting, values.)
819 pub fn options_value_last(&self, id: &str) -> Option<&String> {
820 match self
821 .options
822 .iter()
823 .rev()
824 .find(|opt| opt.id == id && opt.value.is_some())
825 {
826 Some(o) => o.value.as_ref(),
827 None => None,
828 }
829 }
830}
831
832/// Structured option information.
833///
834/// This [`Opt`] struct represents organized information about single
835/// command-line option. Instances of this struct are usually created by
836/// [`OptSpecs::getopt`] method which returns an [`Args`] struct which
837/// have these [`Opt`] structs inside.
838///
839/// A programmer may need these when examining parsed command-line
840/// options. See the documentation of individual fields for more
841/// information. Also see [`Args`] struct and its methods.
842
843#[derive(Debug, PartialEq)]
844pub struct Opt {
845 /// Identifier for the option.
846 ///
847 /// Identifiers are defined with [`OptSpecs::option`] method before
848 /// parsing command-line arguments. After [`OptSpecs::getopt`]
849 /// parsing the same identifier is copied here and it confirms that
850 /// the option was indeed given in the command line.
851 pub id: String,
852
853 /// Option's name in the parsed command line.
854 ///
855 /// Option's name that was used in the command line. For short
856 /// options this is a single-character string. For long options the
857 /// name has more than one characters.
858 pub name: String,
859
860 /// The option requires a value.
861 ///
862 /// `true` means that the option was defined with value type
863 /// [`OptValueType::Required`]. See [`OptSpecs::flag`] method for
864 /// more information. This field does not guarantee that there
865 /// actually was a value for the option in the command line.
866 pub value_required: bool,
867
868 /// Option's value.
869 ///
870 /// The value is a variant of enum [`Option`]. Value `None` means
871 /// that there is no value for the option. Value `Some(String)`
872 /// provides a value.
873 pub value: Option<String>,
874}
875
876#[cfg(test)]
877mod tests {
878 use super::*;
879
880 #[test]
881 fn t_create_optspecs_01() {
882 let mut spec;
883 let mut expect;
884
885 spec = OptSpecs::new().option("help", "help", OptValueType::None);
886 expect = OptSpec {
887 id: String::from("help"),
888 name: String::from("help"),
889 value_type: OptValueType::None,
890 };
891 assert_eq!(1, spec.options.len());
892 assert_eq!(&expect, &spec.options[0]);
893 assert_eq!(COUNTER_LIMIT, spec.option_limit);
894 assert_eq!(COUNTER_LIMIT, spec.other_limit);
895 assert_eq!(COUNTER_LIMIT, spec.unknown_limit);
896
897 spec = spec.option("file", "f", OptValueType::Optional);
898 expect = OptSpec {
899 id: String::from("file"),
900 name: String::from("f"),
901 value_type: OptValueType::Optional,
902 };
903 assert_eq!(2, spec.options.len());
904 assert_eq!(&expect, &spec.options[1]);
905
906 spec = spec.option("file", "file", OptValueType::Required);
907 expect = OptSpec {
908 id: String::from("file"),
909 name: String::from("file"),
910 value_type: OptValueType::Required,
911 };
912 assert_eq!(3, spec.options.len());
913 assert_eq!(&expect, &spec.options[2]);
914
915 spec = spec.flag(OptFlags::OptionsEverywhere);
916 assert_eq!(1, spec.flags.len()); // Length 1
917 assert_eq!(true, spec.is_flag(OptFlags::OptionsEverywhere));
918 spec = spec.flag(OptFlags::PrefixMatchLongOptions);
919 assert_eq!(2, spec.flags.len()); // Length 2
920 assert_eq!(true, spec.is_flag(OptFlags::PrefixMatchLongOptions));
921 spec = spec.flag(OptFlags::OptionsEverywhere);
922 spec = spec.flag(OptFlags::PrefixMatchLongOptions);
923 assert_eq!(2, spec.flags.len()); // Length still 2
924
925 spec = spec.limit_options(9);
926 spec = spec.limit_other_args(10);
927 spec = spec.limit_unknown_options(3);
928 assert_eq!(9, spec.option_limit);
929 assert_eq!(10, spec.other_limit);
930 assert_eq!(3, spec.unknown_limit);
931 }
932
933 #[test]
934 #[should_panic]
935 fn t_create_optspecs_02() {
936 let _spec = OptSpecs::new().option("", "h", OptValueType::None);
937 }
938
939 #[test]
940 #[should_panic]
941 fn t_create_optspecs_03() {
942 let _spec = OptSpecs::new().option("h", "h", OptValueType::None).option(
943 "h",
944 "h",
945 OptValueType::None,
946 );
947 }
948
949 #[test]
950 #[should_panic]
951 fn t_create_optspecs_04() {
952 let _spec = OptSpecs::new().option("h", "", OptValueType::None);
953 }
954
955 #[test]
956 fn t_is_flag() {
957 let mut spec = OptSpecs::new().flag(OptFlags::OptionsEverywhere);
958 assert_eq!(true, spec.is_flag(OptFlags::OptionsEverywhere));
959
960 spec = spec.flag(OptFlags::PrefixMatchLongOptions);
961 assert_eq!(true, spec.is_flag(OptFlags::PrefixMatchLongOptions));
962 }
963
964 #[test]
965 fn t_parsed_output_010() {
966 let parsed = OptSpecs::new()
967 .option("help", "h", OptValueType::None)
968 .option("help", "help", OptValueType::None)
969 .option("file", "f", OptValueType::Required)
970 .option("file", "file", OptValueType::Required)
971 .getopt(["-h", "--help", "-f123", "-f", "456", "foo", "bar"]);
972
973 assert_eq!(true, parsed.option_exists("help"));
974 assert_eq!(true, parsed.option_exists("file"));
975 assert_eq!(false, parsed.option_exists("x"));
976
977 assert_eq!("h", parsed.options_first("help").unwrap().name);
978 assert_eq!("help", parsed.options_last("help").unwrap().name);
979 assert_eq!("help", parsed.options_first("help").unwrap().id);
980 assert_eq!("help", parsed.options_last("help").unwrap().id);
981 assert_eq!(false, parsed.options_first("help").unwrap().value_required);
982 assert_eq!(false, parsed.options_last("help").unwrap().value_required);
983
984 assert_eq!("f", parsed.options_first("file").unwrap().name);
985 assert_eq!(
986 "123",
987 parsed.options_first("file").unwrap().value.clone().unwrap()
988 );
989 assert_eq!(
990 "456",
991 parsed.options_last("file").unwrap().value.clone().unwrap()
992 );
993 assert_eq!(true, parsed.options_first("file").unwrap().value_required);
994
995 assert_eq!("foo", parsed.other[0]);
996 assert_eq!("bar", parsed.other[1]);
997 }
998
999 #[test]
1000 fn t_parsed_output_020() {
1001 let parsed = OptSpecs::new()
1002 .limit_options(1)
1003 .limit_other_args(2)
1004 .option("help", "h", OptValueType::None)
1005 .getopt(["-h", "foo", "-h"]);
1006
1007 assert_eq!("h", parsed.options_first("help").unwrap().name);
1008 assert_eq!(2, parsed.other.len());
1009 assert_eq!("foo", parsed.other[0]);
1010 assert_eq!("-h", parsed.other[1]);
1011 }
1012
1013 #[test]
1014 fn t_parsed_output_030() {
1015 let parsed = OptSpecs::new()
1016 .flag(OptFlags::OptionsEverywhere)
1017 .option("help", "h", OptValueType::None)
1018 .option("help", "help", OptValueType::None)
1019 .option("file", "f", OptValueType::Required)
1020 .option("file", "file", OptValueType::Required)
1021 .getopt(["-h", "foo", "--help", "--file=123", "bar", "--file", "456"]);
1022
1023 assert_eq!("h", parsed.options_first("help").unwrap().name);
1024 assert_eq!("help", parsed.options_last("help").unwrap().name);
1025 assert_eq!(
1026 "123",
1027 parsed.options_first("file").unwrap().value.clone().unwrap()
1028 );
1029 assert_eq!(
1030 "456",
1031 parsed.options_last("file").unwrap().value.clone().unwrap()
1032 );
1033 assert_eq!("foo", parsed.other[0]);
1034 assert_eq!("bar", parsed.other[1]);
1035 }
1036
1037 #[test]
1038 fn t_parsed_output_040() {
1039 let parsed = OptSpecs::new()
1040 .option("debug", "d", OptValueType::Optional)
1041 .option("verbose", "verbose", OptValueType::Optional)
1042 .getopt(["-d1", "-d", "--verbose", "--verbose=123"]);
1043
1044 assert_eq!(
1045 "1",
1046 parsed
1047 .options_first("debug")
1048 .unwrap()
1049 .value
1050 .clone()
1051 .unwrap()
1052 );
1053 assert_eq!(None, parsed.options_last("debug").unwrap().value);
1054 assert_eq!(false, parsed.options_last("debug").unwrap().value_required);
1055
1056 assert_eq!(None, parsed.options_first("verbose").unwrap().value);
1057 assert_eq!(
1058 "123",
1059 parsed
1060 .options_last("verbose")
1061 .unwrap()
1062 .value
1063 .clone()
1064 .unwrap()
1065 );
1066 assert_eq!(
1067 false,
1068 parsed.options_last("verbose").unwrap().value_required
1069 );
1070 }
1071
1072 #[test]
1073 fn t_parsed_output_050() {
1074 let parsed = OptSpecs::new()
1075 .option("debug", "d", OptValueType::Optional)
1076 .getopt(["-abcd", "-adbc"]);
1077
1078 assert_eq!(None, parsed.options_first("debug").unwrap().value);
1079 assert_eq!(
1080 "bc",
1081 parsed.options_last("debug").unwrap().value.clone().unwrap()
1082 );
1083
1084 assert_eq!(3, parsed.unknown.len());
1085 assert_eq!("a", parsed.unknown[0]);
1086 assert_eq!("b", parsed.unknown[1]);
1087 assert_eq!("c", parsed.unknown[2]);
1088 }
1089
1090 #[test]
1091 fn t_parsed_output_060() {
1092 let parsed = OptSpecs::new()
1093 .option("aaa", "bbb", OptValueType::None)
1094 .option("aaa", "c", OptValueType::None)
1095 .option("aaa", "d", OptValueType::None)
1096 .option("aaa", "eee", OptValueType::None)
1097 .getopt(["--bbb", "-cd", "--eee"]);
1098
1099 let m = parsed.options_all("aaa");
1100 assert_eq!("bbb", m[0].name);
1101 assert_eq!("c", m[1].name);
1102 assert_eq!("d", m[2].name);
1103 assert_eq!("eee", m[3].name);
1104 }
1105
1106 #[test]
1107 fn t_parsed_output_070() {
1108 let parsed = OptSpecs::new()
1109 .flag(OptFlags::PrefixMatchLongOptions)
1110 .option("version", "version", OptValueType::None)
1111 .option("verbose", "verbose", OptValueType::None)
1112 .getopt(["--ver", "--verb", "--versi", "--verbose"]);
1113
1114 assert_eq!("ver", parsed.unknown[0]);
1115 assert_eq!("verb", parsed.options_first("verbose").unwrap().name);
1116 assert_eq!("verbose", parsed.options_last("verbose").unwrap().name);
1117 assert_eq!("version", parsed.options_first("version").unwrap().id);
1118 assert_eq!("versi", parsed.options_first("version").unwrap().name);
1119 }
1120
1121 #[test]
1122 fn t_parsed_output_080() {
1123 let parsed = OptSpecs::new()
1124 // .flag(OptFlags::PrefixMatchLongOptions) Must be commented!
1125 .option("version", "version", OptValueType::None)
1126 .option("verbose", "verbose", OptValueType::None)
1127 .getopt(["--version", "--ver", "--verb", "--versi", "--verbose"]);
1128
1129 assert_eq!("ver", parsed.unknown[0]);
1130 assert_eq!("verb", parsed.unknown[1]);
1131 assert_eq!("versi", parsed.unknown[2]);
1132 assert_eq!("version", parsed.options_first("version").unwrap().name);
1133 assert_eq!("verbose", parsed.options_first("verbose").unwrap().name);
1134 }
1135
1136 #[test]
1137 fn t_parsed_output_085() {
1138 let parsed = OptSpecs::new()
1139 .flag(OptFlags::PrefixMatchLongOptions)
1140 .option("foo1", "foo", OptValueType::None)
1141 .option("foo2", "foo-longer", OptValueType::None)
1142 .getopt(["--fo", "--foo", "--foo-", "--foo-longer"]);
1143
1144 assert_eq!("fo", parsed.unknown[0]);
1145 assert_eq!("foo", parsed.options_first("foo1").unwrap().name);
1146 assert_eq!("foo", parsed.options_last("foo1").unwrap().name);
1147 assert_eq!("foo-", parsed.options_first("foo2").unwrap().name);
1148 assert_eq!("foo-longer", parsed.options_last("foo2").unwrap().name);
1149 }
1150
1151 #[test]
1152 fn t_parsed_output_090() {
1153 let parsed = OptSpecs::new()
1154 .flag(OptFlags::OptionsEverywhere)
1155 .option("help", "h", OptValueType::None)
1156 .option("file", "file", OptValueType::Required)
1157 .getopt(["-h", "foo", "--file=123", "--", "bar", "--file", "456"]);
1158
1159 assert_eq!("h", parsed.options_first("help").unwrap().name);
1160 assert_eq!("file", parsed.options_first("file").unwrap().name);
1161 assert_eq!(
1162 "123",
1163 parsed.options_first("file").unwrap().value.clone().unwrap()
1164 );
1165
1166 assert_eq!(4, parsed.other.len());
1167 assert_eq!("foo", parsed.other[0]);
1168 assert_eq!("bar", parsed.other[1]);
1169 assert_eq!("--file", parsed.other[2]);
1170 assert_eq!("456", parsed.other[3]);
1171 }
1172
1173 #[test]
1174 fn t_parsed_output_100() {
1175 let parsed = OptSpecs::new()
1176 .option("file", "file", OptValueType::Required)
1177 .getopt(["--file=", "--file"]);
1178
1179 assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1180 assert_eq!(
1181 "",
1182 parsed.options_first("file").unwrap().value.clone().unwrap()
1183 );
1184 assert_eq!(None, parsed.options_last("file").unwrap().value);
1185 }
1186
1187 #[test]
1188 fn t_parsed_output_110() {
1189 let parsed = OptSpecs::new()
1190 .option("file", "f", OptValueType::Required)
1191 .option("debug", "d", OptValueType::Required)
1192 .getopt(["-fx", "-d", "", "-f"]);
1193
1194 assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1195 assert_eq!(
1196 "x",
1197 parsed.options_first("file").unwrap().value.clone().unwrap()
1198 );
1199 assert_eq!(None, parsed.options_last("file").unwrap().value);
1200 assert_eq!(
1201 "",
1202 parsed
1203 .options_first("debug")
1204 .unwrap()
1205 .value
1206 .clone()
1207 .unwrap()
1208 );
1209 }
1210
1211 #[test]
1212 fn t_parsed_output_120() {
1213 let parsed = OptSpecs::new()
1214 .option("file", "f", OptValueType::Required)
1215 .option("debug", "d", OptValueType::Required)
1216 .getopt(["-f123", "-d", "", "-f", "456", "-f"]);
1217
1218 let f = parsed.options_value_all("file");
1219 let d = parsed.options_value_all("debug");
1220
1221 assert_eq!(2, f.len());
1222 assert_eq!("123", f[0]);
1223 assert_eq!("456", f[1]);
1224
1225 assert_eq!(1, d.len());
1226 assert_eq!("", d[0]);
1227
1228 assert_eq!(None, parsed.options_last("file").unwrap().value);
1229 let m = parsed.required_value_missing();
1230 assert_eq!(1, m.len());
1231 assert_eq!("f", m[0].name);
1232 }
1233
1234 #[test]
1235 fn t_parsed_output_130() {
1236 let parsed = OptSpecs::new()
1237 .option("file", "file", OptValueType::Required)
1238 .option("debug", "debug", OptValueType::Required)
1239 .getopt(["--file=123", "--debug", "", "--file", "456", "--file"]);
1240
1241 let f = parsed.options_value_all("file");
1242 let d = parsed.options_value_all("debug");
1243
1244 assert_eq!(2, f.len());
1245 assert_eq!("123", f[0]);
1246 assert_eq!("456", f[1]);
1247
1248 assert_eq!(1, d.len());
1249 assert_eq!("", d[0]);
1250
1251 assert_eq!(None, parsed.options_last("file").unwrap().value);
1252 let m = parsed.required_value_missing();
1253 assert_eq!(1, m.len());
1254 assert_eq!("file", m[0].name);
1255 }
1256
1257 #[test]
1258 fn t_parsed_output_140() {
1259 let parsed = OptSpecs::new()
1260 .flag(OptFlags::OptionsEverywhere)
1261 .option("debug", "d", OptValueType::Optional)
1262 .option("debug", "debug", OptValueType::Optional)
1263 .getopt([
1264 "-d",
1265 "-d123",
1266 "-d",
1267 "--debug",
1268 "--debug=",
1269 "foo",
1270 "--debug=456",
1271 "-d",
1272 ]);
1273
1274 let d = parsed.options_all("debug");
1275 assert_eq!(7, d.len());
1276
1277 let d = parsed.options_value_all("debug");
1278 assert_eq!(3, d.len());
1279 assert_eq!("123", d[0]);
1280 assert_eq!("", d[1]);
1281 assert_eq!("456", d[2]);
1282 assert_eq!("123", parsed.options_value_first("debug").unwrap());
1283 assert_eq!("456", parsed.options_value_last("debug").unwrap());
1284
1285 assert_eq!(None, parsed.options_value_first("not-at-all"));
1286 assert_eq!(None, parsed.options_value_last("not-at-all"));
1287
1288 assert_eq!("foo", parsed.other[0]);
1289 }
1290
1291 #[test]
1292 fn t_parsed_output_150() {
1293 let parsed = OptSpecs::new().limit_unknown_options(6).getopt([
1294 "-abcd",
1295 "-e",
1296 "--debug",
1297 "-x", // Won't be listed in unknown because of limit.
1298 "--",
1299 "--debug=",
1300 "foo",
1301 "--debug=456",
1302 ]);
1303
1304 assert_eq!(0, parsed.options.len());
1305 assert_eq!(3, parsed.other.len());
1306 assert_eq!(6, parsed.unknown.len());
1307 assert_eq!(vec!["a", "b", "c", "d", "e", "debug"], parsed.unknown);
1308 }
1309
1310 #[test]
1311 fn t_parsed_output_160() {
1312 let parsed = OptSpecs::new()
1313 .option("file", "file", OptValueType::Required)
1314 .getopt(["--file", "--", "--", "--"]);
1315
1316 assert_eq!(
1317 "--",
1318 parsed.options_first("file").unwrap().value.clone().unwrap()
1319 );
1320 assert_eq!(1, parsed.other.len());
1321 assert_eq!("--", parsed.other[0]);
1322
1323 assert_eq!(0, parsed.required_value_missing().len());
1324 }
1325
1326 #[test]
1327 fn t_parsed_output_170() {
1328 let parsed = OptSpecs::new().getopt(["foo", "bar"]);
1329
1330 assert_eq!(None, parsed.options_first("not-at-all"));
1331 assert_eq!(None, parsed.options_last("not-at-all"));
1332 }
1333
1334 #[test]
1335 fn t_parsed_output_180() {
1336 let parsed = OptSpecs::new()
1337 .limit_unknown_options(3)
1338 .option("bar", "bar", OptValueType::None)
1339 .getopt(["-aaa", "--foo", "--foo", "--bar=", "--bar=", "-x"]);
1340
1341 assert_eq!(3, parsed.unknown.len());
1342 assert_eq!("a", parsed.unknown[0]);
1343 assert_eq!("foo", parsed.unknown[1]);
1344 assert_eq!("bar=", parsed.unknown[2]);
1345 }
1346
1347 #[test]
1348 fn t_parsed_output_190() {
1349 let parsed = OptSpecs::new()
1350 .option("äiti", "äiti", OptValueType::Required)
1351 .option("€uro", "€uro", OptValueType::Required)
1352 .getopt(["--äiti=ööö", "--€uro", "€€€", "--äiti", "ää", "--äiti"]);
1353
1354 let a = parsed.options_value_all("äiti");
1355 let e = parsed.options_value_all("€uro");
1356
1357 assert_eq!(2, a.len());
1358 assert_eq!("ööö", a[0]);
1359 assert_eq!("ää", a[1]);
1360 assert_eq!("ööö", parsed.options_value_first("äiti").unwrap());
1361 assert_eq!("ää", parsed.options_value_last("äiti").unwrap());
1362
1363 assert_eq!(1, e.len());
1364 assert_eq!("€€€", e[0]);
1365 assert_eq!("€€€", parsed.options_value_first("€uro").unwrap());
1366 assert_eq!("€€€", parsed.options_value_last("€uro").unwrap());
1367
1368 assert_eq!(None, parsed.options_last("äiti").unwrap().value);
1369
1370 let m = parsed.required_value_missing();
1371 assert_eq!(1, m.len());
1372 assert_eq!("äiti", m[0].name);
1373 assert_eq!(None, m[0].value);
1374 }
1375
1376 #[test]
1377 fn t_parsed_output_195() {
1378 let parsed = OptSpecs::new().getopt(["-ä€", "--€uro", "äää", "€€€"]);
1379
1380 assert_eq!(2, parsed.other.len());
1381 assert_eq!("äää", parsed.other[0]);
1382 assert_eq!("€€€", parsed.other[1]);
1383
1384 assert_eq!(3, parsed.unknown.len());
1385 assert_eq!("ä", parsed.unknown[0]);
1386 assert_eq!("€", parsed.unknown[1]);
1387 assert_eq!("€uro", parsed.unknown[2]);
1388 }
1389
1390 #[test]
1391 fn t_parsed_output_200() {
1392 let parsed = OptSpecs::new().limit_other_args(5).getopt(1..10);
1393 assert_eq!(5, parsed.other.len());
1394 assert_eq!(vec!["1", "2", "3", "4", "5"], parsed.other);
1395 }
1396
1397 #[test]
1398 fn t_parsed_output_210() {
1399 let parsed = OptSpecs::new().limit_other_args(0).getopt(1..10);
1400 assert_eq!(0, parsed.other.len());
1401 }
1402
1403 #[test]
1404 fn t_parsed_output_220() {
1405 let parsed = OptSpecs::new()
1406 .option("file", "f", OptValueType::Required)
1407 .option("file", "file", OptValueType::Required)
1408 .option("help", "help", OptValueType::None)
1409 .limit_options(3)
1410 .limit_other_args(1)
1411 .limit_unknown_options(3)
1412 .getopt([
1413 "--unknown",
1414 "--help=",
1415 "-ab",
1416 "-f",
1417 "one",
1418 "-ftwo",
1419 "--file",
1420 "three",
1421 "--file",
1422 "four",
1423 "other1",
1424 "other2",
1425 ]);
1426
1427 assert_eq!(3, parsed.options.len());
1428 assert_eq!(
1429 vec!["one", "two", "three"],
1430 parsed.options_value_all("file")
1431 );
1432
1433 assert_eq!(1, parsed.other.len());
1434 assert_eq!("other1", parsed.other[0]);
1435
1436 assert_eq!(3, parsed.unknown.len());
1437 assert_eq!(vec!["unknown", "help=", "a"], parsed.unknown);
1438 }
1439
1440 #[test]
1441 fn t_parsed_output_230() {
1442 let parsed = OptSpecs::new()
1443 .option("file", "f", OptValueType::Required)
1444 .option("file", "file", OptValueType::Required)
1445 .limit_options(3)
1446 .getopt(["-f", "one", "-ftwo", "--file=three", "--unknown"]);
1447
1448 assert_eq!(
1449 vec!["one", "two", "three"],
1450 parsed.options_value_all("file")
1451 );
1452 assert_eq!(1, parsed.unknown.len());
1453 assert_eq!("unknown", parsed.unknown[0]);
1454 }
1455
1456 #[test]
1457 fn t_parsed_output_240() {
1458 let parsed = OptSpecs::new()
1459 .option("help", "h", OptValueType::None)
1460 .limit_options(3)
1461 .getopt(["-xhhhh"]);
1462
1463 assert_eq!(3, parsed.options.len());
1464 assert_eq!(true, parsed.options_first("help").is_some());
1465 assert_eq!(1, parsed.unknown.len());
1466 assert_eq!("x", parsed.unknown[0]);
1467 }
1468
1469 #[test]
1470 fn t_parsed_output_250() {
1471 let parsed = OptSpecs::new()
1472 .option("help", "h", OptValueType::None)
1473 .limit_options(3)
1474 .getopt(["-x", "-h", "-h", "-h", "-h"]);
1475
1476 assert_eq!(3, parsed.options.len());
1477 assert_eq!(true, parsed.options_first("help").is_some());
1478 assert_eq!(1, parsed.unknown.len());
1479 assert_eq!("x", parsed.unknown[0]);
1480 }
1481
1482 #[test]
1483 fn t_parsed_output_260() {
1484 let parsed = OptSpecs::new()
1485 .option("help", "h", OptValueType::None)
1486 .limit_options(3)
1487 .getopt(["-x", "-h", "-h", "--", "-h", "-h"]);
1488
1489 assert_eq!(2, parsed.options.len());
1490 assert_eq!(true, parsed.options_first("help").is_some());
1491 assert_eq!(2, parsed.other.len());
1492 assert_eq!(vec!["-h", "-h"], parsed.other);
1493 assert_eq!(1, parsed.unknown.len());
1494 assert_eq!("x", parsed.unknown[0]);
1495 }
1496
1497 #[test]
1498 fn t_parsed_output_270() {
1499 let parsed = OptSpecs::new()
1500 .flag(OptFlags::OptionsEverywhere)
1501 .option("help", "h", OptValueType::None)
1502 .option("file", "f", OptValueType::Required)
1503 .limit_options(1)
1504 .limit_other_args(2)
1505 .limit_unknown_options(1)
1506 .getopt(["bar", "-habf", "123", "foo"]);
1507
1508 // "123" must be parsed as "f" option's value even though it is
1509 // beyond limit_options.
1510 assert_eq!(true, parsed.options_first("help").is_some());
1511 assert_eq!(false, parsed.options_first("file").is_some());
1512 assert_eq!(2, parsed.other.len());
1513 assert_eq!("bar", parsed.other[0]);
1514 assert_eq!("foo", parsed.other[1]);
1515 assert_eq!(1, parsed.unknown.len());
1516 assert_eq!("a", parsed.unknown[0]);
1517 }
1518
1519 #[test]
1520 fn t_parsed_output_280() {
1521 let parsed = OptSpecs::new()
1522 .flag(OptFlags::OptionsEverywhere)
1523 .option("help", "help", OptValueType::None)
1524 .option("file", "file", OptValueType::Required)
1525 .limit_options(1)
1526 .limit_other_args(2)
1527 .limit_unknown_options(1)
1528 .getopt(["bar", "--help", "-ab", "--file", "123", "foo"]);
1529
1530 // "123" must be parsed as "--file" option's value even though
1531 // it is beyond limit_options.
1532 assert_eq!(true, parsed.options_first("help").is_some());
1533 assert_eq!(false, parsed.options_first("file").is_some());
1534 assert_eq!(2, parsed.other.len());
1535 assert_eq!("bar", parsed.other[0]);
1536 assert_eq!("foo", parsed.other[1]);
1537 assert_eq!(1, parsed.unknown.len());
1538 assert_eq!("a", parsed.unknown[0]);
1539 }
1540}