Expand description

An argument parser that is truly zero-cost, similar to Unix’s getopts.

About

getargs is a low-level, efficient and versatile argument parser that works similarly to getopts. It works by producing a stream of options, and after each option, your code decides whether to require and retrieve the value for the option or not.

You don’t have to declare a list of valid options up-front, so getargs does not have to allocate space for them or spend runtime searching for them. This also means that you have to write your own help message, but since --help is just another flag, there are no restrictions on what you do with it.

Features

  • Short -f and long --flag flags
  • Required implicit values -i VALUE and --implicit VALUE
  • Required or optional explicit values -eVALUE and --explicit=VALUE
  • Positional arguments and --
  • Parse options at the beginning of the argument list, or anywhere

Benefits

  • Zero cost
  • Zero copy
  • Zero unsafe code
  • Zero dependencies
  • Zero allocation
  • Simple to use yet versatile
  • #![no_std]-compatible
  • Compatible with &str and &[u8]

Performance

getargs has had a lot of attention put into profiling and optimization, and on a modern machine it takes under 0.2μs to parse a short array of 12 arguments.

In our testing, getargs is faster than every other argument parsing library on crates.io. Its closest competitor is gumdrop, which is only ~30% slower in the worst case, and its second-closest competitor is getopt, which takes three times as long. Other libraries degrade quickly; clap takes 45x longer. (This is not an entirely fair comparison though, as clap is not just an argument-parsing library, but an entire command-line application framework. It is perhaps overqualified for simple tasks.)

Quickstart

Check out the examples.

Overview

  • Options is the main export of the library, and handles argument parsing. If you don’t need a step-by-step tutorial, its documentation contains everything you need to jump right in.

  • Argument is implemented for each type that can be parsed by Options, such as &str and &[u8].

  • Opt represents a short or long option, returned by Options::next_opt.

  • Arg represents either an option or a positional argument, returned by Options::next_arg.

  • Error represents a parsing error.

Obtaining an Options

First, get an iterator over your arguments as &str or &[u8]. Usually, this is done by using args() or args_os():

let args = std::env::args()
    .skip(1) // remember to skip the executable name!
    .collect::<Vec<_>>();

On Linux, you can use the argv crate and OsStrExt to get an iterator of &[u8] without allocating:

// As seen in the `no_alloc` example - only works on Linux due to
// the use of `OsStrExt::as_bytes`
let args = argv::iter().skip(1).map(OsStrExt::as_bytes);

On other platforms, argv will leak memory, so be careful!

Then, pass the iterator to Options::new:

// ...
let mut opts = Options::new(args);

Accepting options at the start

If your command line looks like this, and does not accept options after positional arguments:

usage: command [OPTIONS] [ARGS]...

then you can use Options::next_opt directly, just like classical getargs (before 0.5.0) supported as next. The method is meant to be called in a while let loop, like so:

// ...
let mut opts = Options::new(iter);

while let Some(opt) = opts.next_opt()? {
    // read on!
}

Options notably does not implement Iterator, because a for loop borrows the iterator for the duration of the loop, which would make it impossible to call Options::value and friends. Interior mutability would make this possible, but it is difficult to find an API design that does not sacrifice performance.

After each Opt, you can choose to do one of three things:

  • Call Options::value, which will declare that the option requires a value, and return it or Error::RequiresValue.

  • Call Options::value_opt, which will declare that the option may have an optional value, and return the value, if any.

  • Do nothing before the next call to next_opt, which will assume that the option should not have a value (and return an Error::DoesNotRequireValue if an explicit value is found).

Options::next_opt will return Ok(None) when there are no arguments left, or when -- is encountered. Once that happens, you can iterate over the rest of the arguments as positional:

// ...
let mut opts = Options::new(iter);

while let Some(opt) = opts.next_opt()? {
    // ...
}

for positional in opts.positionals() {
    // ...
}

Here is the print example, which shows required and optional values:

use getargs::{Opt, Options};
use std::env::args;

