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_options() {
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    format,
355    string::{String, ToString},
356    vec::Vec,
357};
358
359/// Specification for program's valid command-line options.
360///
361/// An instance of this struct is needed before command-line options can
362/// be parsed. Instances are created with function [`OptSpecs::new`] and
363/// they are modified with [`option`](OptSpecs::option) and other
364/// methods
365///
366/// The struct instance is used when parsing the command line given by
367/// program's user. The parser methods is [`getopt`](OptSpecs::getopt).
368
369#[derive(Debug, PartialEq)]
370pub struct OptSpecs {
371    options: Vec<OptSpec>,
372    flags: Vec<OptFlags>,
373    option_limit: u32,
374    other_limit: u32,
375    unknown_limit: u32,
376}
377
378const COUNTER_LIMIT: u32 = u32::MAX;
379
380#[derive(Debug, PartialEq)]
381struct OptSpec {
382    id: String,
383    name: String,
384    value_type: OptValue,
385}
386
387/// Option's value type.
388///
389/// Usually used with [`OptSpecs::option`] method. Variants of this enum
390/// define if and how an option accepts a value.
391
392#[derive(Debug, PartialEq)]
393#[non_exhaustive]
394pub enum OptValue {
395    /// Option does not accept a value.
396    None,
397    /// Option accepts an optional value.
398    Optional,
399    /// Option accepts an optional value but empty string is not
400    /// considered a value.
401    OptionalNonEmpty,
402    /// Option requires a value.
403    Required,
404    /// Option requires a value but empty string is not considered a
405    /// value.
406    RequiredNonEmpty,
407}
408
409/// Flags for changing command-line parser's behavior.
410///
411/// Usually used with [`OptSpecs::flag`] method. Variants of this enum
412/// are general configuration flags that change command-line parser's
413/// behavior.
414
415#[derive(Debug, PartialEq)]
416#[non_exhaustive]
417pub enum OptFlags {
418    /// Accept command-line options and other arguments in mixed order
419    /// in the command line. That is, options can come after non-option
420    /// arguments.
421    ///
422    /// This is not the default behavior. By default the first
423    /// non-option argument in the command line stops option parsing and
424    /// the rest of the command line is parsed as non-options (other
425    /// arguments), even if they look like options.
426    OptionsEverywhere,
427
428    /// Long options don't need to be written in full in the command
429    /// line. They can be shortened as long as there are enough
430    /// characters to find a unique prefix match. If there are more than
431    /// one match the option given in the command line is classified as
432    /// unknown.
433    PrefixMatchLongOptions,
434}
435
436impl OptSpecs {
437    /// Create and return a new instance of [`OptSpecs`] struct.
438    ///
439    /// The created instance is "empty" and does not contain any
440    /// specifications for command-line options. Apply
441    /// [`option`](OptSpecs::option) or other methods to make it useful
442    /// for parsing command-line.
443    pub fn new() -> Self {
444        Self {
445            options: Vec::with_capacity(5),
446            flags: Vec::with_capacity(2),
447            option_limit: COUNTER_LIMIT,
448            other_limit: COUNTER_LIMIT,
449            unknown_limit: COUNTER_LIMIT,
450        }
451    }
452
453    /// Add an option specification for [`OptSpecs`].
454    ///
455    /// The method requires three arguments:
456    ///
457    ///  1. `id`: Programmer's identifier string for the option. Later,
458    ///     after parsing the command line, the identifier is used to
459    ///     match if this particular option was present in the
460    ///     command-line.
461    ///
462    ///     Several options may have the same identifier string. This
463    ///     makes sense when different option names in the command line
464    ///     represent the same meaning, like `-h` and `--help` for
465    ///     printing program's help message.
466    ///
467    ///  2. `name`: Option's name string in the command line (without
468    ///     prefix). If the string is a single character (like `h`) it
469    ///     defines a short option which is entered as `-h` in the
470    ///     command line. If there are more than one character in the
471    ///     string it defines a long option name (like `help`) which is
472    ///     entered as `--help` in the command line.
473    ///
474    ///     All options must have a unique `name` string. This method
475    ///     will panic if the same `name` is added twice. The method
476    ///     will also panic if the `name` string contains illegal
477    ///     characters. Space characters are not accepted. A short
478    ///     option name can't be `-` and long option names can't have
479    ///     any `=` characters nor `-` as their first character.
480    ///
481    ///  3. `value_type`: The argument is a variant of enum [`OptValue`]
482    ///     and it defines if and how this option accepts a value. See
483    ///     the enum's documentation for more information.
484    ///
485    /// The return value is the same struct instance which was modified.
486    pub fn option(mut self, id: &str, name: &str, value_type: OptValue) -> Self {
487        assert!(
488            id.chars().count() > 0,
489            "Option's \"id\" must be at least 1 character long."
490        );
491
492        match name.chars().count() {
493            0 => panic!("Option's \"name\" must be at least 1 character long."),
494            1 => assert!(
495                parser::is_valid_short_option_name(name),
496                "Not a valid short option name."
497            ),
498            _ => assert!(
499                parser::is_valid_long_option_name(name),
500                "Not a valid long option name."
501            ),
502        }
503
504        if self.options.iter().any(|o| o.name == name) {
505            panic!("No duplicates allowed for option's \"name\".")
506        }
507
508        self.options.push(OptSpec {
509            id: id.to_string(),
510            name: name.to_string(),
511            value_type,
512        });
513        self
514    }
515
516    /// Add a flag that changes parser's behavior.
517    ///
518    /// Method's only argument `flag` is a variant of enum [`OptFlags`]
519    /// and it is a configuration flag that changes parser's general
520    /// behavior. See the enum's documentation for more information.
521    ///
522    /// The return value is the same struct instance which was modified.
523    pub fn flag(mut self, flag: OptFlags) -> Self {
524        if !self.flags.contains(&flag) {
525            self.flags.push(flag);
526        }
527        self
528    }
529
530    fn is_flag(&self, flag: OptFlags) -> bool {
531        self.flags.contains(&flag)
532    }
533
534    /// Maximum number of valid options.
535    ///
536    /// Method's argument `limit` sets the maximum number of valid
537    /// options to collect from the command line. The rest is ignored.
538    /// This doesn't include unknown options (see
539    /// [`limit_unknown_options`](OptSpecs::limit_unknown_options)).
540    ///
541    /// The return value is the same struct instance which was modified.
542    pub fn limit_options(mut self, limit: u32) -> Self {
543        self.option_limit = limit;
544        self
545    }
546
547    /// Maximum number of other command-line arguments.
548    ///
549    /// Method's argument `limit` sets the maximum number of other
550    /// (non-option) arguments to collect from the command line. The
551    /// rest is ignored.
552    ///
553    /// Note: If your program accepts *n* number of command-line
554    /// argument (apart from options) you could set this limit to *n +
555    /// 1*. This way you know if there were more arguments than needed
556    /// and can inform program's user about that. There is no need to
557    /// collect more arguments.
558    ///
559    /// The return value is the same struct instance which was modified.
560    pub fn limit_other_args(mut self, limit: u32) -> Self {
561        self.other_limit = limit;
562        self
563    }
564
565    /// Maximum number of unknown options.
566    ///
567    /// Method's argument `limit` sets the maximum number of unique
568    /// unknown options to collect from the command line. Duplicates are
569    /// not collected.
570    ///
571    /// Note: If you want to stop your program if it notices just one
572    /// unknown option you can set this limit to 1. There is probably no
573    /// need to collect more of them.
574    ///
575    /// The return value is the same struct instance which was modified.
576    pub fn limit_unknown_options(mut self, limit: u32) -> Self {
577        self.unknown_limit = limit;
578        self
579    }
580
581    /// Getopt-parse an iterable item as command line arguments.
582    ///
583    /// This method's argument `args` is of any type that implements
584    /// trait [`IntoIterator`] and that has items of type that
585    /// implements trait [`ToString`]. For example, argument `args` can
586    /// be a vector or an iterator such as command-line arguments
587    /// returned by [`std::env::args`].
588    ///
589    /// The return value is an [`Args`] struct which represents the
590    /// command-line information in organized form.
591    pub fn getopt<I, S>(&self, args: I) -> Args
592    where
593        I: IntoIterator<Item = S>,
594        S: ToString,
595    {
596        parser::parse(self, args.into_iter().map(|i| i.to_string()))
597    }
598
599    fn get_short_option_match(&self, name: &str) -> Option<&OptSpec> {
600        if name.chars().count() != 1 {
601            return None;
602        }
603        self.options.iter().find(|e| e.name == name)
604    }
605
606    fn get_long_option_match(&self, name: &str) -> Option<&OptSpec> {
607        if name.chars().count() < 2 {
608            return None;
609        }
610        self.options.iter().find(|e| e.name == name)
611    }
612
613    fn get_long_option_prefix_match(&self, name: &str) -> Option<&OptSpec> {
614        if name.chars().count() < 2 {
615            return None;
616        }
617
618        if let Some(exact) = self.get_long_option_match(name) {
619            return Some(exact);
620        }
621
622        let mut result = None;
623
624        for e in &self.options {
625            if e.name.starts_with(name) {
626                if result.is_none() {
627                    result = Some(e);
628                } else {
629                    return None;
630                }
631            }
632        }
633        result
634    }
635}
636
637impl Default for OptSpecs {
638    fn default() -> Self {
639        Self::new()
640    }
641}
642
643/// Parsed command line in organized form.
644///
645/// Instances of this struct are usually created with
646/// [`OptSpecs::getopt`] method and an instance represents the parsed
647/// output in organized form. See each field's documentation for more
648/// information.
649///
650/// Programmers can use the parsed output ([`Args`] struct) any way they
651/// like. There are some methods for convenience.
652
653#[derive(Debug, PartialEq)]
654pub struct Args {
655    /// A vector of valid command-line options.
656    ///
657    /// Elements of this vector are [`Opt`] structs which each
658    /// represents a single command-line option. Elements are in the
659    /// same order as given (by program's user) in the command line. The
660    /// vector is empty if the parser didn't find any valid command-line
661    /// options.
662    pub options: Vec<Opt>,
663
664    /// A vector of other arguments (non-options).
665    ///
666    /// Each element of the vector is a single non-option argument
667    /// string in the same order as given (by program's user) in the
668    /// command line. The vector is empty if the parser didn't find any
669    /// non-option arguments.
670    pub other: Vec<String>,
671
672    /// Unknown options.
673    ///
674    /// Command-line arguments that look like options but were not part
675    /// of [`OptSpecs`] specification are classified as unknown. They
676    /// are listed in this vector. Possible duplicate unknown options
677    /// have been filtered out.
678    ///
679    /// Each element is the name string for the option (without `-` or
680    /// `--` prefix). For unknown short options the element is a
681    /// single-character string. For unknown long options the string has
682    /// more than one character. The whole vector is empty if there were
683    /// no unknown options.
684    ///
685    /// If a long option does not accept a value (that is, its value
686    /// type is [`OptValue::None`]) but user gives it a value with
687    /// equal sign notation (`--foo=`), that option is classified as
688    /// unknown and it will be in this field's vector with name `foo=`.
689    pub unknown: Vec<String>,
690}
691
692impl Args {
693    fn new() -> Self {
694        Args {
695            options: Vec::new(),
696            other: Vec::new(),
697            unknown: Vec::new(),
698        }
699    }
700
701    /// Find options with missing required value.
702    ///
703    /// This method finds all (otherwise valid) options which require a
704    /// value but the value is missing. That is, [`OptSpecs`] struct
705    /// specification defined that an option requires a value but
706    /// program's user didn't give one in the command line. Such thing
707    /// can happen if an option like `--file` is the last argument in
708    /// the command line and that option requires a value.
709    ///
710    /// If option's value type is [`OptValue::Required`] the empty
711    /// string `""` is not classified as missing value because it can be
712    /// valid user input in many situations. If option's value type is
713    /// [`OptValue::RequiredNonEmpty`] the empty string that was given
714    /// in the command line will be classified as missing value.
715    ///
716    /// The return value implements the [`DoubleEndedIterator`] trait
717    /// (possibly empty, if no matches) and each item is a reference to
718    /// [`Opt`] struct in the original [`Args::options`] field. Items
719    /// are in the same order as in the parsed command line. You can
720    /// collect the iterator to a vector by applying method
721    /// [`collect`](core::iter::Iterator::collect)`::<Vec<&Opt>>()`.
722    pub fn required_value_missing(&self) -> impl DoubleEndedIterator<Item = &Opt> {
723        self.options
724            .iter()
725            .filter(|opt| opt.value_required && opt.value.is_none())
726    }
727
728    /// Return boolean whether option with the given `id` exists.
729    ///
730    /// This is functionally the same as
731    /// [`options_first`](Args::options_first)`(id).is_some()`.
732    pub fn option_exists(&self, id: &str) -> bool {
733        self.options.iter().any(|opt| opt.id == id)
734    }
735
736    /// Find all options with the given `id`.
737    ///
738    /// Find all options which have the identifier `id`. (Option
739    /// identifiers have been defined in [`OptSpecs`] struct before
740    /// parsing.)
741    ///
742    /// The return value implements the [`DoubleEndedIterator`] trait
743    /// (possibly empty, if no matches) and each item is a reference to
744    /// [`Opt`] struct in the original [`Args::options`] field. Items
745    /// are in the same order as in the parsed command line. You can
746    /// collect the iterator to a vector by applying method
747    /// [`collect`](core::iter::Iterator::collect)`::<Vec<&Opt>>()`.
748    pub fn options_all<'a>(&'a self, id: &'a str) -> impl DoubleEndedIterator<Item = &'a Opt> {
749        self.options.iter().filter(move |opt| opt.id == id)
750    }
751
752    /// Find the first option with the given `id`.
753    ///
754    /// Find and return the first match for option `id` in command-line
755    /// arguments' order. (Options' identifiers have been defined in
756    /// [`OptSpecs`] struct before parsing.)
757    ///
758    /// The return value is a variant of enum [`Option`]. Their
759    /// meanings:
760    ///
761    ///   - `None`: No options found with the given `id`.
762    ///
763    ///   - `Some(&Opt)`: An option was found with the given `id` and a
764    ///     reference is provided to its [`Opt`] struct in the original
765    ///     [`Args::options`] field.
766    pub fn options_first(&self, id: &str) -> Option<&Opt> {
767        self.options.iter().find(|opt| opt.id == id)
768    }
769
770    /// Find the last option with the given `id`.
771    ///
772    /// This is similar to [`options_first`](Args::options_first) method
773    /// but this returns the last match in command-line arguments'
774    /// order.
775    pub fn options_last(&self, id: &str) -> Option<&Opt> {
776        self.options.iter().rev().find(|opt| opt.id == id)
777    }
778
779    /// Find all values for options with the given `id`.
780    ///
781    /// Find all options which match the identifier `id` and which also
782    /// have a value assigned. (Options' identifiers have been defined
783    /// in [`OptSpecs`] struct before parsing.)
784    ///
785    /// The return value implements the [`DoubleEndedIterator`] trait
786    /// (possibly empty, if no matches) and each item is a reference to
787    /// string in [`Opt::value`] field in the original [`Args::options`]
788    /// field. Items are in the same order as in the parsed command
789    /// line. You can collect the iterator to a vector by applying
790    /// method
791    /// [`collect`](core::iter::Iterator::collect)`::<Vec<&String>>()`.
792    pub fn options_value_all<'a>(
793        &'a self,
794        id: &'a str,
795    ) -> impl DoubleEndedIterator<Item = &'a String> {
796        self.options.iter().filter_map(move |opt| {
797            if opt.id == id {
798                opt.value.as_ref()
799            } else {
800                None
801            }
802        })
803    }
804
805    /// Find the first option with a value for given option `id`.
806    ///
807    /// Find the first option with the identifier `id` and which has a
808    /// value assigned. (Options' identifiers have been defined in
809    /// [`OptSpecs`] struct before parsing.) Method's return value is a
810    /// variant of enum [`Option`] which are:
811    ///
812    ///   - `None`: No options found with the given `id` and a value
813    ///     assigned. Note that there could be options for the same `id`
814    ///     but they don't have a value.
815    ///
816    ///   - `Some(&String)`: An option was found with the given `id` and
817    ///     the option has a value assigned. A reference is provided to
818    ///     the string value in the [`Opt::value`] field in the original
819    ///     [`Args::options`] field.
820    pub fn options_value_first(&self, id: &str) -> Option<&String> {
821        match self
822            .options
823            .iter()
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    /// Find the last option with a value for given option `id`.
832    ///
833    /// This is similar to
834    /// [`options_value_first`](Args::options_value_first) method but
835    /// this method finds and returns the last option's value.
836    ///
837    /// Note: Program's user may give the same option several times in
838    /// the command line. If the option accepts a value it may be
839    /// suitable to consider only the last value relevant. (Or the
840    /// first, or maybe print an error message for providing several,
841    /// possibly conflicting, values.)
842    pub fn options_value_last(&self, id: &str) -> Option<&String> {
843        match self
844            .options
845            .iter()
846            .rev()
847            .find(|opt| opt.id == id && opt.value.is_some())
848        {
849            Some(o) => o.value.as_ref(),
850            None => None,
851        }
852    }
853
854    /// List unknown options with prefix.
855    ///
856    /// Command-line arguments that look like options but were not part
857    /// of [`OptSpecs`] specification are classified as unknown. They
858    /// can be listed with this method which iterates over
859    /// [`Args::unknown`] field and outputs options with their prefix
860    /// (`-` or `--`). Possible duplicate unknown options have been
861    /// filtered out.
862    ///
863    /// The return value implements the [`DoubleEndedIterator`] trait
864    /// (possibly empty, if no matches). Iterator's each item creates a
865    /// new `String` which has the short or long option prefix (`-` or
866    /// `--`) followed by the option name string. You can collect the
867    /// iterator to a vector by applying method
868    /// [`collect`](core::iter::Iterator::collect)`::<Vec<String>>()`.
869    pub fn unknown_options(&self) -> impl DoubleEndedIterator<Item = String> + '_ {
870        self.unknown.iter().map(|o| match o.chars().count() {
871            0 => panic!("zero-length string in Args::unknown"),
872            1 => format!("-{}", o),
873            _ => format!("--{}", o),
874        })
875    }
876}
877
878/// Structured option information.
879///
880/// This [`Opt`] struct represents organized information about single
881/// command-line option. Instances of this struct are usually created by
882/// [`OptSpecs::getopt`] method which returns an [`Args`] struct which
883/// have these [`Opt`] structs inside.
884///
885/// A programmer may need these when examining parsed command-line
886/// options. See the documentation of individual fields for more
887/// information. Also see [`Args`] struct and its methods.
888
889#[derive(Debug, PartialEq)]
890pub struct Opt {
891    /// Identifier for the option.
892    ///
893    /// Identifiers are defined with [`OptSpecs::option`] method before
894    /// parsing command-line arguments. After [`OptSpecs::getopt`]
895    /// parsing the same identifier is copied here and it confirms that
896    /// the option was indeed given in the command line.
897    pub id: String,
898
899    /// Option's name in the parsed command line.
900    ///
901    /// Option's name that was used in the command line. For short
902    /// options this is a single-character string. For long options the
903    /// name has more than one characters.
904    pub name: String,
905
906    /// The option requires a value.
907    ///
908    /// `true` means that the option was defined with value type
909    /// [`OptValue::Required`]. See [`OptSpecs::flag`] method for
910    /// more information. This field does not guarantee that there
911    /// actually was a value for the option in the command line.
912    pub value_required: bool,
913
914    /// Option's value.
915    ///
916    /// The value is a variant of enum [`Option`]. Value `None` means
917    /// that there is no value for the option. Value `Some(String)`
918    /// provides a value.
919    pub value: Option<String>,
920}
921
922#[cfg(test)]
923mod tests {
924    use super::*;
925    use alloc::vec;
926
927    #[test]
928    fn create_optspecs_010() {
929        let mut spec;
930        let mut expect;
931
932        spec = OptSpecs::new().option("help", "help", OptValue::None);
933        expect = OptSpec {
934            id: String::from("help"),
935            name: String::from("help"),
936            value_type: OptValue::None,
937        };
938        assert_eq!(1, spec.options.len());
939        assert_eq!(&expect, &spec.options[0]);
940        assert_eq!(COUNTER_LIMIT, spec.option_limit);
941        assert_eq!(COUNTER_LIMIT, spec.other_limit);
942        assert_eq!(COUNTER_LIMIT, spec.unknown_limit);
943
944        spec = spec.option("file", "f", OptValue::Optional);
945        expect = OptSpec {
946            id: String::from("file"),
947            name: String::from("f"),
948            value_type: OptValue::Optional,
949        };
950        assert_eq!(2, spec.options.len());
951        assert_eq!(&expect, &spec.options[1]);
952
953        spec = spec.option("file", "file", OptValue::Required);
954        expect = OptSpec {
955            id: String::from("file"),
956            name: String::from("file"),
957            value_type: OptValue::Required,
958        };
959        assert_eq!(3, spec.options.len());
960        assert_eq!(&expect, &spec.options[2]);
961
962        spec = spec.flag(OptFlags::OptionsEverywhere);
963        assert_eq!(1, spec.flags.len()); // Length 1
964        assert!(spec.is_flag(OptFlags::OptionsEverywhere));
965        spec = spec.flag(OptFlags::PrefixMatchLongOptions);
966        assert_eq!(2, spec.flags.len()); // Length 2
967        assert!(spec.is_flag(OptFlags::PrefixMatchLongOptions));
968        spec = spec.flag(OptFlags::OptionsEverywhere);
969        spec = spec.flag(OptFlags::PrefixMatchLongOptions);
970        assert_eq!(2, spec.flags.len()); // Length still 2
971
972        spec = spec.limit_options(9);
973        spec = spec.limit_other_args(10);
974        spec = spec.limit_unknown_options(3);
975        assert_eq!(9, spec.option_limit);
976        assert_eq!(10, spec.other_limit);
977        assert_eq!(3, spec.unknown_limit);
978    }
979
980    #[test]
981    #[should_panic]
982    fn create_optspecs_020() {
983        OptSpecs::new().option("", "h", OptValue::None);
984    }
985
986    #[test]
987    #[should_panic]
988    fn create_optspecs_030() {
989        OptSpecs::new()
990            .option("h", "h", OptValue::None)
991            .option("h", "h", OptValue::None);
992    }
993
994    #[test]
995    #[should_panic]
996    fn create_optspecs_040() {
997        OptSpecs::new().option("h", "", OptValue::None);
998    }
999
1000    #[test]
1001    #[should_panic]
1002    fn create_optspecs_050() {
1003        OptSpecs::new().option("h", "-", OptValue::None);
1004    }
1005
1006    #[test]
1007    #[should_panic]
1008    fn create_optspecs_060() {
1009        OptSpecs::new().option("h", " ", OptValue::None);
1010    }
1011
1012    #[test]
1013    #[should_panic]
1014    fn create_optspecs_070() {
1015        OptSpecs::new().option("h", "hh ", OptValue::None);
1016    }
1017
1018    #[test]
1019    #[should_panic]
1020    fn create_optspecs_080() {
1021        OptSpecs::new().option("h", "hh=hh", OptValue::None);
1022    }
1023
1024    #[test]
1025    fn is_flag() {
1026        let mut spec = OptSpecs::new().flag(OptFlags::OptionsEverywhere);
1027        assert!(spec.is_flag(OptFlags::OptionsEverywhere));
1028
1029        spec = spec.flag(OptFlags::PrefixMatchLongOptions);
1030        assert!(spec.is_flag(OptFlags::PrefixMatchLongOptions));
1031    }
1032
1033    #[test]
1034    fn parsed_output_010() {
1035        let parsed = OptSpecs::new()
1036            .option("help", "h", OptValue::None)
1037            .option("help", "help", OptValue::None)
1038            .option("file", "f", OptValue::Required)
1039            .option("file", "file", OptValue::Required)
1040            .getopt(["-h", "--help", "-f123", "-f", "456", "foo", "bar"]);
1041
1042        assert!(parsed.option_exists("help"));
1043        assert!(parsed.option_exists("file"));
1044        assert!(!parsed.option_exists("x"));
1045
1046        assert_eq!("h", parsed.options_first("help").unwrap().name);
1047        assert_eq!("help", parsed.options_last("help").unwrap().name);
1048        assert_eq!("help", parsed.options_first("help").unwrap().id);
1049        assert_eq!("help", parsed.options_last("help").unwrap().id);
1050        assert_eq!(false, parsed.options_first("help").unwrap().value_required);
1051        assert_eq!(false, parsed.options_last("help").unwrap().value_required);
1052
1053        assert_eq!("f", parsed.options_first("file").unwrap().name);
1054        assert_eq!(
1055            "123",
1056            parsed.options_first("file").unwrap().value.clone().unwrap()
1057        );
1058        assert_eq!(
1059            "456",
1060            parsed.options_last("file").unwrap().value.clone().unwrap()
1061        );
1062        assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1063
1064        assert_eq!("foo", parsed.other[0]);
1065        assert_eq!("bar", parsed.other[1]);
1066    }
1067
1068    #[test]
1069    fn parsed_output_020() {
1070        let parsed = OptSpecs::new()
1071            .limit_options(1)
1072            .limit_other_args(2)
1073            .option("help", "h", OptValue::None)
1074            .getopt(["-h", "foo", "-h"]);
1075
1076        assert_eq!("h", parsed.options_first("help").unwrap().name);
1077        assert_eq!(2, parsed.other.len());
1078        assert_eq!("foo", parsed.other[0]);
1079        assert_eq!("-h", parsed.other[1]);
1080    }
1081
1082    #[test]
1083    fn parsed_output_030() {
1084        let parsed = OptSpecs::new()
1085            .flag(OptFlags::OptionsEverywhere)
1086            .option("help", "h", OptValue::None)
1087            .option("help", "help", OptValue::None)
1088            .option("file", "f", OptValue::Required)
1089            .option("file", "file", OptValue::Required)
1090            .getopt(["-h", "foo", "--help", "--file=123", "bar", "--file", "456"]);
1091
1092        assert_eq!("h", parsed.options_first("help").unwrap().name);
1093        assert_eq!("help", parsed.options_last("help").unwrap().name);
1094        assert_eq!(
1095            "123",
1096            parsed.options_first("file").unwrap().value.clone().unwrap()
1097        );
1098        assert_eq!(
1099            "456",
1100            parsed.options_last("file").unwrap().value.clone().unwrap()
1101        );
1102        assert_eq!("foo", parsed.other[0]);
1103        assert_eq!("bar", parsed.other[1]);
1104    }
1105
1106    #[test]
1107    fn parsed_output_040() {
1108        let parsed = OptSpecs::new()
1109            .option("debug", "d", OptValue::Optional)
1110            .option("verbose", "verbose", OptValue::Optional)
1111            .getopt(["-d1", "-d", "--verbose", "--verbose=123"]);
1112
1113        assert_eq!(
1114            "1",
1115            parsed
1116                .options_first("debug")
1117                .unwrap()
1118                .value
1119                .clone()
1120                .unwrap()
1121        );
1122        assert_eq!(None, parsed.options_last("debug").unwrap().value);
1123        assert_eq!(false, parsed.options_last("debug").unwrap().value_required);
1124
1125        assert_eq!(None, parsed.options_first("verbose").unwrap().value);
1126        assert_eq!(
1127            "123",
1128            parsed
1129                .options_last("verbose")
1130                .unwrap()
1131                .value
1132                .clone()
1133                .unwrap()
1134        );
1135        assert_eq!(
1136            false,
1137            parsed.options_last("verbose").unwrap().value_required
1138        );
1139    }
1140
1141    #[test]
1142    fn parsed_output_050() {
1143        let parsed = OptSpecs::new()
1144            .option("debug", "d", OptValue::Optional)
1145            .getopt(["-abcd", "-adbc"]);
1146
1147        assert_eq!(None, parsed.options_first("debug").unwrap().value);
1148        assert_eq!(
1149            "bc",
1150            parsed.options_last("debug").unwrap().value.clone().unwrap()
1151        );
1152
1153        assert_eq!(3, parsed.unknown.len());
1154        assert_eq!("a", parsed.unknown[0]);
1155        assert_eq!("b", parsed.unknown[1]);
1156        assert_eq!("c", parsed.unknown[2]);
1157    }
1158
1159    #[test]
1160    fn parsed_output_060() {
1161        let parsed = OptSpecs::new()
1162            .option("aaa", "bbb", OptValue::None)
1163            .option("aaa", "c", OptValue::None)
1164            .option("aaa", "d", OptValue::None)
1165            .option("aaa", "eee", OptValue::None)
1166            .getopt(["--bbb", "-cd", "--eee"]);
1167
1168        let m: Vec<&Opt> = parsed.options_all("aaa").collect();
1169        assert_eq!("bbb", m[0].name);
1170        assert_eq!("c", m[1].name);
1171        assert_eq!("d", m[2].name);
1172        assert_eq!("eee", m[3].name);
1173    }
1174
1175    #[test]
1176    fn parsed_output_070() {
1177        let parsed = OptSpecs::new()
1178            .flag(OptFlags::PrefixMatchLongOptions)
1179            .option("version", "version", OptValue::None)
1180            .option("verbose", "verbose", OptValue::None)
1181            .getopt(["--ver", "--verb", "--versi", "--verbose"]);
1182
1183        assert_eq!("ver", parsed.unknown[0]);
1184        assert_eq!("verb", parsed.options_first("verbose").unwrap().name);
1185        assert_eq!("verbose", parsed.options_last("verbose").unwrap().name);
1186        assert_eq!("version", parsed.options_first("version").unwrap().id);
1187        assert_eq!("versi", parsed.options_first("version").unwrap().name);
1188    }
1189
1190    #[test]
1191    fn parsed_output_080() {
1192        let parsed = OptSpecs::new()
1193            // .flag(OptFlags::PrefixMatchLongOptions) Must be commented!
1194            .option("version", "version", OptValue::None)
1195            .option("verbose", "verbose", OptValue::None)
1196            .getopt(["--version", "--ver", "--verb", "--versi", "--verbose"]);
1197
1198        assert_eq!("ver", parsed.unknown[0]);
1199        assert_eq!("verb", parsed.unknown[1]);
1200        assert_eq!("versi", parsed.unknown[2]);
1201        assert_eq!("version", parsed.options_first("version").unwrap().name);
1202        assert_eq!("verbose", parsed.options_first("verbose").unwrap().name);
1203    }
1204
1205    #[test]
1206    fn parsed_output_085() {
1207        let parsed = OptSpecs::new()
1208            .flag(OptFlags::PrefixMatchLongOptions)
1209            .option("foo1", "foo", OptValue::None)
1210            .option("foo2", "foo-longer", OptValue::None)
1211            .getopt(["--fo", "--foo", "--foo-", "--foo-longer"]);
1212
1213        assert_eq!("fo", parsed.unknown[0]);
1214        assert_eq!("foo", parsed.options_first("foo1").unwrap().name);
1215        assert_eq!("foo", parsed.options_last("foo1").unwrap().name);
1216        assert_eq!("foo-", parsed.options_first("foo2").unwrap().name);
1217        assert_eq!("foo-longer", parsed.options_last("foo2").unwrap().name);
1218    }
1219
1220    #[test]
1221    fn parsed_output_090() {
1222        let parsed = OptSpecs::new()
1223            .flag(OptFlags::OptionsEverywhere)
1224            .option("help", "h", OptValue::None)
1225            .option("file", "file", OptValue::Required)
1226            .getopt(["-h", "foo", "--file=123", "--", "bar", "--file", "456"]);
1227
1228        assert_eq!("h", parsed.options_first("help").unwrap().name);
1229        assert_eq!("file", parsed.options_first("file").unwrap().name);
1230        assert_eq!(
1231            "123",
1232            parsed.options_first("file").unwrap().value.clone().unwrap()
1233        );
1234
1235        assert_eq!(4, parsed.other.len());
1236        assert_eq!("foo", parsed.other[0]);
1237        assert_eq!("bar", parsed.other[1]);
1238        assert_eq!("--file", parsed.other[2]);
1239        assert_eq!("456", parsed.other[3]);
1240    }
1241
1242    #[test]
1243    fn parsed_output_100() {
1244        let parsed = OptSpecs::new()
1245            .option("file", "file", OptValue::Required)
1246            .getopt(["--file=", "--file"]);
1247
1248        assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1249        assert_eq!(
1250            "",
1251            parsed.options_first("file").unwrap().value.clone().unwrap()
1252        );
1253        assert_eq!(None, parsed.options_last("file").unwrap().value);
1254    }
1255
1256    #[test]
1257    fn parsed_output_110() {
1258        let parsed = OptSpecs::new()
1259            .option("file", "f", OptValue::Required)
1260            .option("debug", "d", OptValue::Required)
1261            .getopt(["-fx", "-d", "", "-f"]);
1262
1263        assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1264        assert_eq!(
1265            "x",
1266            parsed.options_first("file").unwrap().value.clone().unwrap()
1267        );
1268        assert_eq!(None, parsed.options_last("file").unwrap().value);
1269        assert_eq!(
1270            "",
1271            parsed
1272                .options_first("debug")
1273                .unwrap()
1274                .value
1275                .clone()
1276                .unwrap()
1277        );
1278    }
1279
1280    #[test]
1281    fn parsed_output_120() {
1282        let parsed = OptSpecs::new()
1283            .option("file", "f", OptValue::Required)
1284            .option("debug", "d", OptValue::Required)
1285            .getopt(["-f123", "-d", "", "-f", "456", "-f"]);
1286
1287        let f: Vec<&String> = parsed.options_value_all("file").collect();
1288        assert_eq!(2, f.len());
1289        assert_eq!("123", f[0]);
1290        assert_eq!("456", f[1]);
1291
1292        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1293        assert_eq!(1, d.len());
1294        assert_eq!("", d[0]);
1295
1296        assert_eq!(None, parsed.options_last("file").unwrap().value);
1297        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1298        assert_eq!(1, m.len());
1299        assert_eq!("f", m[0].name);
1300    }
1301
1302    #[test]
1303    fn parsed_output_125() {
1304        let parsed = OptSpecs::new()
1305            .option("file", "f", OptValue::Required)
1306            .option("debug", "d", OptValue::RequiredNonEmpty)
1307            .getopt(["-f123", "-d", "", "-f", "456", "-f"]);
1308
1309        let f: Vec<&String> = parsed.options_value_all("file").collect();
1310        assert_eq!(2, f.len());
1311        assert_eq!("123", f[0]);
1312        assert_eq!("456", f[1]);
1313
1314        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1315        assert_eq!(0, d.len());
1316
1317        assert_eq!(None, parsed.options_last("file").unwrap().value);
1318        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1319        assert_eq!(2, m.len());
1320        assert_eq!("d", m[0].name);
1321        assert_eq!("f", m[1].name);
1322    }
1323
1324    #[test]
1325    fn parsed_output_130() {
1326        let parsed = OptSpecs::new()
1327            .option("file", "file", OptValue::Required)
1328            .option("debug", "debug", OptValue::Required)
1329            .getopt(["--file=123", "--debug", "", "--file", "456", "--file"]);
1330
1331        let f: Vec<&String> = parsed.options_value_all("file").collect();
1332        assert_eq!(2, f.len());
1333        assert_eq!("123", f[0]);
1334        assert_eq!("456", f[1]);
1335
1336        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1337        assert_eq!(1, d.len());
1338        assert_eq!("", d[0]);
1339
1340        assert_eq!(None, parsed.options_last("file").unwrap().value);
1341        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1342        assert_eq!(1, m.len());
1343        assert_eq!("file", m[0].name);
1344    }
1345
1346    #[test]
1347    fn parsed_output_135() {
1348        let parsed = OptSpecs::new()
1349            .option("file", "file", OptValue::RequiredNonEmpty)
1350            .option("debug", "debug", OptValue::RequiredNonEmpty)
1351            .getopt(["--file=123", "--debug", "", "--file", "456", "--file="]);
1352
1353        let f: Vec<&String> = parsed.options_value_all("file").collect();
1354        assert_eq!(2, f.len());
1355        assert_eq!("123", f[0]);
1356        assert_eq!("456", f[1]);
1357
1358        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1359        assert_eq!(0, d.len());
1360
1361        assert_eq!(None, parsed.options_last("file").unwrap().value);
1362        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1363        assert_eq!(2, m.len());
1364        assert_eq!("debug", m[0].name);
1365        assert_eq!("file", m[1].name);
1366    }
1367
1368    #[test]
1369    fn parsed_output_137() {
1370        let parsed = OptSpecs::new()
1371            .option("debug", "d", OptValue::OptionalNonEmpty)
1372            .option("debug", "debug", OptValue::OptionalNonEmpty)
1373            .getopt([
1374                "-d",
1375                "-d123",
1376                "--debug",
1377                "--debug=",
1378                "--debug=456",
1379                "--debug=",
1380            ]);
1381
1382        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1383        assert_eq!(2, d.len());
1384        assert_eq!("123", d[0]);
1385        assert_eq!("456", d[1]);
1386        assert_eq!("123", parsed.options_value_first("debug").unwrap());
1387        assert_eq!("456", parsed.options_value_last("debug").unwrap());
1388    }
1389
1390    #[test]
1391    fn parsed_output_140() {
1392        let parsed = OptSpecs::new()
1393            .flag(OptFlags::OptionsEverywhere)
1394            .option("debug", "d", OptValue::Optional)
1395            .option("debug", "debug", OptValue::Optional)
1396            .getopt([
1397                "-d",
1398                "-d123",
1399                "-d",
1400                "--debug",
1401                "--debug=",
1402                "foo",
1403                "--debug=456",
1404                "-d",
1405            ]);
1406
1407        let d: Vec<&Opt> = parsed.options_all("debug").collect();
1408        assert_eq!(7, d.len());
1409
1410        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1411        assert_eq!(3, d.len());
1412        assert_eq!("123", d[0]);
1413        assert_eq!("", d[1]);
1414        assert_eq!("456", d[2]);
1415        assert_eq!("123", parsed.options_value_first("debug").unwrap());
1416        assert_eq!("456", parsed.options_value_last("debug").unwrap());
1417
1418        assert_eq!(None, parsed.options_value_first("not-at-all"));
1419        assert_eq!(None, parsed.options_value_last("not-at-all"));
1420
1421        assert_eq!("foo", parsed.other[0]);
1422    }
1423
1424    #[test]
1425    fn parsed_output_150() {
1426        let parsed = OptSpecs::new().limit_unknown_options(6).getopt([
1427            "-abcd",
1428            "-e",
1429            "--debug",
1430            "-x", // Won't be listed in unknown because of limit.
1431            "--",
1432            "--debug=",
1433            "foo",
1434            "--debug=456",
1435        ]);
1436
1437        assert_eq!(0, parsed.options.len());
1438        assert_eq!(3, parsed.other.len());
1439        assert_eq!(6, parsed.unknown.len());
1440        assert_eq!(vec!["a", "b", "c", "d", "e", "debug"], parsed.unknown);
1441    }
1442
1443    #[test]
1444    fn parsed_output_160() {
1445        let parsed = OptSpecs::new()
1446            .option("file", "file", OptValue::Required)
1447            .getopt(["--file", "--", "--", "--"]);
1448
1449        assert_eq!(
1450            "--",
1451            parsed.options_first("file").unwrap().value.clone().unwrap()
1452        );
1453        assert_eq!(1, parsed.other.len());
1454        assert_eq!("--", parsed.other[0]);
1455
1456        assert_eq!(0, parsed.required_value_missing().count());
1457    }
1458
1459    #[test]
1460    fn parsed_output_170() {
1461        let parsed = OptSpecs::new().getopt(["foo", "bar"]);
1462
1463        assert_eq!(None, parsed.options_first("not-at-all"));
1464        assert_eq!(None, parsed.options_last("not-at-all"));
1465    }
1466
1467    #[test]
1468    fn parsed_output_180() {
1469        let parsed = OptSpecs::new()
1470            .limit_unknown_options(3)
1471            .option("bar", "bar", OptValue::None)
1472            .getopt(["-aaa", "--foo", "--foo", "--bar=", "--bar=", "-x"]);
1473
1474        assert_eq!(3, parsed.unknown.len());
1475        assert_eq!("a", parsed.unknown[0]);
1476        assert_eq!("foo", parsed.unknown[1]);
1477        assert_eq!("bar=", parsed.unknown[2]);
1478    }
1479
1480    #[test]
1481    fn parsed_output_190() {
1482        let parsed = OptSpecs::new()
1483            .option("äiti", "äiti", OptValue::Required)
1484            .option("€uro", "€uro", OptValue::Required)
1485            .getopt(["--äiti=ööö", "--€uro", "€€€", "--äiti", "ää", "--äiti"]);
1486
1487        let a: Vec<&String> = parsed.options_value_all("äiti").collect();
1488        assert_eq!(2, a.len());
1489        assert_eq!("ööö", a[0]);
1490        assert_eq!("ää", a[1]);
1491        assert_eq!("ööö", parsed.options_value_first("äiti").unwrap());
1492        assert_eq!("ää", parsed.options_value_last("äiti").unwrap());
1493
1494        let e: Vec<&String> = parsed.options_value_all("€uro").collect();
1495        assert_eq!(1, e.len());
1496        assert_eq!("€€€", e[0]);
1497        assert_eq!("€€€", parsed.options_value_first("€uro").unwrap());
1498        assert_eq!("€€€", parsed.options_value_last("€uro").unwrap());
1499
1500        assert_eq!(None, parsed.options_last("äiti").unwrap().value);
1501
1502        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1503        assert_eq!(1, m.len());
1504        assert_eq!("äiti", m[0].name);
1505        assert_eq!(None, m[0].value);
1506    }
1507
1508    #[test]
1509    fn parsed_output_195() {
1510        let parsed = OptSpecs::new().getopt(["-ä€", "--€uro", "äää", "€€€"]);
1511
1512        assert_eq!(2, parsed.other.len());
1513        assert_eq!("äää", parsed.other[0]);
1514        assert_eq!("€€€", parsed.other[1]);
1515
1516        assert_eq!(3, parsed.unknown.len());
1517        assert_eq!("ä", parsed.unknown[0]);
1518        assert_eq!("€", parsed.unknown[1]);
1519        assert_eq!("€uro", parsed.unknown[2]);
1520    }
1521
1522    #[test]
1523    fn parsed_output_200() {
1524        let parsed = OptSpecs::new().limit_other_args(5).getopt(1..10);
1525        assert_eq!(5, parsed.other.len());
1526        assert_eq!(vec!["1", "2", "3", "4", "5"], parsed.other);
1527    }
1528
1529    #[test]
1530    fn parsed_output_210() {
1531        let parsed = OptSpecs::new().limit_other_args(0).getopt(1..10);
1532        assert_eq!(0, parsed.other.len());
1533    }
1534
1535    #[test]
1536    fn parsed_output_220() {
1537        let parsed = OptSpecs::new()
1538            .option("file", "f", OptValue::Required)
1539            .option("file", "file", OptValue::Required)
1540            .option("help", "help", OptValue::None)
1541            .limit_options(3)
1542            .limit_other_args(1)
1543            .limit_unknown_options(3)
1544            .getopt([
1545                "--unknown",
1546                "--help=",
1547                "-ab",
1548                "-f",
1549                "one",
1550                "-ftwo",
1551                "--file",
1552                "three",
1553                "--file",
1554                "four",
1555                "other1",
1556                "other2",
1557            ]);
1558
1559        assert_eq!(3, parsed.options.len());
1560        assert_eq!(
1561            vec!["one", "two", "three"],
1562            parsed.options_value_all("file").collect::<Vec<&String>>()
1563        );
1564
1565        assert_eq!(1, parsed.other.len());
1566        assert_eq!("other1", parsed.other[0]);
1567
1568        assert_eq!(3, parsed.unknown.len());
1569        assert_eq!(vec!["unknown", "help=", "a"], parsed.unknown);
1570    }
1571
1572    #[test]
1573    fn parsed_output_230() {
1574        let parsed = OptSpecs::new()
1575            .option("file", "f", OptValue::Required)
1576            .option("file", "file", OptValue::Required)
1577            .limit_options(3)
1578            .getopt(["-f", "one", "-ftwo", "--file=three", "--unknown"]);
1579
1580        assert_eq!(
1581            vec!["one", "two", "three"],
1582            parsed.options_value_all("file").collect::<Vec<&String>>()
1583        );
1584        assert_eq!(1, parsed.unknown.len());
1585        assert_eq!("unknown", parsed.unknown[0]);
1586    }
1587
1588    #[test]
1589    fn parsed_output_240() {
1590        let parsed = OptSpecs::new()
1591            .option("help", "h", OptValue::None)
1592            .limit_options(3)
1593            .getopt(["-xhhhh"]);
1594
1595        assert_eq!(3, parsed.options.len());
1596        assert!(parsed.options_first("help").is_some());
1597        assert_eq!(1, parsed.unknown.len());
1598        assert_eq!("x", parsed.unknown[0]);
1599    }
1600
1601    #[test]
1602    fn parsed_output_250() {
1603        let parsed = OptSpecs::new()
1604            .option("help", "h", OptValue::None)
1605            .limit_options(3)
1606            .getopt(["-x", "-h", "-h", "-h", "-h"]);
1607
1608        assert_eq!(3, parsed.options.len());
1609        assert!(parsed.options_first("help").is_some());
1610        assert_eq!(1, parsed.unknown.len());
1611        assert_eq!("x", parsed.unknown[0]);
1612    }
1613
1614    #[test]
1615    fn parsed_output_260() {
1616        let parsed = OptSpecs::new()
1617            .option("help", "h", OptValue::None)
1618            .limit_options(3)
1619            .getopt(["-x", "-h", "-h", "--", "-h", "-h"]);
1620
1621        assert_eq!(2, parsed.options.len());
1622        assert!(parsed.options_first("help").is_some());
1623        assert_eq!(2, parsed.other.len());
1624        assert_eq!(vec!["-h", "-h"], parsed.other);
1625        assert_eq!(1, parsed.unknown.len());
1626        assert_eq!("x", parsed.unknown[0]);
1627    }
1628
1629    #[test]
1630    fn parsed_output_270() {
1631        let parsed = OptSpecs::new()
1632            .flag(OptFlags::OptionsEverywhere)
1633            .option("help", "h", OptValue::None)
1634            .option("file", "f", OptValue::Required)
1635            .limit_options(1)
1636            .limit_other_args(2)
1637            .limit_unknown_options(1)
1638            .getopt(["bar", "-habf", "123", "foo"]);
1639
1640        // "123" must be parsed as "f" option's value even though it is
1641        // beyond limit_options.
1642        assert!(parsed.options_first("help").is_some());
1643        assert!(parsed.options_first("file").is_none());
1644        assert_eq!(2, parsed.other.len());
1645        assert_eq!("bar", parsed.other[0]);
1646        assert_eq!("foo", parsed.other[1]);
1647        assert_eq!(1, parsed.unknown.len());
1648        assert_eq!("a", parsed.unknown[0]);
1649    }
1650
1651    #[test]
1652    fn parsed_output_280() {
1653        let parsed = OptSpecs::new()
1654            .flag(OptFlags::OptionsEverywhere)
1655            .option("help", "help", OptValue::None)
1656            .option("file", "file", OptValue::Required)
1657            .limit_options(1)
1658            .limit_other_args(2)
1659            .limit_unknown_options(1)
1660            .getopt(["bar", "--help", "-ab", "--file", "123", "foo"]);
1661
1662        // "123" must be parsed as "--file" option's value even though
1663        // it is beyond limit_options.
1664        assert!(parsed.options_first("help").is_some());
1665        assert!(parsed.options_first("file").is_none());
1666        assert_eq!(2, parsed.other.len());
1667        assert_eq!("bar", parsed.other[0]);
1668        assert_eq!("foo", parsed.other[1]);
1669        assert_eq!(1, parsed.unknown.len());
1670        assert_eq!("a", parsed.unknown[0]);
1671    }
1672
1673    #[test]
1674    fn parsed_output_290() {
1675        let parsed = OptSpecs::new()
1676            .option("file", "f", OptValue::RequiredNonEmpty)
1677            .option("debug", "d", OptValue::RequiredNonEmpty)
1678            .getopt(["-f1", "-d", "", "-f", "", "-f", "2", "-f"]);
1679
1680        let mut i = parsed.options_all("file").rev();
1681        assert_eq!("f", i.next().unwrap().name);
1682        assert_eq!("f", i.next().unwrap().name);
1683        assert_eq!("f", i.next().unwrap().name);
1684        assert_eq!("f", i.next().unwrap().name);
1685        assert_eq!(None, i.next());
1686
1687        let mut i = parsed.options_all("debug").rev();
1688        assert_eq!("d", i.next().unwrap().name);
1689        assert_eq!(None, i.next());
1690
1691        let mut i = parsed.options_value_all("file").rev();
1692        assert_eq!("2", i.next().unwrap());
1693        assert_eq!("1", i.next().unwrap());
1694        assert_eq!(None, i.next());
1695
1696        let mut i = parsed.required_value_missing().rev();
1697        assert_eq!("f", i.next().unwrap().name);
1698        assert_eq!("f", i.next().unwrap().name);
1699        assert_eq!("d", i.next().unwrap().name);
1700        assert_eq!(None, i.next());
1701    }
1702
1703    #[test]
1704    fn parsed_output_300() {
1705        let parsed = OptSpecs::new()
1706            .option("debug", "d", OptValue::None)
1707            .getopt(["-aä€d", "--föo"]);
1708
1709        assert_eq!(4, parsed.unknown.len());
1710
1711        let mut u = parsed.unknown_options();
1712        assert_eq!("-a", u.next().unwrap());
1713        assert_eq!("-ä", u.next().unwrap());
1714        assert_eq!("-€", u.next().unwrap());
1715        assert_eq!("--föo", u.next().unwrap());
1716        assert_eq!(None, u.next());
1717
1718        let u = parsed.unknown_options().collect::<Vec<String>>();
1719        assert_eq!(vec!["-a", "-ä", "-€", "--föo"], u);
1720    }
1721}