[go: up one dir, main page]

gumdrop/
lib.rs

1//! Option parser with custom derive support
2//!
3//! For full documentation on customization of `derive(Options)`, please see the crate
4//! documentation for [`gumdrop_derive`](https://docs.rs/crate/gumdrop_derive/).
5//!
6//! # Examples
7//!
8//! ```
9//! use gumdrop::Options;
10//!
11//! // Defines options that can be parsed from the command line.
12//! //
13//! // `derive(Options)` will generate an implementation of the trait `Options`.
14//! // Each field must either have a `Default` implementation or an inline
15//! // default value provided.
16//! //
17//! // (`Debug` is derived here only for demonstration purposes.)
18//! #[derive(Debug, Options)]
19//! struct MyOptions {
20//!     // Contains "free" arguments -- those that are not options.
21//!     // If no `free` field is declared, free arguments will result in an error.
22//!     #[options(free)]
23//!     free: Vec<String>,
24//!
25//!     // Boolean options are treated as flags, taking no additional values.
26//!     // The optional `help` attribute is displayed in `usage` text.
27//!     //
28//!     // A boolean field named `help` is automatically given the `help_flag` attribute.
29//!     // The `parse_args_or_exit` and `parse_args_default_or_exit` functions use help flags
30//!     // to automatically display usage to the user.
31//!     #[options(help = "print help message")]
32//!     help: bool,
33//!
34//!     // Non-boolean fields will take a value from the command line.
35//!     // Wrapping the type in an `Option` is not necessary, but provides clarity.
36//!     #[options(help = "give a string argument")]
37//!     string: Option<String>,
38//!
39//!     // A field can be any type that implements `FromStr`.
40//!     // The optional `meta` attribute is displayed in `usage` text.
41//!     #[options(help = "give a number as an argument", meta = "N")]
42//!     number: Option<i32>,
43//!
44//!     // A `Vec` field will accumulate all values received from the command line.
45//!     #[options(help = "give a list of string items")]
46//!     item: Vec<String>,
47//!
48//!     // The `count` flag will treat the option as a counter.
49//!     // Each time the option is encountered, the field is incremented.
50//!     #[options(count, help = "increase a counting value")]
51//!     count: u32,
52//!
53//!     // Option names are automatically generated from field names, but these
54//!     // can be overriden. The attributes `short = "?"`, `long = "..."`,
55//!     // `no_short`, and `no_long` are used to control option names.
56//!     #[options(no_short, help = "this option has no short form")]
57//!     long_option_only: bool,
58//! }
59//!
60//! fn main() {
61//!     let opts = MyOptions::parse_args_default_or_exit();
62//!
63//!     println!("{:#?}", opts);
64//! }
65//! ```
66//!
67//! `derive(Options)` can also be used on `enum`s to produce a subcommand
68//! option parser.
69//!
70//! ```
71//! use gumdrop::Options;
72//!
73//! // Define options for the program.
74//! #[derive(Debug, Options)]
75//! struct MyOptions {
76//!     // Options here can be accepted with any command (or none at all),
77//!     // but they must come before the command name.
78//!     #[options(help = "print help message")]
79//!     help: bool,
80//!     #[options(help = "be verbose")]
81//!     verbose: bool,
82//!
83//!     // The `command` option will delegate option parsing to the command type,
84//!     // starting at the first free argument.
85//!     #[options(command)]
86//!     command: Option<Command>,
87//! }
88//!
89//! // The set of commands and the options each one accepts.
90//! //
91//! // Each variant of a command enum should be a unary tuple variant with only
92//! // one field. This field must implement `Options` and is used to parse arguments
93//! // that are given after the command name.
94//! #[derive(Debug, Options)]
95//! enum Command {
96//!     // Command names are generated from variant names.
97//!     // By default, a CamelCase name will be converted into a lowercase,
98//!     // hyphen-separated name; e.g. `FooBar` becomes `foo-bar`.
99//!     //
100//!     // Names can be explicitly specified using `#[options(name = "...")]`
101//!     #[options(help = "show help for a command")]
102//!     Help(HelpOpts),
103//!     #[options(help = "make stuff")]
104//!     Make(MakeOpts),
105//!     #[options(help = "install stuff")]
106//!     Install(InstallOpts),
107//! }
108//!
109//! // Options accepted for the `help` command
110//! #[derive(Debug, Options)]
111//! struct HelpOpts {
112//!     #[options(free)]
113//!     free: Vec<String>,
114//! }
115//!
116//! // Options accepted for the `make` command
117//! #[derive(Debug, Options)]
118//! struct MakeOpts {
119//!     #[options(free)]
120//!     free: Vec<String>,
121//!     #[options(help = "number of jobs", meta = "N")]
122//!     jobs: Option<u32>,
123//! }
124//!
125//! // Options accepted for the `install` command
126//! #[derive(Debug, Options)]
127//! struct InstallOpts {
128//!     #[options(help = "target directory")]
129//!     dir: Option<String>,
130//! }
131//!
132//! fn main() {
133//!     let opts = MyOptions::parse_args_default_or_exit();
134//!
135//!     println!("{:#?}", opts);
136//! }
137//! ```
138//!
139//! A custom parsing function can be supplied for each option field.
140//!
141//! ```
142//! use gumdrop::Options;
143//!
144//! #[derive(Debug, Options)]
145//! struct MyOptions {
146//!     // `try_from_str = "..."` supplies a conversion function that may fail
147//!     #[options(help = "a hexadecimal value", parse(try_from_str = "parse_hex"))]
148//!     hex: u32,
149//!     // `from_str = "..."` supplies a conversion function that always succeeds
150//!     #[options(help = "a string that becomes uppercase", parse(from_str = "to_upper"))]
151//!     upper: String,
152//! }
153//!
154//! fn parse_hex(s: &str) -> Result<u32, std::num::ParseIntError> {
155//!     u32::from_str_radix(s, 16)
156//! }
157//!
158//! fn to_upper(s: &str) -> String {
159//!     s.to_uppercase()
160//! }
161//!
162//! fn main() {
163//!     let opts = MyOptions::parse_args_default_or_exit();
164//!
165//!     println!("{:#?}", opts);
166//! }
167//! ```
168
169#![deny(missing_docs)]
170
171pub use gumdrop_derive::*;
172
173use std::error::Error as StdError;
174use std::fmt;
175use std::slice::Iter;
176use std::str::Chars;
177
178/// Represents an error encountered during argument parsing
179#[derive(Debug)]
180pub struct Error {
181    kind: ErrorKind,
182}
183
184#[derive(Debug)]
185enum ErrorKind {
186    FailedParse(String, String),
187    FailedParseDefault{
188        option: &'static str,
189        value: &'static str,
190        err: String,
191    },
192    InsufficientArguments{
193        option: String,
194        expected: usize,
195        found: usize,
196    },
197    MissingArgument(String),
198    MissingCommand,
199    MissingRequired(String),
200    MissingRequiredCommand,
201    MissingRequiredFree,
202    UnexpectedArgument(String),
203    UnexpectedSingleArgument(String, usize),
204    UnexpectedFree(String),
205    UnrecognizedCommand(String),
206    UnrecognizedLongOption(String),
207    UnrecognizedShortOption(char),
208}
209
210/// Parses options from a series of `&str`-like values.
211pub struct Parser<'a, S: 'a> {
212    args: Iter<'a, S>,
213    cur: Option<Chars<'a>>,
214    style: ParsingStyle,
215    terminated: bool,
216}
217
218/// Represents an option parsed from a `Parser`
219#[derive(Copy, Clone, Debug, Eq, PartialEq)]
220pub enum Opt<'a> {
221    /// Short option, e.g. `-o`
222    Short(char),
223    /// Long option, e.g. `--option`
224    Long(&'a str),
225    /// Long option with argument, e.g. `--option=value`
226    LongWithArg(&'a str, &'a str),
227    /// Free argument
228    Free(&'a str),
229}
230
231/// Implements a set of options parsed from command line arguments.
232///
233/// An implementation of this trait can be generated with `#[derive(Options)]`.
234pub trait Options {
235    /// Parses arguments until the given parser is exhausted or until
236    /// an error is encountered.
237    fn parse<S: AsRef<str>>(parser: &mut Parser<S>) -> Result<Self, Error> where Self: Sized;
238
239    /// Returns the subcommand instance, if present.
240    ///
241    /// This method **must never** return `self` or otherwise return a `&dyn Options` instance
242    /// which would create a cycle. Doing so may cause other methods or `gumdrop` functions
243    /// to loop infinitely or overflow the runtime stack.
244    fn command(&self) -> Option<&dyn Options>;
245
246    /// Returns the name of a parsed command, if present.
247    ///
248    /// This is implemented by `derive(Options)` in one of two ways:
249    ///
250    /// * For `struct` types, if the type contains a field marked
251    ///   `#[options(command)]`, this method is called on that value.
252    ///   Otherwise, `None` is returned.
253    /// * For `enum` types, the name corresponding to the variant is returned.
254    fn command_name(&self) -> Option<&'static str> { None }
255
256    /// Returns whether the user supplied a "help" option to request
257    /// usage information about the program or any contained subcommands.
258    ///
259    /// The default implementation returns `false`.
260    fn help_requested(&self) -> bool { false }
261
262    /// Parses arguments received from the command line.
263    ///
264    /// The first argument (the program name) should be omitted.
265    fn parse_args<S: AsRef<str>>(args: &[S], style: ParsingStyle) -> Result<Self, Error>
266            where Self: Sized {
267        Self::parse(&mut Parser::new(args, style))
268    }
269
270    /// Parses arguments from the environment.
271    ///
272    /// If an error is encountered, the error is printed to `stderr` and the
273    /// process will exit with status code `2`.
274    ///
275    /// If the user supplies a help option, option usage will be printed to
276    /// `stderr` and the process will exit with status code `0`.
277    ///
278    /// Otherwise, the parsed options are returned.
279    fn parse_args_or_exit(style: ParsingStyle) -> Self where Self: Sized {
280        use std::env::args;
281        use std::process::exit;
282
283        let args = args().collect::<Vec<_>>();
284
285        let opts = Self::parse_args(&args[1..], style).unwrap_or_else(|e| {
286            eprintln!("{}: {}", args[0], e);
287            exit(2);
288        });
289
290        if opts.help_requested() {
291            let mut command = &opts as &dyn Options;
292            let mut command_str = String::new();
293
294            loop {
295                if let Some(new_command) = command.command() {
296                    command = new_command;
297
298                    if let Some(name) = new_command.command_name() {
299                        command_str.push(' ');
300                        command_str.push_str(name);
301                    }
302                } else {
303                    break;
304                }
305            }
306
307            eprintln!("Usage: {}{} [OPTIONS]", args[0], command_str);
308            eprintln!();
309            eprintln!("{}", command.self_usage());
310
311            if let Some(cmds) = command.self_command_list() {
312                eprintln!();
313                eprintln!("Available commands:");
314                eprintln!("{}", cmds);
315            }
316
317            exit(0);
318        }
319
320        opts
321    }
322
323    /// Parses arguments from the environment, using the default
324    /// [parsing style](enum.ParsingStyle.html).
325    ///
326    /// If an error is encountered, the error is printed to `stderr` and the
327    /// process will exit with status code `2`.
328    ///
329    /// If the user supplies a help option, option usage will be printed to
330    /// `stderr` and the process will exit with status code `0`.
331    ///
332    /// Otherwise, the parsed options are returned.
333    fn parse_args_default_or_exit() -> Self where Self: Sized {
334        Self::parse_args_or_exit(ParsingStyle::default())
335    }
336
337    /// Parses arguments received from the command line,
338    /// using the default [parsing style](enum.ParsingStyle.html).
339    ///
340    /// The first argument (the program name) should be omitted.
341    fn parse_args_default<S: AsRef<str>>(args: &[S]) -> Result<Self, Error> where Self: Sized {
342        Self::parse(&mut Parser::new(args, ParsingStyle::default()))
343    }
344
345    /// Parses options for the named command.
346    fn parse_command<S: AsRef<str>>(name: &str, parser: &mut Parser<S>) -> Result<Self, Error> where Self: Sized;
347
348    /// Returns a string showing usage and help for each supported option.
349    ///
350    /// Option descriptions are separated by newlines. The returned string
351    /// should **not** end with a newline.
352    fn usage() -> &'static str where Self: Sized;
353
354    /// Returns a string showing usage and help for this options instance.
355    ///
356    /// In contrast to `usage`, this method will return usage for a subcommand,
357    /// if one is selected.
358    ///
359    /// Option descriptions are separated by newlines. The returned string
360    /// should **not** end with a newline.
361    fn self_usage(&self) -> &'static str;
362
363    /// Returns a usage string for the named command.
364    ///
365    /// If the named command does not exist, `None` is returned.
366    ///
367    /// Command descriptions are separated by newlines. The returned string
368    /// should **not** end with a newline.
369    fn command_usage(command: &str) -> Option<&'static str> where Self: Sized;
370
371    /// Returns a string listing available commands and help text.
372    ///
373    /// Commands are separated by newlines. The string should **not** end with
374    /// a newline.
375    ///
376    /// For `enum` types with `derive(Options)`, this is the same as `usage`.
377    ///
378    /// For `struct` types containing a field marked `#[options(command)]`,
379    /// `usage` is called on the command type.
380    fn command_list() -> Option<&'static str> where Self: Sized;
381
382    /// Returns a listing of available commands and help text.
383    ///
384    /// In contrast to `usage`, this method will return command list for a subcommand,
385    /// if one is selected.
386    ///
387    /// Commands are separated by newlines. The string should **not** end with
388    /// a newline.
389    fn self_command_list(&self) -> Option<&'static str>;
390}
391
392/// Controls behavior of free arguments in `Parser`
393///
394/// The [`parse_args_default`] and [`parse_args_default_or_exit`] functions will use the
395/// default parsing style, `AllOptions`.
396///
397/// # Examples
398///
399/// ```
400/// use gumdrop::{Options, ParsingStyle};
401///
402/// #[derive(Options)]
403/// struct MyOptions {
404///     // If the "-o" is parsed as an option, this will be `true`.
405///     option: bool,
406///     // All free (non-option) arguments will be collected into this Vec.
407///     #[options(free)]
408///     free: Vec<String>,
409/// }
410///
411/// // Command line arguments.
412/// let args = &["foo", "-o", "bar"];
413///
414/// // Using the `AllOptions` parsing style, the "-o" argument in the middle of args
415/// // will be parsed as an option.
416/// let opts = MyOptions::parse_args(args, ParsingStyle::AllOptions).unwrap();
417///
418/// assert_eq!(opts.option, true);
419/// assert_eq!(opts.free, vec!["foo", "bar"]);
420///
421/// // Using the `StopAtFirstFree` option, the first non-option argument will terminate
422/// // option parsing. That means "-o" is treated as a free argument.
423/// let opts = MyOptions::parse_args(args, ParsingStyle::StopAtFirstFree).unwrap();
424///
425/// assert_eq!(opts.option, false);
426/// assert_eq!(opts.free, vec!["foo", "-o", "bar"]);
427/// ```
428///
429/// [`parse_args_default`]: fn.parse_args_default.html
430/// [`parse_args_default_or_exit`]: fn.parse_args_default_or_exit.html
431#[derive(Copy, Clone, Debug, Eq, PartialEq)]
432pub enum ParsingStyle {
433    /// Process all option arguments that appear
434    AllOptions,
435    /// After the first "free" argument is encountered,
436    /// all remaining arguments will be considered "free" arguments.
437    StopAtFirstFree,
438}
439
440impl Error {
441    /// Returns an error for a failed attempt at parsing an option value.
442    pub fn failed_parse(opt: Opt, err: String) -> Error {
443        Error{kind: ErrorKind::FailedParse(opt.to_string(), err)}
444    }
445
446    /// Returns an error for a failed attempt at parsing an option's default value.
447    pub fn failed_parse_default(option: &'static str,
448            value: &'static str, err: String) -> Error {
449        Error{kind: ErrorKind::FailedParseDefault{option, value, err}}
450    }
451
452    /// Returns an error for a failed attempt at parsing an option value.
453    pub fn failed_parse_with_name(name: String, err: String) -> Error {
454        Error{kind: ErrorKind::FailedParse(name, err)}
455    }
456
457    /// Returns an error for an option expecting two or more arguments not
458    /// receiving the expected number of arguments.
459    pub fn insufficient_arguments(opt: Opt, expected: usize, found: usize) -> Error {
460        Error{kind: ErrorKind::InsufficientArguments{
461            option: opt.to_string(),
462            expected: expected,
463            found: found,
464        }}
465    }
466
467    /// Returns an error for an option receiving an unexpected argument value,
468    /// e.g. `--option=value`.
469    pub fn unexpected_argument(opt: Opt) -> Error {
470        Error{kind: ErrorKind::UnexpectedArgument(opt.to_string())}
471    }
472
473    /// Returns an error for an option expecting two or more argument values
474    /// receiving only one in the long form, e.g. `--option=value`.
475    ///
476    /// These options must be passed as, e.g. `--option value second-value [...]`.
477    pub fn unexpected_single_argument(opt: Opt, n: usize) -> Error {
478        Error{kind: ErrorKind::UnexpectedSingleArgument(opt.to_string(), n)}
479    }
480
481    /// Returns an error for a missing required argument.
482    pub fn missing_argument(opt: Opt) -> Error {
483        Error{kind: ErrorKind::MissingArgument(opt.to_string())}
484    }
485
486    /// Returns an error for a missing command name.
487    pub fn missing_command() -> Error {
488        Error{kind: ErrorKind::MissingCommand}
489    }
490
491    /// Returns an error for a missing required option.
492    pub fn missing_required(opt: &str) -> Error {
493        Error{kind: ErrorKind::MissingRequired(opt.to_owned())}
494    }
495
496    /// Returns an error for a missing required command.
497    pub fn missing_required_command() -> Error {
498        Error{kind: ErrorKind::MissingRequiredCommand}
499    }
500
501    /// Returns an error for a missing required free argument.
502    pub fn missing_required_free() -> Error {
503        Error{kind: ErrorKind::MissingRequiredFree}
504    }
505
506    /// Returns an error when a free argument was encountered, but the options
507    /// type does not support free arguments.
508    pub fn unexpected_free(arg: &str) -> Error {
509        Error{kind: ErrorKind::UnexpectedFree(arg.to_owned())}
510    }
511
512    /// Returns an error for an unrecognized command.
513    pub fn unrecognized_command(name: &str) -> Error {
514        Error{kind: ErrorKind::UnrecognizedCommand(name.to_owned())}
515    }
516
517    /// Returns an error for an unrecognized option.
518    pub fn unrecognized_option(opt: Opt) -> Error {
519        match opt {
520            Opt::Short(short) => Error::unrecognized_short(short),
521            Opt::Long(long) | Opt::LongWithArg(long, _) =>
522                Error::unrecognized_long(long),
523            Opt::Free(_) => panic!("`Error::unrecognized_option` called with `Opt::Free` value")
524        }
525    }
526
527    /// Returns an error for an unrecognized long option, e.g. `--option`.
528    pub fn unrecognized_long(opt: &str) -> Error {
529        Error{kind: ErrorKind::UnrecognizedLongOption(opt.to_owned())}
530    }
531
532    /// Returns an error for an unrecognized short option, e.g. `-o`.
533    pub fn unrecognized_short(opt: char) -> Error {
534        Error{kind: ErrorKind::UnrecognizedShortOption(opt)}
535    }
536}
537
538impl fmt::Display for Error {
539    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
540        use self::ErrorKind::*;
541
542        match &self.kind {
543            FailedParse(opt, arg) => write!(f, "invalid argument to option `{}`: {}", opt, arg),
544            FailedParseDefault{option, value, err} => write!(f, "invalid default value for `{}` ({:?}): {}", option, value, err),
545            InsufficientArguments{option, expected, found} =>
546                write!(f, "insufficient arguments to option `{}`: expected {}; found {}",
547                    option, expected, found),
548            MissingArgument(opt) => write!(f, "missing argument to option `{}`", opt),
549            MissingCommand => f.write_str("missing command name"),
550            MissingRequired(opt) => write!(f, "missing required option `{}`", opt),
551            MissingRequiredCommand => f.write_str("missing required command"),
552            MissingRequiredFree => f.write_str("missing required free argument"),
553            UnexpectedArgument(opt) => write!(f, "option `{}` does not accept an argument", opt),
554            UnexpectedSingleArgument(opt, n) =>
555                write!(f, "option `{}` expects {} arguments; found 1", opt, n),
556            UnexpectedFree(arg) => write!(f, "unexpected free argument `{}`", arg),
557            UnrecognizedCommand(cmd) => write!(f, "unrecognized command `{}`", cmd),
558            UnrecognizedLongOption(opt) => write!(f, "unrecognized option `--{}`", opt),
559            UnrecognizedShortOption(opt) => write!(f, "unrecognized option `-{}`", opt),
560        }
561    }
562}
563
564impl StdError for Error {
565    fn description(&self) -> &str {
566        "failed to parse arguments"
567    }
568}
569
570impl<'a, S: 'a + AsRef<str>> Parser<'a, S> {
571    /// Returns a new parser for the given series of arguments.
572    ///
573    /// The given slice should **not** contain the program name as its first
574    /// element.
575    pub fn new(args: &'a [S], style: ParsingStyle) -> Parser<'a, S> {
576        Parser{
577            args: args.iter(),
578            cur: None,
579            style: style,
580            terminated: false,
581        }
582    }
583
584    /// Returns the next option or `None` if no options remain.
585    pub fn next_opt(&mut self) -> Option<Opt<'a>> {
586        if let Some(mut cur) = self.cur.take() {
587            if let Some(opt) = cur.next() {
588                self.cur = Some(cur);
589                return Some(Opt::Short(opt));
590            }
591        }
592
593        if self.terminated {
594            return self.args.next().map(|s| Opt::Free(s.as_ref()));
595        }
596
597        match self.args.next().map(|s| s.as_ref()) {
598            Some(arg @ "-") => {
599                if self.style == ParsingStyle::StopAtFirstFree {
600                    self.terminated = true;
601                }
602                Some(Opt::Free(arg))
603            }
604            Some("--") => {
605                self.terminated = true;
606                self.args.next().map(|s| Opt::Free(s.as_ref()))
607            }
608            Some(long) if long.starts_with("--") => {
609                match long.find('=') {
610                    Some(pos) => Some(Opt::LongWithArg(
611                        &long[2..pos], &long[pos + 1..])),
612                    None => Some(Opt::Long(&long[2..]))
613                }
614            }
615            Some(short) if short.starts_with('-') => {
616                let mut chars = short[1..].chars();
617
618                let res = chars.next().map(Opt::Short);
619
620                self.cur = Some(chars);
621                res
622            }
623            Some(free) => {
624                if self.style == ParsingStyle::StopAtFirstFree {
625                    self.terminated = true;
626                }
627                Some(Opt::Free(free))
628            }
629            None => None
630        }
631    }
632
633    /// Returns the next argument to an option or `None` if none remain.
634    pub fn next_arg(&mut self) -> Option<&'a str> {
635        if let Some(cur) = self.cur.take() {
636            let arg = cur.as_str();
637
638            if !arg.is_empty() {
639                return Some(arg);
640            }
641        }
642
643        self.args.next().map(|s| s.as_ref())
644    }
645}
646
647impl<'a, S: 'a> Clone for Parser<'a, S> {
648    fn clone(&self) -> Parser<'a, S> {
649        Parser{
650            args: self.args.clone(),
651            cur: self.cur.clone(),
652            style: self.style,
653            terminated: self.terminated,
654        }
655    }
656}
657
658impl<'a> Opt<'a> {
659    #[doc(hidden)]
660    pub fn to_string(&self) -> String {
661        match *self {
662            Opt::Short(ch) => format!("-{}", ch),
663            Opt::Long(s) => format!("--{}", s),
664            Opt::LongWithArg(opt, _) => format!("--{}", opt),
665            Opt::Free(_) => "free".to_owned()
666        }
667    }
668}
669
670impl Default for ParsingStyle {
671    /// Returns the default parsing style, `AllOptions`.
672    fn default() -> ParsingStyle {
673        ParsingStyle::AllOptions
674    }
675}
676
677/// Parses arguments from the command line.
678///
679/// The first argument (the program name) should be omitted.
680pub fn parse_args<T: Options>(args: &[String], style: ParsingStyle) -> Result<T, Error> {
681    T::parse_args(args, style)
682}
683
684/// Parses arguments from the command line using the default
685/// [parsing style](enum.ParsingStyle.html).
686///
687/// The first argument (the program name) should be omitted.
688pub fn parse_args_default<T: Options>(args: &[String]) -> Result<T, Error> {
689    T::parse_args_default(args)
690}
691
692/// Parses arguments from the environment.
693///
694/// If an error is encountered, the error is printed to `stderr` and the
695/// process will exit with status code `2`.
696///
697/// If the user supplies a help option, option usage will be printed to
698/// `stderr` and the process will exit with status code `0`.
699///
700/// Otherwise, the parsed options are returned.
701///
702/// # Panics
703///
704/// If any argument to the process is not valid unicode.
705pub fn parse_args_or_exit<T: Options>(style: ParsingStyle) -> T {
706    T::parse_args_or_exit(style)
707}
708
709/// Parses arguments from the environment, using the default
710/// [parsing style](enum.ParsingStyle.html).
711///
712/// If an error is encountered, the error is printed to `stderr` and the
713/// process will exit with status code `2`.
714///
715/// If the user supplies a help option, option usage will be printed to
716/// `stderr` and the process will exit with status code `0`.
717///
718/// Otherwise, the parsed options are returned.
719///
720/// # Panics
721///
722/// If any argument to the process is not valid unicode.
723pub fn parse_args_default_or_exit<T: Options>() -> T {
724    T::parse_args_default_or_exit()
725}
726
727#[cfg(test)]
728mod test {
729    use super::{Opt, Parser, ParsingStyle};
730    use assert_matches::assert_matches;
731
732    #[test]
733    fn test_parser() {
734        let args = &["-a", "b", "-cde", "arg", "-xfoo", "--long", "--opt=val",
735            "--", "y", "-z"];
736
737        let mut p = Parser::new(args, ParsingStyle::AllOptions);
738
739        assert_matches!(p.next_opt(), Some(Opt::Short('a')));
740        assert_matches!(p.next_opt(), Some(Opt::Free("b")));
741        assert_matches!(p.next_opt(), Some(Opt::Short('c')));
742        assert_matches!(p.next_opt(), Some(Opt::Short('d')));
743        assert_matches!(p.next_opt(), Some(Opt::Short('e')));
744        assert_matches!(p.next_arg(), Some("arg"));
745        assert_matches!(p.next_opt(), Some(Opt::Short('x')));
746        assert_matches!(p.next_arg(), Some("foo"));
747        assert_matches!(p.next_opt(), Some(Opt::Long("long")));
748        assert_matches!(p.next_opt(), Some(Opt::LongWithArg("opt", "val")));
749        assert_matches!(p.next_opt(), Some(Opt::Free("y")));
750        assert_matches!(p.next_opt(), Some(Opt::Free("-z")));
751        assert_matches!(p.next_opt(), None);
752    }
753
754    #[test]
755    fn test_parsing_style() {
756        let args = &["-a", "b", "-c", "--d"];
757
758        let mut p = Parser::new(args, ParsingStyle::AllOptions);
759
760        assert_matches!(p.next_opt(), Some(Opt::Short('a')));
761        assert_matches!(p.next_opt(), Some(Opt::Free("b")));
762        assert_matches!(p.next_opt(), Some(Opt::Short('c')));
763        assert_matches!(p.next_opt(), Some(Opt::Long("d")));
764        assert_matches!(p.next_opt(), None);
765
766        let mut p = Parser::new(args, ParsingStyle::StopAtFirstFree);
767
768        assert_matches!(p.next_opt(), Some(Opt::Short('a')));
769        assert_matches!(p.next_opt(), Some(Opt::Free("b")));
770        assert_matches!(p.next_opt(), Some(Opt::Free("-c")));
771        assert_matches!(p.next_opt(), Some(Opt::Free("--d")));
772        assert_matches!(p.next_opt(), None);
773    }
774}