fn main() {
    let mut args = args().skip(1).collect::<Vec<_>>();

    if args.is_empty() {
        args.push(String::from("--help")); // help the user out :)
    }

    let mut opts = Options::new(args.iter().map(String::as_str));

    while let Some(opt) = opts.next_opt().expect("argument parsing error") {
        match opt {
            Opt::Short('h') | Opt::Long("help") => {
                eprintln!(
                    r"Usage: print.rs [OPTIONS] [ARGS]...
This example prints all options and positional arguments passed to it.
It also includes some short and long options that have a required or
optional value.

  -h, --help       display this help and exit
  -o, --optional   consume an optional value and print the result
  -r, --required   consume a required value and print it
  <anything else>  print the flag passed"
                );

                return;
            }

            Opt::Short('o') | Opt::Long("optional") => {
                eprintln!("option {:?}: {:?}", opt, opts.value_opt());
            }

            Opt::Short('r') | Opt::Long("required") => {
                eprintln!("option {:?}: {:?}", opt, opts.value());
            }

            _ => eprintln!("option: {:?}", opt),
        }
    }

    for arg in opts.positionals() {
        eprintln!("positional: {:?}", arg)
    }
}

Accepting options anywhere

getargs includes a utility function, Options::next_arg, that can be used to easily accept both options and positional arguments anywhere. This is starting to become the behavior of many modern programs - arguments that do not begin with - or -- are counted as positional, but option parsing still continues.

All you have to do is call next_arg instead of next_opt, and match on Arg rather than Opt:

// ...
let mut opts = Options::new(iter);

while let Some(arg) = opts.next_arg()? {
    // ...
    match arg {
        Arg::Short('h') | Arg::Long("help") => { /* ... */ }
        Arg::Short('v') | Arg::Long("version") => { /* ... */ }
        Arg::Positional(positional) => { /* ... */ }
        _ => panic!("Unknown option: {}", arg)
    }
}

Arg also has some utility methods, like Arg::opt and Arg::positional.

The rest is virtually identical to parsing with next_opt.

Here is the anywhere example, which is similar to print but uses next_arg instead of next_opt:

//! This example shows how to use the `Options::next_arg` method to
//! accept options and positional arguments anywhere.
//!
//! Try it with arguments like: `x -r hi y -ohi z -- --not-a-flag`
//!
//! You will get:
//!
//! ```text
//! positional: "x"
//! option Short('r'): Ok("hi")
//! positional: "y"
//! option Short('o'): Some("hi")
//! positional: "z"
//! positional: "--not-a-flag"
//! ```

use getargs::{Arg, Options};
use std::env::args;

fn main() {
    let mut args = args().skip(1).collect::<Vec<_>>();

    if args.is_empty() {
        args.push(String::from("--help")); // help the user out :)
    }

    let mut opts = Options::new(args.iter().map(String::as_str));

    while let Some(arg) = opts.next_arg().expect("argument parsing error") {
        match arg {
            Arg::Short('h') | Arg::Long("help") => {
                eprintln!(
                    r"Usage: anywhere.rs [OPTIONS/ARGS]...
This example prints all options and positional arguments passed to it,
but options and positional arguments can be passed anywhere. It also
includes some short and long options that have a required or optional
value, just like print.rs.

  -h, --help       display this help and exit
  -o, --optional   consume an optional value and print the result
  -r, --required   consume a required value and print it
  <anything else>  print the flag passed"
                );

                return;
            }

            Arg::Short('o') | Arg::Long("optional") => {
                eprintln!("option {:?}: {:?}", arg, opts.value_opt());
            }

            Arg::Short('r') | Arg::Long("required") => {
                eprintln!("option {:?}: {:?}", arg, opts.value());
            }

            Arg::Short(_) | Arg::Long(_) => eprintln!("option: {:?}", arg),
            Arg::Positional(arg) => eprintln!("positional: {:?}", arg),
        }
    }
}

Examples

There are other examples available on GitHub.

Structs

An iterator over what used to be the positional arguments of an Options.

An argument parser.

An iterator over the positional arguments of an Options.

Enums

An option or positional argument.

An argument parsing error.

A short or long option.

Traits

The argument trait for types that can be parsed by Options.

Type Definitions