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}