1 unstable release
Uses old Rust 2015
0.0.1 | Oct 12, 2017 |
---|
#4250 in #parser
37KB
599 lines
TypeParam allows to write argument parsing in typesafe manner.
TypeParam is a macro taking an annotated structure and generating a structure and implementation for parsing. Internally it uses clap.
Please note that it is in very early stage of development and not all features are implemented. The interface and defaults are subject to change.
Example
#[macro_use]
extern crate typeparam;
extern crate clap;
typeparam!{
struct Params [@app test ::std::string::ParseError] {
quiet: bool [QUIET: -q],
verbose: bool [VERBOSE: -v (value: flag)],
cfg: String [CFG: -c (value: default String::from("---"))],
path: String [PATH: -p (value: required)],
foo: Option<String> [FOO: --foo (value: optional)],
n: Option<u32> [N: -n (value: option map (|_v: Option<&str>| Some(3)))],
x: u32 [X: -x (value: map (|_| 4))],
command: @SUBCOMMANDS<Commands> [list => List(List),
get => Get2(Get),
foo => Foo: (default = Default)]
}
struct List [@subcommand ::std::string::ParseError];
struct Get [@subcommand ::std::string::ParseError];
}
#
fn main() {
use typeparam::Command;
let params = Params::parse(["simple", "-v", "--foo", "bar", "-p", "path", "-x", "X"].iter()).unwrap();
assert!(!params.quiet);
assert!(params.verbose);
assert_eq!(params.cfg, "---");
assert_eq!(params.path, "path");
assert_eq!(params.foo, Some(String::from("bar")));
assert_eq!(params.n, Some(3));
assert_eq!(params.x, 4);
match params.command {
Commands::Default => {},
Commands::List(_) | Commands::Get2(_) | Commands::Foo => {
panic!("params.commands != Commands::Default")
}
}
}
In following example it created an parsing structure for application named
test
. Application takes two required arguments - -p
and -x
- and
three optional ones - --cfg
, --foo
and -n
. In addition it accepts two
flags - -q
anf -v
.
It accepts optionally one of three commands - list
, get
or foo
.
After successful parsing it returns a structure containing parsed commands.
Structures and Enums
Currently two types of structures are accepted - apps and subcommands. Both have the same syntax with exception of the square brackets after type name.
Apps need to have @app
as first token appearing in square bracket followed
by app name and type of error parsing may return.
Subcommands need to have @subcommand
as first token followed by type of
error parsing may return.
Parameters and Options
Parameters and options are specified as normal fields in struct followed by square brackets containing their name. Optionally after the name there can be colon followed by any number of arguments. Arguments can be specified in any order.
To denote a short option a dash with a letter should be specified as argument
(-l
) while long double dash with an identifier (--foo
). They are not
mutually exclusive.
#[macro_use]
extern crate typeparam;
extern crate clap;
typeparam!{
struct Params [@app test ::std::string::ParseError] {
foo: bool [FOO: --foo],
bar: bool [BAR: -b],
foobar: bool [FOOBAR: -f --foobar]
}
}
fn main() {
use typeparam::Command;
let params = Params::parse(["simple", "-f", "--foo"].iter()).unwrap();
assert_eq!(params.foo, true);
assert_eq!(params.bar, false);
assert_eq!(params.foobar, true);
}
If either option is omitted the field denote a positional argument.
#[macro_use]
extern crate typeparam;
extern crate clap;
typeparam!{
struct Params [@app test ::std::string::ParseError] {
foo: String [FOO: (value: required)],
bar: String [BAR: (value: required)],
foobar: bool [FOOBAR: -f]
}
}
fn main() {
use typeparam::Command;
let params = Params::parse(["simple", "foo", "-f", "bar"].iter()).unwrap();
assert_eq!(params.foo.as_str(), "foo");
assert_eq!(params.bar.as_str(), "bar");
assert_eq!(params.foobar, true);
}
Each option can also take a value
setting. Currently there are 5 valid
settings:
value: flag
is default and denotes a single flag - in other words no argument. Value returned isbool
.value: required
denotes an required argument. If user does not passes it, an error is returned. OtherwisefromStr
is called on value passed by user.value: optional
denotes an optional argument. If user does not passes itNone
is returned. OtherwisefromStr
is called on value and wrapped bySome
.value: map callback
denotes an required argument, just asvalue: required
, however it allows to supply arbitrary function. Both functions returning value directly as well asResult
are accepted.value: option map callback
denotes an optional argument, just asvalue: optional
. However it allows to supply an arbitrary function. Both functions returning value directly as well asResult
are accepted.
Subcommands
Subcommands functions as nested apps inside the command. This style has been popularized by (git)https://git-scm.com/.
For application (or recursivly subcommand) to have subcommands it is necessary
to add field of type @SUBCOMMANDS<NameOfEnum>
. There can be at most one such
field in struct.
#[macro_use]
extern crate typeparam;
extern crate clap;
typeparam!{
struct IllegalParams [@app illegal ::std::string::ParseError] {
illegal1: @SUBCOMMANDS<Illegal1> [foo => Foo],
illegal2: @SUBCOMMANDS<Illegal2> [foo => Bar]
}
}
Afterwards the subcommands are specified in square brackets. There are
two forms of subcommand specification - including type
(subcommand => Identifier(Type)
) and not (subcommand => Identifier
).
In the first form a type must be a subcommand and the description of fields
is taken from there. The second form makes a subcommand not to get any
additional fields.
In both cases the Identifier
is added to the enum specified after @SUBCOMMAND
- with single argument or without any arguments respectivly.
#[macro_use]
extern crate typeparam;
extern crate clap;
typeparam!{
struct Params [@app test ::std::string::ParseError] {
command: @SUBCOMMANDS<Commands> [list => List, find => Find(Find)],
debug: bool [DEBUG: -d (value: flag)]
}
}
typeparam!{
struct Find [@subcommand ::std::string::ParseError] {
name: String [NAME: (value: required)],
case_insensitive: bool [CASE_INSENSITIVE: -i (value: flag)]
}
}
#
fn main() {
use typeparam::Command;
let list = Params::parse(["simple", "-d", "list"].iter()).unwrap();
assert_eq!(list.debug, true);
match list.command {
Commands::List => {},
_ => panic!("Expected Commands::List")
}
let find = Params::parse(["simple", "-d", "find", "-i", "something"].iter()).unwrap();
assert_eq!(find.debug, true);
match find.command {
Commands::Find(find_cmd) => {
assert_eq!(find_cmd.case_insensitive, true);
assert_eq!(find_cmd.name.as_str(), "something");
},
_ => panic!("Expected Commands::Find(_)")
}
}
Optionally they can be followed by colon and any number of arguments passed.
Currently there is only one argument supported - (default = Value)
. If it is
specified Value
becomes a value of enum when no command is given. Otherwise
passing a command is required.
#[macro_use]
extern crate typeparam;
extern crate clap;
typeparam!{
struct Params [@app test ::std::string::ParseError] {
command: @SUBCOMMANDS<Commands> [list => List, find => Find(Find)],
debug: bool [DEBUG: -d (value: flag)]
}
struct Find [@subcommand ::std::string::ParseError] {
name: String [NAME: (value: required)],
case_insensitive: bool [CASE_INSENSITIVE: -i (value: flag)]
}
}
#
fn main() {
use typeparam::Command;
assert!(Params::parse(["simple"].iter()).is_err());
}
Dependencies
~1.5MB
~24K SLoC