Applause is a basic DSL that allows for easy construction of Clap App
s that
make use of subcommands.
It magicks away the endless match
es and enum
s to define subcommands,
arguments and handlers in favour of a simple dispatch system while passing as
much as possible back to clap
via its derive
API.
The applause
DSL is simple and aims to mimic normal Rust syntax.
The definition of the DSL syntax in Rust Reference Notation is as follows. Where syntactical items are as defined in the Reference, the appropriate page is linked.
See Examples for complete examples.
Syntax
Applause :
ParserDef
DispatchParams?
CommandsParserDef :
IDENTIFIER{
StructFields?}
;
DispatchParams :
dispatch_params
(
FunctionParams)
;
Commands :
commands
{
CommandDef +}
;
CommandDef :
IDENTIFIER ( EnumItemTuple | EnumItemStruct )
DispatchExpr?
,
DispatchExpr :
=>
BlockExpression
TODO
TODO
TODO: Rewrite more to do with mapping to clap uses
The syntax of a CommandDef is Rust-like but does not behave like any of the Rust syntactical constructs it resembles.
The IDENTIFIER, EnumItemTuple and EnumItemStruct are used to generate an
item in an enum
. This enum
is processed by the clap::Subcommand
derive macro and may take the same form as accepted by clap
, i.e. struct items
are parsed as clap::Args
and tuple items are use to refer to a separate
clap::Args
struct. Any attributes on the items or struct fields are passed
through appropriately.
The optional DispatchExpr construct is used to populate the generated dispatcher function.
Each subcommand may also have an associated DispatchExpr. This is inserted into the dispatcher for that subcommand's match arm.
For example, a subcommand defined as
commands {
Foo(Foo) => { handler.foo(); },
};
would produce a match arm like
match &self {
Foo(handler) => { handler.foo(); },
}
Note that the enum variant is automatically destructured into handler
, which can be referenced in the DispatchExpr. This is mainly useful for referring to clap
structs defined outside applause
.
A struct-like enum variant is automatically destructured to its named fields. A CommandDef of
Foo {name: String} => { println!("Saying foo to {}", name) },
would produce a match arm of
match &self {
// SNIP
Foo {name} => { println!("Saying foo to {}", name) }
// SNIP
}
TODO: Talk about how clap treats these as args for the subcommand
As a shorthand, if no BlockExpression is specified for an enum variant with an
unnamed field, for example Foo(commands::Foo),
, applause
will assume that the contained type is another
applause
generated Parser
. It will then attempt to invoke its dispatch method with no other parameters.
This means a command line such as my_exe foo bar
can be handled by
// main.rs - Top level
mod commands;
applause!{
Cli{};
commands {
Foo(commands::Foo),
};
}
fn main() {
dispatch!(Cli);
}
// commands.rs - Subcommands
applause!{
Foo{};
commands {
Bar(Bar) => { /* Do a bar */ },
}
}
applause
ships several declarative macros to help with parsing and dispatch of
commands.
The dispatch!(T)
macro parses the command line using the given type T
and
immediately dispatches to the subcommand handler.
let subcmd_result = dispatch_args!(Cli);
The dispatch_args!(T => args...)
macro parses the command line using the type
T
and dispatches to the handler along with the additional list of arguments
provided.
let subcmd_result = dispatch_args!(Cli => "arg1", "arg2");
This can be used to call the run
function when it has additional parameters
defined by the dispatch_params {}
block.
The parse_args!(T)
macro simply parses the command line with type T
but does
not perform any dispatch.
let cli = parse_args!(Cli);