This crates provides a procedural macro for parsing command line arguments.
It is intended for use in development tools, so it emphasizes fast compile times and convenience at the expense of features.
Rough decision tree for picking argument parsing library:
- if you need all of the features and don't care about minimalism, use clap
- if you want to be maximally minimal, need only basic features (eg, no help generation), and want to be pedantically correct, use lexopt
- if you want to get things done fast (eg, you want auto help, but not at the cost of waiting for syn to compile), consider this crate.
The secret sauce of xflags is that it is the opposite of a derive macro.
Rather than generating a command line grammar from a Rust struct, xflags
generates Rust structs based on input grammar. The grammar definition is
both shorter and simpler to write, and is lighter on compile times.
xflags supports "immediate" mode of parsing flags into "invisible" struct:
use std::path::PathBuf;
fn main() {
let flags = xflags::parse_or_exit! {
optional -r,--recursive
required path: PathBuf
};
println!(
"removing {}{}",
flags.path.display(),
if flags.recursive { "recursively" } else { "" },
)
}
Example
However, for non-tiny programs you would typically want full syntax, which also generates a Rust struct to hold a representation of arguments.
To make the macro less opaque, xflag can generate struct describing the
CLI in-place. To disable this behavior, omit the src attribute.
xflags correctly handles non-utf8 arguments.
Syntax Reference
The cmd keyword introduces a command that accepts positional arguments and switches.
xflags!
Switches are specified inside the curly braces. Long names (--switch) are
mandatory, short names (-s) are optional. Each switch can be optional,
required, or repeated. Dashes are allowed in switch names.
xflags!
Switches can also take values. If the value type is OsString or PathBuf,
it is created directly from the underlying argument. Otherwise, FromStr is
used for parsing
use ;
xflags!
Arguments without -- in then ame are positional.
use ;
xflags!
Nesting cmd is allowed. xflag automatically generates boilerplate
enums for subcommands:
xflags::xflags! {
src "./examples/subcommands.rs"
cmd app {
repeated -v, --verbose
cmd foo { optional -s, --switch }
cmd bar {}
}
}
// generated start
// The following code is generated by `xflags` macro.
// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
#[derive(Debug)]
pub struct App {
pub verbose: u32,
pub subcommand: AppCmd,
}
#[derive(Debug)]
pub enum AppCmd {
Foo(Foo),
Bar(Bar),
}
#[derive(Debug)]
pub struct Foo {
pub switch: bool,
}
#[derive(Debug)]
pub struct Bar {
}
impl App {
pub const HELP: &'static str = Self::HELP_;
pub fn from_env() -> xflags::Result<Self> {
Self::from_env_()
}
pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
Self::from_vec_(args)
}
}
// generated end
Switches are always "inherited". That is, both app init --home tmp and
app --home tmp init produce the same result given the following
definition:
use PathBuf;
xflags!
To make subcommand name optional use the default keyword to mark a subcommand to select if no subcommand name is passed. The name of the default subcommand affects only the name of the generated Rust struct, it can't be specified explicitly on the command line.
xflags!
Commands, arguments, and switches can be documented. Doc comments become a part of generated help:
#
The src keyword controls how the code generation works. If it is absent,
xflags acts as a typical procedure macro, which generates a bunch of
structs and impls.
If the src keyword is present, it should specify the path to the file
with xflags! invocation. The path should be relative to the directory with
Cargo.toml. The macro then will avoid generating the structs. Instead, if
the UPDATE_XFLAGS environmental variable is set, the macro will write them
directly to the specified file.
By convention, xflag! macro should be invoked from the flags submodule.
The flags:: prefix should be used to refer to command names. Additional
validation logic can go to the flags module: