7 releases
0.1.6 | Jun 26, 2021 |
---|---|
0.1.5 | Jun 26, 2021 |
0.1.4 | Apr 18, 2021 |
#503 in Command-line interface
79KB
1.5K
SLoC
Parsnip
A small Rust Argparser
// ./prog --arg param1 param2 -a
fn main() {
let args = args! {
// define all your args here
args: vec![arg! {
name: "arg",
short: Some("a"),
}, arg! {
name: "arg2",
long: Some("arg2"),
num_values: NumValues::Between(1, 3),
}],
// subcommands (e.g git add)
subcommands: vec![args! {}],
// optional, by default parsing will return a "Results" object
handler: |results| { return results.args.get("arg2"); }
};
let results = args.parse(std::env::args());
}
Setup
Add the lib to your Cargo.toml
.
[dependencies]
argparsnip = "0.1.5"
Features
- Autogenerated help/version commands
- can be overwritten
- [subcommand]* help & [subcommand]* version commands are also generated
- name/version/about parameters default to using Cargo.toml
CARGO_PKG_NAME/CARGO_PKG_VERSION/CARGO_PKG_DESCRIPTION
variables
- Arguments
- supports short
-h
and--help
syntax - unicode support by default
- supports returning (as a vec) or failing on unknown arguments
- Flags
- supports combinations (e.g
-rli
is the same as-r -l -i
) - supports repeats, e.g
-vvv -v
will count as the same flagv
appearing 4 times
- supports combinations (e.g
- With Values
- supports constraints on the number of values for an arg, e.g having exactly one value, having between 2 & 4 values, having any number of values. E.g
-v foo bar
- supports restricting values to specific primitive types (any, bool, i32, i64, f32, f64, String) via the TryInto trait
- supports additional custom validation (so you can e.g write your own sum type restrictions)
- supports default values
- supports constraints on the number of values for an arg, e.g having exactly one value, having between 2 & 4 values, having any number of values. E.g
- Combinations
- Parsing can be configured to fail if any required argument is missing
- supports requiring at least one of a set of arguments (e.g A || B || C)
- supports requiring all arguments in a set (e.g A && B && C)
- supports inverting sets (e.g !(A && B))
- supports requiring any or all of multiple sets (e.g (A && B) || (A && C)). This can also be negated
- Positional
- supports unix
--
, i.efoo -- -a -b -c
will recieve positional arguments ["-a", "-b", "-c"] - treats all args that don't start with
-
or--
as positional.
- supports unix
- supports short
- Subcommands
- e.g cargo run vs cargo test (run/test are subcommands)
- subcommands can have their own subcommands and arguments
- help/version commands are generated separately for each subcommand
- Optional Callback support
- Instead of returning a results object with the argparsing results, A handler fn(Results) -> T can be provided for each command/subcommand (not supported when using serde).
- no_std support
- disable default features to enable no_std
parsnip = { version = "x", default-features = false }
- serde support
parsnip = { version = "x", features = ["derive"] }
- write your args schema in any format with a serde parser (serde_json, toml etc.), see derive-test for an example
- Other opt-in features
- debug - enables logging info about arg parsing
- macros - enabled by default, we provide some utility macros to avoid writing ..Default::default() everywhere
Usage
Here are some quick common cases. For more examples please look at the tests in lib.rs
Documentation
https://docs.rs/argparsnip/0.1.5/argparsnip/
Examples
Minimal Flag Example
// ./prog --arg
fn main() {
let args = args! {
args: vec![arg! {
name: "arg",
short: Some("a"),
}],
};
let results = args.parse(std::env::args());
assert_eq!(1, results.flags("arg"));
}
Check if a flag was given once
// ./prog --arg
fn main() {
let args = args! {
args: vec![arg! {
name: "arg",
short: Some("a"),
about: "a flag",
long: Some("arg"),
required: true,
}],
};
let results = args.parse(std::env::args());
assert_eq!(1, results.flags("arg"));
}
Get the value of an arg
// ./prog -a 1
fn main() {
let args = args! {
args: vec![arg! {
name: "arg",
short: Some("a"),
default: Some(|| { Value::From(2) }),
value_type: Type::Int,
num_values: NumValues::Fixed(1),
}],
};
let results = args.parse(std::env::args());
assert_eq!(1, results.params.get("arg")?.try_into());
}
Validate an argument
// ./prog -a 1 2
fn main() {
let args = args! {
args: vec![arg! {
name: "arg",
short: Some("a"),
value_type: Type::Int,
num_values: NumValues::AtLeast(1),
validation: |val| {
let val: &i32 = v.try_into().unwrap();
if 2 >= *val {
Ok(())
} else {
Err("failed validation")
}
}
}],
};
let results = args.parse(std::env::args());
assert_eq!(vec![1, 2], results.params.get("arg")?.try_into());
}
Using Subcommand
// ./prog sub --arg
fn main() {
let args = args! {
args: vec![arg! {
name: "arg",
long: Some("arg"),
num_values: NumValues::None,
}],
subcommands: vec![args! {
name: "sub",
path: Some("main/sub"),
args: vec![arg! {
name: "arg",
long: Some("arg"),
num_values: NumValues::None,
}],
}],
};
let results = args.parse(std::env::args());
// this is the unique identifier for the subcommand
assert_eq!("main/sub", results.path);
assert_eq!(1, results.flags["arg"]);
}
Filters
// only supports combinations (--arg && --arg2) or (--arg && --arg3)
// will fail if --arg or --arg2 or --arg3 are passed on their own
fn main() {
let args = args! {
args: vec![arg! {
name: "arg",
long: Some("arg"),
num_values: NumValues::None,
}, arg! {
name: "arg2",
long: Some("arg2"),
num_values: NumValues::None,
}, arg! {
name: "arg3",
long: Some("arg3"),
num_values: NumValues::None,
}],
filters: Filters {
filters: vec![Filter {
filter_type: FilterType::All,
inverse: false,
args: vec!["arg", "arg2"],
}, Filter {
filter_type: FilterType::All,
inverse: false,
args: vec!["arg", "arg3"],
}],
..Default::default()
},
// this flag means we will fail if we see the same value multiple times
disable_overrides: true,
};
let results = args.parse(std::env::args());
}
Development
TODO
- Benchmarks
- More tests
- Features
- Bash/Zsh completion
- Support disabling positional args
- Support updating repeats, e.g --arg Foo --arg Bar should give {"arg": ["Foo", "Bar"]}
- Pretty sure my design isn't iterator friendly, try using a counter instead
Dependencies
~315–720KB
~11K SLoC