just_getopt/
lib.rs

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