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        if let Some(exact) = self.get_long_option_match(name) {
618            return Some(exact);
619        }
620
621        let mut result = None;
622
623        for e in &self.options {
624            if e.name.starts_with(name) {
625                if result.is_none() {
626                    result = Some(e);
627                } else {
628                    return None;
629                }
630            }
631        }
632        result
633    }
634}
635
636impl Default for OptSpecs {
637    fn default() -> Self {
638        Self::new()
639    }
640}
641
642/// Parsed command line in organized form.
643///
644/// Instances of this struct are usually created with
645/// [`OptSpecs::getopt`] method and an instance represents the parsed
646/// output in organized form. See each field's documentation for more
647/// information.
648///
649/// Programmers can use the parsed output ([`Args`] struct) any way they
650/// like. There are some methods for convenience.
651
652#[derive(Debug, PartialEq)]
653pub struct Args {
654    /// A vector of valid command-line options.
655    ///
656    /// Elements of this vector are [`Opt`] structs which each
657    /// represents a single command-line option. Elements are in the
658    /// same order as given (by program's user) in the command line. The
659    /// vector is empty if the parser didn't find any valid command-line
660    /// options.
661    pub options: Vec<Opt>,
662
663    /// A vector of other arguments (non-options).
664    ///
665    /// Each element of the vector is a single non-option argument
666    /// string in the same order as given (by program's user) in the
667    /// command line. The vector is empty if the parser didn't find any
668    /// non-option arguments.
669    pub other: Vec<String>,
670
671    /// Unknown options.
672    ///
673    /// Command-line arguments that look like options but were not part
674    /// of [`OptSpecs`] specification are classified as unknown. They
675    /// are listed in this vector. Possible duplicate unknown options
676    /// have been filtered out.
677    ///
678    /// Each element is the name string for the option (without `-` or
679    /// `--` prefix). For unknown short options the element is a
680    /// single-character string. For unknown long options the string has
681    /// more than one character. The whole vector is empty if there were
682    /// no unknown options.
683    ///
684    /// If a long option does not accept a value (that is, its value
685    /// type is [`OptValue::None`]) but user gives it a value with
686    /// equal sign notation (`--foo=`), that option is classified as
687    /// unknown and it will be in this field's vector with name `foo=`.
688    pub unknown: Vec<String>,
689}
690
691impl Args {
692    fn new() -> Self {
693        Args {
694            options: Vec::new(),
695            other: Vec::new(),
696            unknown: Vec::new(),
697        }
698    }
699
700    /// Find options with missing required value.
701    ///
702    /// This method finds all (otherwise valid) options which require a
703    /// value but the value is missing. That is, [`OptSpecs`] struct
704    /// specification defined that an option requires a value but
705    /// program's user didn't give one in the command line. Such thing
706    /// can happen if an option like `--file` is the last argument in
707    /// the command line and that option requires a value.
708    ///
709    /// If option's value type is [`OptValue::Required`] the empty
710    /// string `""` is not classified as missing value because it can be
711    /// valid user input in many situations. If option's value type is
712    /// [`OptValue::RequiredNonEmpty`] the empty string that was given
713    /// in the command line will be classified as missing value.
714    ///
715    /// The return value implements the [`DoubleEndedIterator`] trait
716    /// (possibly empty, if no matches) and each item is a reference to
717    /// [`Opt`] struct in the original [`Args::options`] field. Items
718    /// are in the same order as in the parsed command line. You can
719    /// collect the iterator to a vector by applying method
720    /// [`collect`](core::iter::Iterator::collect)`::<Vec<&Opt>>()`.
721    pub fn required_value_missing(&self) -> impl DoubleEndedIterator<Item = &Opt> {
722        self.options
723            .iter()
724            .filter(|opt| opt.value_required && opt.value.is_none())
725    }
726
727    /// Return boolean whether option with the given `id` exists.
728    ///
729    /// This is functionally the same as
730    /// [`options_first`](Args::options_first)`(id).is_some()`.
731    pub fn option_exists(&self, id: &str) -> bool {
732        self.options.iter().any(|opt| opt.id == id)
733    }
734
735    /// Find all options with the given `id`.
736    ///
737    /// Find all options which have the identifier `id`. (Option
738    /// identifiers have been defined in [`OptSpecs`] struct before
739    /// parsing.)
740    ///
741    /// The return value implements the [`DoubleEndedIterator`] trait
742    /// (possibly empty, if no matches) and each item is a reference to
743    /// [`Opt`] struct in the original [`Args::options`] field. Items
744    /// are in the same order as in the parsed command line. You can
745    /// collect the iterator to a vector by applying method
746    /// [`collect`](core::iter::Iterator::collect)`::<Vec<&Opt>>()`.
747    pub fn options_all<'a>(&'a self, id: &'a str) -> impl DoubleEndedIterator<Item = &'a Opt> {
748        self.options.iter().filter(move |opt| opt.id == id)
749    }
750
751    /// Find the first option with the given `id`.
752    ///
753    /// Find and return the first match for option `id` in command-line
754    /// arguments' order. (Options' identifiers have been defined in
755    /// [`OptSpecs`] struct before parsing.)
756    ///
757    /// The return value is a variant of enum [`Option`]. Their
758    /// meanings:
759    ///
760    ///   - `None`: No options found with the given `id`.
761    ///
762    ///   - `Some(&Opt)`: An option was found with the given `id` and a
763    ///     reference is provided to its [`Opt`] struct in the original
764    ///     [`Args::options`] field.
765    pub fn options_first(&self, id: &str) -> Option<&Opt> {
766        self.options.iter().find(|opt| opt.id == id)
767    }
768
769    /// Find the last option with the given `id`.
770    ///
771    /// This is similar to [`options_first`](Args::options_first) method
772    /// but this returns the last match in command-line arguments'
773    /// order.
774    pub fn options_last(&self, id: &str) -> Option<&Opt> {
775        self.options.iter().rev().find(|opt| opt.id == id)
776    }
777
778    /// Find all values for options with the given `id`.
779    ///
780    /// Find all options which match the identifier `id` and which also
781    /// have a value assigned. (Options' identifiers have been defined
782    /// in [`OptSpecs`] struct before parsing.)
783    ///
784    /// The return value implements the [`DoubleEndedIterator`] trait
785    /// (possibly empty, if no matches) and each item is a reference to
786    /// string in [`Opt::value`] field in the original [`Args::options`]
787    /// field. Items are in the same order as in the parsed command
788    /// line. You can collect the iterator to a vector by applying
789    /// method
790    /// [`collect`](core::iter::Iterator::collect)`::<Vec<&String>>()`.
791    pub fn options_value_all<'a>(
792        &'a self,
793        id: &'a str,
794    ) -> impl DoubleEndedIterator<Item = &'a String> {
795        self.options.iter().filter_map(move |opt| {
796            if opt.id == id {
797                opt.value.as_ref()
798            } else {
799                None
800            }
801        })
802    }
803
804    /// Find the first option with a value for given option `id`.
805    ///
806    /// Find the first option with the identifier `id` and which has a
807    /// value assigned. (Options' identifiers have been defined in
808    /// [`OptSpecs`] struct before parsing.) Method's return value is a
809    /// variant of enum [`Option`] which are:
810    ///
811    ///   - `None`: No options found with the given `id` and a value
812    ///     assigned. Note that there could be options for the same `id`
813    ///     but they don't have a value.
814    ///
815    ///   - `Some(&String)`: An option was found with the given `id` and
816    ///     the option has a value assigned. A reference is provided to
817    ///     the string value in the [`Opt::value`] field in the original
818    ///     [`Args::options`] field.
819    pub fn options_value_first(&self, id: &str) -> Option<&String> {
820        match self
821            .options
822            .iter()
823            .find(|opt| opt.id == id && opt.value.is_some())
824        {
825            Some(o) => o.value.as_ref(),
826            None => None,
827        }
828    }
829
830    /// Find the last option with a value for given option `id`.
831    ///
832    /// This is similar to
833    /// [`options_value_first`](Args::options_value_first) method but
834    /// this method finds and returns the last option's value.
835    ///
836    /// Note: Program's user may give the same option several times in
837    /// the command line. If the option accepts a value it may be
838    /// suitable to consider only the last value relevant. (Or the
839    /// first, or maybe print an error message for providing several,
840    /// possibly conflicting, values.)
841    pub fn options_value_last(&self, id: &str) -> Option<&String> {
842        match self
843            .options
844            .iter()
845            .rev()
846            .find(|opt| opt.id == id && opt.value.is_some())
847        {
848            Some(o) => o.value.as_ref(),
849            None => None,
850        }
851    }
852}
853
854/// Structured option information.
855///
856/// This [`Opt`] struct represents organized information about single
857/// command-line option. Instances of this struct are usually created by
858/// [`OptSpecs::getopt`] method which returns an [`Args`] struct which
859/// have these [`Opt`] structs inside.
860///
861/// A programmer may need these when examining parsed command-line
862/// options. See the documentation of individual fields for more
863/// information. Also see [`Args`] struct and its methods.
864
865#[derive(Debug, PartialEq)]
866pub struct Opt {
867    /// Identifier for the option.
868    ///
869    /// Identifiers are defined with [`OptSpecs::option`] method before
870    /// parsing command-line arguments. After [`OptSpecs::getopt`]
871    /// parsing the same identifier is copied here and it confirms that
872    /// the option was indeed given in the command line.
873    pub id: String,
874
875    /// Option's name in the parsed command line.
876    ///
877    /// Option's name that was used in the command line. For short
878    /// options this is a single-character string. For long options the
879    /// name has more than one characters.
880    pub name: String,
881
882    /// The option requires a value.
883    ///
884    /// `true` means that the option was defined with value type
885    /// [`OptValue::Required`]. See [`OptSpecs::flag`] method for
886    /// more information. This field does not guarantee that there
887    /// actually was a value for the option in the command line.
888    pub value_required: bool,
889
890    /// Option's value.
891    ///
892    /// The value is a variant of enum [`Option`]. Value `None` means
893    /// that there is no value for the option. Value `Some(String)`
894    /// provides a value.
895    pub value: Option<String>,
896}
897
898#[cfg(test)]
899mod tests {
900    use super::*;
901    use alloc::vec;
902
903    #[test]
904    fn t_create_optspecs_010() {
905        let mut spec;
906        let mut expect;
907
908        spec = OptSpecs::new().option("help", "help", OptValue::None);
909        expect = OptSpec {
910            id: String::from("help"),
911            name: String::from("help"),
912            value_type: OptValue::None,
913        };
914        assert_eq!(1, spec.options.len());
915        assert_eq!(&expect, &spec.options[0]);
916        assert_eq!(COUNTER_LIMIT, spec.option_limit);
917        assert_eq!(COUNTER_LIMIT, spec.other_limit);
918        assert_eq!(COUNTER_LIMIT, spec.unknown_limit);
919
920        spec = spec.option("file", "f", OptValue::Optional);
921        expect = OptSpec {
922            id: String::from("file"),
923            name: String::from("f"),
924            value_type: OptValue::Optional,
925        };
926        assert_eq!(2, spec.options.len());
927        assert_eq!(&expect, &spec.options[1]);
928
929        spec = spec.option("file", "file", OptValue::Required);
930        expect = OptSpec {
931            id: String::from("file"),
932            name: String::from("file"),
933            value_type: OptValue::Required,
934        };
935        assert_eq!(3, spec.options.len());
936        assert_eq!(&expect, &spec.options[2]);
937
938        spec = spec.flag(OptFlags::OptionsEverywhere);
939        assert_eq!(1, spec.flags.len()); // Length 1
940        assert_eq!(true, spec.is_flag(OptFlags::OptionsEverywhere));
941        spec = spec.flag(OptFlags::PrefixMatchLongOptions);
942        assert_eq!(2, spec.flags.len()); // Length 2
943        assert_eq!(true, spec.is_flag(OptFlags::PrefixMatchLongOptions));
944        spec = spec.flag(OptFlags::OptionsEverywhere);
945        spec = spec.flag(OptFlags::PrefixMatchLongOptions);
946        assert_eq!(2, spec.flags.len()); // Length still 2
947
948        spec = spec.limit_options(9);
949        spec = spec.limit_other_args(10);
950        spec = spec.limit_unknown_options(3);
951        assert_eq!(9, spec.option_limit);
952        assert_eq!(10, spec.other_limit);
953        assert_eq!(3, spec.unknown_limit);
954    }
955
956    #[test]
957    #[should_panic]
958    fn t_create_optspecs_020() {
959        OptSpecs::new().option("", "h", OptValue::None);
960    }
961
962    #[test]
963    #[should_panic]
964    fn t_create_optspecs_030() {
965        OptSpecs::new()
966            .option("h", "h", OptValue::None)
967            .option("h", "h", OptValue::None);
968    }
969
970    #[test]
971    #[should_panic]
972    fn t_create_optspecs_040() {
973        OptSpecs::new().option("h", "", OptValue::None);
974    }
975
976    #[test]
977    #[should_panic]
978    fn t_create_optspecs_050() {
979        OptSpecs::new().option("h", "-", OptValue::None);
980    }
981
982    #[test]
983    #[should_panic]
984    fn t_create_optspecs_060() {
985        OptSpecs::new().option("h", " ", OptValue::None);
986    }
987
988    #[test]
989    #[should_panic]
990    fn t_create_optspecs_070() {
991        OptSpecs::new().option("h", "hh ", OptValue::None);
992    }
993
994    #[test]
995    #[should_panic]
996    fn t_create_optspecs_080() {
997        OptSpecs::new().option("h", "hh=hh", OptValue::None);
998    }
999
1000    #[test]
1001    fn t_is_flag() {
1002        let mut spec = OptSpecs::new().flag(OptFlags::OptionsEverywhere);
1003        assert_eq!(true, spec.is_flag(OptFlags::OptionsEverywhere));
1004
1005        spec = spec.flag(OptFlags::PrefixMatchLongOptions);
1006        assert_eq!(true, spec.is_flag(OptFlags::PrefixMatchLongOptions));
1007    }
1008
1009    #[test]
1010    fn t_parsed_output_010() {
1011        let parsed = OptSpecs::new()
1012            .option("help", "h", OptValue::None)
1013            .option("help", "help", OptValue::None)
1014            .option("file", "f", OptValue::Required)
1015            .option("file", "file", OptValue::Required)
1016            .getopt(["-h", "--help", "-f123", "-f", "456", "foo", "bar"]);
1017
1018        assert_eq!(true, parsed.option_exists("help"));
1019        assert_eq!(true, parsed.option_exists("file"));
1020        assert_eq!(false, parsed.option_exists("x"));
1021
1022        assert_eq!("h", parsed.options_first("help").unwrap().name);
1023        assert_eq!("help", parsed.options_last("help").unwrap().name);
1024        assert_eq!("help", parsed.options_first("help").unwrap().id);
1025        assert_eq!("help", parsed.options_last("help").unwrap().id);
1026        assert_eq!(false, parsed.options_first("help").unwrap().value_required);
1027        assert_eq!(false, parsed.options_last("help").unwrap().value_required);
1028
1029        assert_eq!("f", parsed.options_first("file").unwrap().name);
1030        assert_eq!(
1031            "123",
1032            parsed.options_first("file").unwrap().value.clone().unwrap()
1033        );
1034        assert_eq!(
1035            "456",
1036            parsed.options_last("file").unwrap().value.clone().unwrap()
1037        );
1038        assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1039
1040        assert_eq!("foo", parsed.other[0]);
1041        assert_eq!("bar", parsed.other[1]);
1042    }
1043
1044    #[test]
1045    fn t_parsed_output_020() {
1046        let parsed = OptSpecs::new()
1047            .limit_options(1)
1048            .limit_other_args(2)
1049            .option("help", "h", OptValue::None)
1050            .getopt(["-h", "foo", "-h"]);
1051
1052        assert_eq!("h", parsed.options_first("help").unwrap().name);
1053        assert_eq!(2, parsed.other.len());
1054        assert_eq!("foo", parsed.other[0]);
1055        assert_eq!("-h", parsed.other[1]);
1056    }
1057
1058    #[test]
1059    fn t_parsed_output_030() {
1060        let parsed = OptSpecs::new()
1061            .flag(OptFlags::OptionsEverywhere)
1062            .option("help", "h", OptValue::None)
1063            .option("help", "help", OptValue::None)
1064            .option("file", "f", OptValue::Required)
1065            .option("file", "file", OptValue::Required)
1066            .getopt(["-h", "foo", "--help", "--file=123", "bar", "--file", "456"]);
1067
1068        assert_eq!("h", parsed.options_first("help").unwrap().name);
1069        assert_eq!("help", parsed.options_last("help").unwrap().name);
1070        assert_eq!(
1071            "123",
1072            parsed.options_first("file").unwrap().value.clone().unwrap()
1073        );
1074        assert_eq!(
1075            "456",
1076            parsed.options_last("file").unwrap().value.clone().unwrap()
1077        );
1078        assert_eq!("foo", parsed.other[0]);
1079        assert_eq!("bar", parsed.other[1]);
1080    }
1081
1082    #[test]
1083    fn t_parsed_output_040() {
1084        let parsed = OptSpecs::new()
1085            .option("debug", "d", OptValue::Optional)
1086            .option("verbose", "verbose", OptValue::Optional)
1087            .getopt(["-d1", "-d", "--verbose", "--verbose=123"]);
1088
1089        assert_eq!(
1090            "1",
1091            parsed
1092                .options_first("debug")
1093                .unwrap()
1094                .value
1095                .clone()
1096                .unwrap()
1097        );
1098        assert_eq!(None, parsed.options_last("debug").unwrap().value);
1099        assert_eq!(false, parsed.options_last("debug").unwrap().value_required);
1100
1101        assert_eq!(None, parsed.options_first("verbose").unwrap().value);
1102        assert_eq!(
1103            "123",
1104            parsed
1105                .options_last("verbose")
1106                .unwrap()
1107                .value
1108                .clone()
1109                .unwrap()
1110        );
1111        assert_eq!(
1112            false,
1113            parsed.options_last("verbose").unwrap().value_required
1114        );
1115    }
1116
1117    #[test]
1118    fn t_parsed_output_050() {
1119        let parsed = OptSpecs::new()
1120            .option("debug", "d", OptValue::Optional)
1121            .getopt(["-abcd", "-adbc"]);
1122
1123        assert_eq!(None, parsed.options_first("debug").unwrap().value);
1124        assert_eq!(
1125            "bc",
1126            parsed.options_last("debug").unwrap().value.clone().unwrap()
1127        );
1128
1129        assert_eq!(3, parsed.unknown.len());
1130        assert_eq!("a", parsed.unknown[0]);
1131        assert_eq!("b", parsed.unknown[1]);
1132        assert_eq!("c", parsed.unknown[2]);
1133    }
1134
1135    #[test]
1136    fn t_parsed_output_060() {
1137        let parsed = OptSpecs::new()
1138            .option("aaa", "bbb", OptValue::None)
1139            .option("aaa", "c", OptValue::None)
1140            .option("aaa", "d", OptValue::None)
1141            .option("aaa", "eee", OptValue::None)
1142            .getopt(["--bbb", "-cd", "--eee"]);
1143
1144        let m: Vec<&Opt> = parsed.options_all("aaa").collect();
1145        assert_eq!("bbb", m[0].name);
1146        assert_eq!("c", m[1].name);
1147        assert_eq!("d", m[2].name);
1148        assert_eq!("eee", m[3].name);
1149    }
1150
1151    #[test]
1152    fn t_parsed_output_070() {
1153        let parsed = OptSpecs::new()
1154            .flag(OptFlags::PrefixMatchLongOptions)
1155            .option("version", "version", OptValue::None)
1156            .option("verbose", "verbose", OptValue::None)
1157            .getopt(["--ver", "--verb", "--versi", "--verbose"]);
1158
1159        assert_eq!("ver", parsed.unknown[0]);
1160        assert_eq!("verb", parsed.options_first("verbose").unwrap().name);
1161        assert_eq!("verbose", parsed.options_last("verbose").unwrap().name);
1162        assert_eq!("version", parsed.options_first("version").unwrap().id);
1163        assert_eq!("versi", parsed.options_first("version").unwrap().name);
1164    }
1165
1166    #[test]
1167    fn t_parsed_output_080() {
1168        let parsed = OptSpecs::new()
1169            // .flag(OptFlags::PrefixMatchLongOptions) Must be commented!
1170            .option("version", "version", OptValue::None)
1171            .option("verbose", "verbose", OptValue::None)
1172            .getopt(["--version", "--ver", "--verb", "--versi", "--verbose"]);
1173
1174        assert_eq!("ver", parsed.unknown[0]);
1175        assert_eq!("verb", parsed.unknown[1]);
1176        assert_eq!("versi", parsed.unknown[2]);
1177        assert_eq!("version", parsed.options_first("version").unwrap().name);
1178        assert_eq!("verbose", parsed.options_first("verbose").unwrap().name);
1179    }
1180
1181    #[test]
1182    fn t_parsed_output_085() {
1183        let parsed = OptSpecs::new()
1184            .flag(OptFlags::PrefixMatchLongOptions)
1185            .option("foo1", "foo", OptValue::None)
1186            .option("foo2", "foo-longer", OptValue::None)
1187            .getopt(["--fo", "--foo", "--foo-", "--foo-longer"]);
1188
1189        assert_eq!("fo", parsed.unknown[0]);
1190        assert_eq!("foo", parsed.options_first("foo1").unwrap().name);
1191        assert_eq!("foo", parsed.options_last("foo1").unwrap().name);
1192        assert_eq!("foo-", parsed.options_first("foo2").unwrap().name);
1193        assert_eq!("foo-longer", parsed.options_last("foo2").unwrap().name);
1194    }
1195
1196    #[test]
1197    fn t_parsed_output_090() {
1198        let parsed = OptSpecs::new()
1199            .flag(OptFlags::OptionsEverywhere)
1200            .option("help", "h", OptValue::None)
1201            .option("file", "file", OptValue::Required)
1202            .getopt(["-h", "foo", "--file=123", "--", "bar", "--file", "456"]);
1203
1204        assert_eq!("h", parsed.options_first("help").unwrap().name);
1205        assert_eq!("file", parsed.options_first("file").unwrap().name);
1206        assert_eq!(
1207            "123",
1208            parsed.options_first("file").unwrap().value.clone().unwrap()
1209        );
1210
1211        assert_eq!(4, parsed.other.len());
1212        assert_eq!("foo", parsed.other[0]);
1213        assert_eq!("bar", parsed.other[1]);
1214        assert_eq!("--file", parsed.other[2]);
1215        assert_eq!("456", parsed.other[3]);
1216    }
1217
1218    #[test]
1219    fn t_parsed_output_100() {
1220        let parsed = OptSpecs::new()
1221            .option("file", "file", OptValue::Required)
1222            .getopt(["--file=", "--file"]);
1223
1224        assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1225        assert_eq!(
1226            "",
1227            parsed.options_first("file").unwrap().value.clone().unwrap()
1228        );
1229        assert_eq!(None, parsed.options_last("file").unwrap().value);
1230    }
1231
1232    #[test]
1233    fn t_parsed_output_110() {
1234        let parsed = OptSpecs::new()
1235            .option("file", "f", OptValue::Required)
1236            .option("debug", "d", OptValue::Required)
1237            .getopt(["-fx", "-d", "", "-f"]);
1238
1239        assert_eq!(true, parsed.options_first("file").unwrap().value_required);
1240        assert_eq!(
1241            "x",
1242            parsed.options_first("file").unwrap().value.clone().unwrap()
1243        );
1244        assert_eq!(None, parsed.options_last("file").unwrap().value);
1245        assert_eq!(
1246            "",
1247            parsed
1248                .options_first("debug")
1249                .unwrap()
1250                .value
1251                .clone()
1252                .unwrap()
1253        );
1254    }
1255
1256    #[test]
1257    fn t_parsed_output_120() {
1258        let parsed = OptSpecs::new()
1259            .option("file", "f", OptValue::Required)
1260            .option("debug", "d", OptValue::Required)
1261            .getopt(["-f123", "-d", "", "-f", "456", "-f"]);
1262
1263        let f: Vec<&String> = parsed.options_value_all("file").collect();
1264        assert_eq!(2, f.len());
1265        assert_eq!("123", f[0]);
1266        assert_eq!("456", f[1]);
1267
1268        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1269        assert_eq!(1, d.len());
1270        assert_eq!("", d[0]);
1271
1272        assert_eq!(None, parsed.options_last("file").unwrap().value);
1273        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1274        assert_eq!(1, m.len());
1275        assert_eq!("f", m[0].name);
1276    }
1277
1278    #[test]
1279    fn t_parsed_output_125() {
1280        let parsed = OptSpecs::new()
1281            .option("file", "f", OptValue::Required)
1282            .option("debug", "d", OptValue::RequiredNonEmpty)
1283            .getopt(["-f123", "-d", "", "-f", "456", "-f"]);
1284
1285        let f: Vec<&String> = parsed.options_value_all("file").collect();
1286        assert_eq!(2, f.len());
1287        assert_eq!("123", f[0]);
1288        assert_eq!("456", f[1]);
1289
1290        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1291        assert_eq!(0, d.len());
1292
1293        assert_eq!(None, parsed.options_last("file").unwrap().value);
1294        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1295        assert_eq!(2, m.len());
1296        assert_eq!("d", m[0].name);
1297        assert_eq!("f", m[1].name);
1298    }
1299
1300    #[test]
1301    fn t_parsed_output_130() {
1302        let parsed = OptSpecs::new()
1303            .option("file", "file", OptValue::Required)
1304            .option("debug", "debug", OptValue::Required)
1305            .getopt(["--file=123", "--debug", "", "--file", "456", "--file"]);
1306
1307        let f: Vec<&String> = parsed.options_value_all("file").collect();
1308        assert_eq!(2, f.len());
1309        assert_eq!("123", f[0]);
1310        assert_eq!("456", f[1]);
1311
1312        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1313        assert_eq!(1, d.len());
1314        assert_eq!("", d[0]);
1315
1316        assert_eq!(None, parsed.options_last("file").unwrap().value);
1317        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1318        assert_eq!(1, m.len());
1319        assert_eq!("file", m[0].name);
1320    }
1321
1322    #[test]
1323    fn t_parsed_output_135() {
1324        let parsed = OptSpecs::new()
1325            .option("file", "file", OptValue::RequiredNonEmpty)
1326            .option("debug", "debug", OptValue::RequiredNonEmpty)
1327            .getopt(["--file=123", "--debug", "", "--file", "456", "--file="]);
1328
1329        let f: Vec<&String> = parsed.options_value_all("file").collect();
1330        assert_eq!(2, f.len());
1331        assert_eq!("123", f[0]);
1332        assert_eq!("456", f[1]);
1333
1334        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1335        assert_eq!(0, d.len());
1336
1337        assert_eq!(None, parsed.options_last("file").unwrap().value);
1338        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1339        assert_eq!(2, m.len());
1340        assert_eq!("debug", m[0].name);
1341        assert_eq!("file", m[1].name);
1342    }
1343
1344    #[test]
1345    fn t_parsed_output_137() {
1346        let parsed = OptSpecs::new()
1347            .option("debug", "d", OptValue::OptionalNonEmpty)
1348            .option("debug", "debug", OptValue::OptionalNonEmpty)
1349            .getopt([
1350                "-d",
1351                "-d123",
1352                "--debug",
1353                "--debug=",
1354                "--debug=456",
1355                "--debug=",
1356            ]);
1357
1358        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1359        assert_eq!(2, d.len());
1360        assert_eq!("123", d[0]);
1361        assert_eq!("456", d[1]);
1362        assert_eq!("123", parsed.options_value_first("debug").unwrap());
1363        assert_eq!("456", parsed.options_value_last("debug").unwrap());
1364    }
1365
1366    #[test]
1367    fn t_parsed_output_140() {
1368        let parsed = OptSpecs::new()
1369            .flag(OptFlags::OptionsEverywhere)
1370            .option("debug", "d", OptValue::Optional)
1371            .option("debug", "debug", OptValue::Optional)
1372            .getopt([
1373                "-d",
1374                "-d123",
1375                "-d",
1376                "--debug",
1377                "--debug=",
1378                "foo",
1379                "--debug=456",
1380                "-d",
1381            ]);
1382
1383        let d: Vec<&Opt> = parsed.options_all("debug").collect();
1384        assert_eq!(7, d.len());
1385
1386        let d: Vec<&String> = parsed.options_value_all("debug").collect();
1387        assert_eq!(3, d.len());
1388        assert_eq!("123", d[0]);
1389        assert_eq!("", d[1]);
1390        assert_eq!("456", d[2]);
1391        assert_eq!("123", parsed.options_value_first("debug").unwrap());
1392        assert_eq!("456", parsed.options_value_last("debug").unwrap());
1393
1394        assert_eq!(None, parsed.options_value_first("not-at-all"));
1395        assert_eq!(None, parsed.options_value_last("not-at-all"));
1396
1397        assert_eq!("foo", parsed.other[0]);
1398    }
1399
1400    #[test]
1401    fn t_parsed_output_150() {
1402        let parsed = OptSpecs::new().limit_unknown_options(6).getopt([
1403            "-abcd",
1404            "-e",
1405            "--debug",
1406            "-x", // Won't be listed in unknown because of limit.
1407            "--",
1408            "--debug=",
1409            "foo",
1410            "--debug=456",
1411        ]);
1412
1413        assert_eq!(0, parsed.options.len());
1414        assert_eq!(3, parsed.other.len());
1415        assert_eq!(6, parsed.unknown.len());
1416        assert_eq!(vec!["a", "b", "c", "d", "e", "debug"], parsed.unknown);
1417    }
1418
1419    #[test]
1420    fn t_parsed_output_160() {
1421        let parsed = OptSpecs::new()
1422            .option("file", "file", OptValue::Required)
1423            .getopt(["--file", "--", "--", "--"]);
1424
1425        assert_eq!(
1426            "--",
1427            parsed.options_first("file").unwrap().value.clone().unwrap()
1428        );
1429        assert_eq!(1, parsed.other.len());
1430        assert_eq!("--", parsed.other[0]);
1431
1432        assert_eq!(0, parsed.required_value_missing().count());
1433    }
1434
1435    #[test]
1436    fn t_parsed_output_170() {
1437        let parsed = OptSpecs::new().getopt(["foo", "bar"]);
1438
1439        assert_eq!(None, parsed.options_first("not-at-all"));
1440        assert_eq!(None, parsed.options_last("not-at-all"));
1441    }
1442
1443    #[test]
1444    fn t_parsed_output_180() {
1445        let parsed = OptSpecs::new()
1446            .limit_unknown_options(3)
1447            .option("bar", "bar", OptValue::None)
1448            .getopt(["-aaa", "--foo", "--foo", "--bar=", "--bar=", "-x"]);
1449
1450        assert_eq!(3, parsed.unknown.len());
1451        assert_eq!("a", parsed.unknown[0]);
1452        assert_eq!("foo", parsed.unknown[1]);
1453        assert_eq!("bar=", parsed.unknown[2]);
1454    }
1455
1456    #[test]
1457    fn t_parsed_output_190() {
1458        let parsed = OptSpecs::new()
1459            .option("äiti", "äiti", OptValue::Required)
1460            .option("€uro", "€uro", OptValue::Required)
1461            .getopt(["--äiti=ööö", "--€uro", "€€€", "--äiti", "ää", "--äiti"]);
1462
1463        let a: Vec<&String> = parsed.options_value_all("äiti").collect();
1464        assert_eq!(2, a.len());
1465        assert_eq!("ööö", a[0]);
1466        assert_eq!("ää", a[1]);
1467        assert_eq!("ööö", parsed.options_value_first("äiti").unwrap());
1468        assert_eq!("ää", parsed.options_value_last("äiti").unwrap());
1469
1470        let e: Vec<&String> = parsed.options_value_all("€uro").collect();
1471        assert_eq!(1, e.len());
1472        assert_eq!("€€€", e[0]);
1473        assert_eq!("€€€", parsed.options_value_first("€uro").unwrap());
1474        assert_eq!("€€€", parsed.options_value_last("€uro").unwrap());
1475
1476        assert_eq!(None, parsed.options_last("äiti").unwrap().value);
1477
1478        let m: Vec<&Opt> = parsed.required_value_missing().collect();
1479        assert_eq!(1, m.len());
1480        assert_eq!("äiti", m[0].name);
1481        assert_eq!(None, m[0].value);
1482    }
1483
1484    #[test]
1485    fn t_parsed_output_195() {
1486        let parsed = OptSpecs::new().getopt(["-ä€", "--€uro", "äää", "€€€"]);
1487
1488        assert_eq!(2, parsed.other.len());
1489        assert_eq!("äää", parsed.other[0]);
1490        assert_eq!("€€€", parsed.other[1]);
1491
1492        assert_eq!(3, parsed.unknown.len());
1493        assert_eq!("ä", parsed.unknown[0]);
1494        assert_eq!("€", parsed.unknown[1]);
1495        assert_eq!("€uro", parsed.unknown[2]);
1496    }
1497
1498    #[test]
1499    fn t_parsed_output_200() {
1500        let parsed = OptSpecs::new().limit_other_args(5).getopt(1..10);
1501        assert_eq!(5, parsed.other.len());
1502        assert_eq!(vec!["1", "2", "3", "4", "5"], parsed.other);
1503    }
1504
1505    #[test]
1506    fn t_parsed_output_210() {
1507        let parsed = OptSpecs::new().limit_other_args(0).getopt(1..10);
1508        assert_eq!(0, parsed.other.len());
1509    }
1510
1511    #[test]
1512    fn t_parsed_output_220() {
1513        let parsed = OptSpecs::new()
1514            .option("file", "f", OptValue::Required)
1515            .option("file", "file", OptValue::Required)
1516            .option("help", "help", OptValue::None)
1517            .limit_options(3)
1518            .limit_other_args(1)
1519            .limit_unknown_options(3)
1520            .getopt([
1521                "--unknown",
1522                "--help=",
1523                "-ab",
1524                "-f",
1525                "one",
1526                "-ftwo",
1527                "--file",
1528                "three",
1529                "--file",
1530                "four",
1531                "other1",
1532                "other2",
1533            ]);
1534
1535        assert_eq!(3, parsed.options.len());
1536        assert_eq!(
1537            vec!["one", "two", "three"],
1538            parsed.options_value_all("file").collect::<Vec<&String>>()
1539        );
1540
1541        assert_eq!(1, parsed.other.len());
1542        assert_eq!("other1", parsed.other[0]);
1543
1544        assert_eq!(3, parsed.unknown.len());
1545        assert_eq!(vec!["unknown", "help=", "a"], parsed.unknown);
1546    }
1547
1548    #[test]
1549    fn t_parsed_output_230() {
1550        let parsed = OptSpecs::new()
1551            .option("file", "f", OptValue::Required)
1552            .option("file", "file", OptValue::Required)
1553            .limit_options(3)
1554            .getopt(["-f", "one", "-ftwo", "--file=three", "--unknown"]);
1555
1556        assert_eq!(
1557            vec!["one", "two", "three"],
1558            parsed.options_value_all("file").collect::<Vec<&String>>()
1559        );
1560        assert_eq!(1, parsed.unknown.len());
1561        assert_eq!("unknown", parsed.unknown[0]);
1562    }
1563
1564    #[test]
1565    fn t_parsed_output_240() {
1566        let parsed = OptSpecs::new()
1567            .option("help", "h", OptValue::None)
1568            .limit_options(3)
1569            .getopt(["-xhhhh"]);
1570
1571        assert_eq!(3, parsed.options.len());
1572        assert_eq!(true, parsed.options_first("help").is_some());
1573        assert_eq!(1, parsed.unknown.len());
1574        assert_eq!("x", parsed.unknown[0]);
1575    }
1576
1577    #[test]
1578    fn t_parsed_output_250() {
1579        let parsed = OptSpecs::new()
1580            .option("help", "h", OptValue::None)
1581            .limit_options(3)
1582            .getopt(["-x", "-h", "-h", "-h", "-h"]);
1583
1584        assert_eq!(3, parsed.options.len());
1585        assert_eq!(true, parsed.options_first("help").is_some());
1586        assert_eq!(1, parsed.unknown.len());
1587        assert_eq!("x", parsed.unknown[0]);
1588    }
1589
1590    #[test]
1591    fn t_parsed_output_260() {
1592        let parsed = OptSpecs::new()
1593            .option("help", "h", OptValue::None)
1594            .limit_options(3)
1595            .getopt(["-x", "-h", "-h", "--", "-h", "-h"]);
1596
1597        assert_eq!(2, parsed.options.len());
1598        assert_eq!(true, parsed.options_first("help").is_some());
1599        assert_eq!(2, parsed.other.len());
1600        assert_eq!(vec!["-h", "-h"], parsed.other);
1601        assert_eq!(1, parsed.unknown.len());
1602        assert_eq!("x", parsed.unknown[0]);
1603    }
1604
1605    #[test]
1606    fn t_parsed_output_270() {
1607        let parsed = OptSpecs::new()
1608            .flag(OptFlags::OptionsEverywhere)
1609            .option("help", "h", OptValue::None)
1610            .option("file", "f", OptValue::Required)
1611            .limit_options(1)
1612            .limit_other_args(2)
1613            .limit_unknown_options(1)
1614            .getopt(["bar", "-habf", "123", "foo"]);
1615
1616        // "123" must be parsed as "f" option's value even though it is
1617        // beyond limit_options.
1618        assert_eq!(true, parsed.options_first("help").is_some());
1619        assert_eq!(false, parsed.options_first("file").is_some());
1620        assert_eq!(2, parsed.other.len());
1621        assert_eq!("bar", parsed.other[0]);
1622        assert_eq!("foo", parsed.other[1]);
1623        assert_eq!(1, parsed.unknown.len());
1624        assert_eq!("a", parsed.unknown[0]);
1625    }
1626
1627    #[test]
1628    fn t_parsed_output_280() {
1629        let parsed = OptSpecs::new()
1630            .flag(OptFlags::OptionsEverywhere)
1631            .option("help", "help", OptValue::None)
1632            .option("file", "file", OptValue::Required)
1633            .limit_options(1)
1634            .limit_other_args(2)
1635            .limit_unknown_options(1)
1636            .getopt(["bar", "--help", "-ab", "--file", "123", "foo"]);
1637
1638        // "123" must be parsed as "--file" option's value even though
1639        // it is beyond limit_options.
1640        assert_eq!(true, parsed.options_first("help").is_some());
1641        assert_eq!(false, parsed.options_first("file").is_some());
1642        assert_eq!(2, parsed.other.len());
1643        assert_eq!("bar", parsed.other[0]);
1644        assert_eq!("foo", parsed.other[1]);
1645        assert_eq!(1, parsed.unknown.len());
1646        assert_eq!("a", parsed.unknown[0]);
1647    }
1648
1649    #[test]
1650    fn t_parsed_output_290() {
1651        let parsed = OptSpecs::new()
1652            .option("file", "f", OptValue::RequiredNonEmpty)
1653            .option("debug", "d", OptValue::RequiredNonEmpty)
1654            .getopt(["-f1", "-d", "", "-f", "", "-f", "2", "-f"]);
1655
1656        let mut i = parsed.options_all("file").rev();
1657        assert_eq!("f", i.next().unwrap().name);
1658        assert_eq!("f", i.next().unwrap().name);
1659        assert_eq!("f", i.next().unwrap().name);
1660        assert_eq!("f", i.next().unwrap().name);
1661        assert_eq!(None, i.next());
1662
1663        let mut i = parsed.options_all("debug").rev();
1664        assert_eq!("d", i.next().unwrap().name);
1665        assert_eq!(None, i.next());
1666
1667        let mut i = parsed.options_value_all("file").rev();
1668        assert_eq!("2", i.next().unwrap());
1669        assert_eq!("1", i.next().unwrap());
1670        assert_eq!(None, i.next());
1671
1672        let mut i = parsed.required_value_missing().rev();
1673        assert_eq!("f", i.next().unwrap().name);
1674        assert_eq!("f", i.next().unwrap().name);
1675        assert_eq!("d", i.next().unwrap().name);
1676        assert_eq!(None, i.next());
1677    }
1678}