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