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 byOptions
, such as&str
and&[u8]
. -
Opt
represents a short or long option, returned byOptions::next_opt
. -
Arg
represents either an option or a positional argument, returned byOptions::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 orError::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 anError::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§
- Into
Positionals - An iterator over what used to be the positional arguments of an
Options
. - Options
- An argument parser.
- Positionals
- An iterator over the positional arguments of an
Options
.