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