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}