3 releases (breaking)
0.3.0 | Apr 19, 2023 |
---|---|
0.2.0 | Mar 26, 2023 |
0.1.0 | Feb 7, 2023 |
#90 in #optimized
1,225 downloads per month
Used in 12 crates
(via argp)
88KB
2K
SLoC
Argp is a Derive-based argument parser optimized for code size and flexibility.
The public API of this library consists primarily of the FromArgs
derive and the parse_args_or_exit
function, which can be used to produce a top-level FromArgs
type from the current program’s command-line arguments.
Features
-
Zero runtime dependencies.
-
Small size overhead – ~40 kiB [1], that’s 10x less than clap or clap_derive! See argparse-rosetta-rs for more details.
-
Derive-based API – you define structs and enums for the parsed values, use attributes to specify how they should be parsed and a procedural derive macro will generate the parser at compile-time.
-
Context-sensitive parsing.
-
Support for subcommands.
-
Help message generator with Markdown support and dynamic wrapping based on terminal width.
Origins
Argp originally started as a fork of argh to make it less opinionated, more UNIXy and flexible.
Notable changes from argh:
-
Support for global options (i.e. options defined at the top level can be used in subcommands).
-
Support for combined short options (e.g.
-ab
is parsed as-a -b
,-an 5
as-a -n 5
). -
Support for non-UTF8 arguments (OsStr).
-
The
from_str_fn
attribute can also contain a function path, not just a single identifier, and can return anyErr
type which implementsToString
. -
No pedantic requirement for descriptions to start with a lower-case letter.
-
Help message is dynamically wrapped based on terminal width (on unix systems).
-
The indentation of descriptions in the help message is dynamically calculated based on the widths of all elements.
-
The
arg_name
attribute can also be used even on positional arguments to customise how the argument is displayed in the help message. -
Errors are represented using an enum instead of a String and the information used to generate a help message is stored in a partially structured form in a struct; this opens the door to customisation of messages.
-
Specialised
example
,note
, anderror_code
attributes are replaced by a singlefooter
attribute – you can use it for whatever you like. -
Positional arguments in the Usage string are displayed after options and switches and
<arg_name>
is displayed in descriptions of options. -
Trailing options are allowed after the
-h, --help
switch, but are not allowed after thehelp
subcommand only. -
The
from_env
function has been renamed toparse_args_or_exit
,cargo_from_env
tocargo_parse_args_or_exit
. -
redact_arg_values
has been removed (if you happen to need it, let me know in Issues).
Basic Example
use argp::FromArgs;
/// Reach new heights.
#[derive(FromArgs)]
struct GoUp {
/// Whether or not to jump.
#[argp(switch, short = 'j')]
jump: bool,
/// How high to go.
#[argp(option, arg_name = "meters")]
height: usize,
/// An optional nickname for the pilot.
#[argp(option, arg_name = "name")]
pilot_nickname: Option<String>,
}
fn main() {
let up: GoUp = argp::parse_args_or_exit(argp::DEFAULT);
}
./some_bin --help
will then output the following:
Usage: cmdname [-j] --height <meters> [--pilot-nickname <name>]
Reach new heights.
Options:
-j, --jump Whether or not to jump.
--height <meters> How high to go.
--pilot-nickname <name> An optional nickname for the pilot.
-h, --help Show this help message and exit.
The resulting program can then be used in any of these ways:
-
./some_bin --height 5
-
./some_bin -j --height 5
-
./some_bin --jump --height 5 --pilot-nickname Wes
Switches, like jump
, are optional and will be set to true if provided.
Options, like height
and pilot_nickname
, can be either required, optional, or repeating, depending on whether they are contained in an Option
or a Vec
.
Default values can be provided using the #[argp(default = "<your_code_here>")]
attribute, and in this case an option is treated as optional.
use argp::FromArgs;
fn default_height() -> usize {
5
}
/// Reach new heights.
#[derive(FromArgs)]
struct GoUp {
/// An optional nickname for the pilot.
#[argp(option)]
pilot_nickname: Option<String>,
/// An optional height.
#[argp(option, default = "default_height()")]
height: usize,
/// An optional direction which is "up" by default.
#[argp(option, default = "String::from(\"only up\")")]
direction: String,
}
fn main() {
let up: GoUp = argp::parse_args_or_exit(argp::DEFAULT);
}
Custom option types can be deserialized so long as they implement the FromArgValue
trait (already implemented for most types in std for which the FromStr
trait is implemented).
If more customized parsing is required, you can supply a custom fn(&str) → Result<T, E>
using the from_str_fn
attribute, or fn(&OsStr) → Result<T, E>
using the from_os_str_fn
attribute, where E
implements ToString
:
use argp::FromArgs;
use std::ffi::OsStr;
use std::path::PathBuf;
/// Goofy thing.
#[derive(FromArgs)]
struct FineStruct {
/// Always five.
#[argp(option, from_str_fn(always_five))]
five: usize,
/// File path.
#[argp(option, from_os_str_fn(convert_path))]
path: PathBuf,
}
fn always_five(_value: &str) -> Result<usize, String> {
Ok(5)
}
fn convert_path(value: &OsStr) -> Result<PathBuf, String> {
Ok(PathBuf::from("/tmp").join(value))
}
Positional arguments can be declared using #[argp(positional)]
.
These arguments will be parsed in order of their declaration in the structure:
use argp::FromArgs;
/// A command with positional arguments.
#[derive(FromArgs, PartialEq, Debug)]
struct WithPositional {
#[argp(positional)]
first: String,
}
The last positional argument may include a default, or be wrapped in Option
or Vec
to indicate an optional or repeating positional argument.
Subcommands are also supported.
To use a subcommand, declare a separate FromArgs
type for each subcommand as well as an enum that cases over each command:
use argp::FromArgs;
/// Top-level command.
#[derive(FromArgs, PartialEq, Debug)]
struct TopLevel {
/// Be verbose.
#[argp(switch, short = 'v', global)]
verbose: bool,
#[argp(subcommand)]
nested: MySubCommandEnum,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argp(subcommand)]
enum MySubCommandEnum {
One(SubCommandOne),
Two(SubCommandTwo),
}
/// First subcommand.
#[derive(FromArgs, PartialEq, Debug)]
#[argp(subcommand, name = "one")]
struct SubCommandOne {
/// How many x.
#[argp(option)]
x: usize,
}
/// Second subcommand.
#[derive(FromArgs, PartialEq, Debug)]
#[argp(subcommand, name = "two")]
struct SubCommandTwo {
/// Whether to fooey.
#[argp(switch)]
fooey: bool,
}
For more information, refer to the argp documentation.
How to debug the expanded derive macro for argp
The argp::FromArgs
derive macro can be debugged with the cargo-expand crate.
Expand the derive macro in examples/simple_example.rs
See argp/examples/simple_example.rs for the example struct we wish to expand.
First, install cargo-expand
by running cargo install cargo-expand
.
Note this requires the nightly build of Rust.
Once installed, run cargo expand
with in the argp
package and you can see the expanded code.
License
This project is licensed under BSD-3-Clause license. For the full text of the license, see the LICENSE file.
- Measured on a release build with
strip = true
andpanic = "abort"
. The exact size depends on several factors, including the number of options and subcommands.
Dependencies
~4MB
~78K SLoC