//! `xflags` 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 an argument parsing library: //! //! * if you need all of the features and don't care about minimalism, use //! [clap](https://github.com/clap-rs/clap) //! * if you want to be maximally minimal, need only basic features (eg, no help //! generation), and want to be pedantically correct, use //! [lexopt](https://github.com/blyxxyz/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. //! //! Here's a complete example of `parse_or_exit!` macro which parses arguments //! into an "anonymous" struct: //! //! ```no_run //! use std::path::PathBuf; //! //! fn main() { //! let flags = xflags::parse_or_exit! { //! /// Remove directories and their contents recursively. //! optional -r,--recursive //! /// File or directory to remove //! required path: PathBuf //! }; //! //! println!( //! "removing {}{}", //! flags.path.display(), //! if flags.recursive { "recursively" } else { "" }, //! ) //! } //! ``` //! //! The above program, when run with `--help` argument, generates the following //! help: //! //! ```text //! ARGS: //! //! File or directory to remove //! //! OPTIONS: //! -r, --recursive //! Remove directories and their contents recursively. //! //! -h, --help //! Prints help information. //! ``` //! //! For larger programs, you'd typically want to use `xflags!` macro, which //! generates _named_ structs for you. Unlike a typical macro, `xflags` writes //! generated code into the source file, to make it easy to understand the rust //! types at a glance. //! //! ``` //! mod flags { //! use std::path::PathBuf; //! //! xflags::xflags! { //! src "./examples/basic.rs" //! //! cmd my-command { //! required path: PathBuf //! optional -v, --verbose //! } //! } //! //! // generated start //! // The following code is generated by `xflags` macro. //! // Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. //! #[derive(Debug)] //! pub struct MyCommand { //! pub path: PathBuf, //! pub verbose: bool, //! } //! //! impl MyCommand { //! pub fn from_env_or_exit() -> Self { //! Self::from_env_or_exit_() //! } //! pub fn from_env() -> xflags::Result { //! Self::from_env_() //! } //! pub fn from_vec(args: Vec) -> xflags::Result { //! Self::from_vec_(args) //! } //! } //! // generated end //! } //! //! fn main() { //! let flags = flags::MyCommand::from_env(); //! println!("{:#?}", flags); //! } //! ``` //! //! If you'd rather use a typical proc-macro which generates hidden code, just //! omit the src attribute. //! //! xflags correctly handles non-utf8 arguments. //! //! ## Syntax Reference //! //! The `xflags!` macro uses **cmd** keyword to introduce a command or //! subcommand that accepts positional arguments and switches. //! //! ``` //! xflags::xflags! { //! cmd command-name { } //! } //! ``` //! //! 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::xflags! { //! cmd switches { //! optional -q,--quiet //! required --pass-me //! repeated --verbose //! } //! } //! ``` //! //! 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 std::{path::PathBuf, ffi::OsString}; //! //! xflags::xflags! { //! cmd switches-with-values { //! optional --config path: PathBuf //! repeated --data val: OsString //! optional -j, --jobs n: u32 //! } //! } //! ``` //! //! Arguments without `--` in then are are positional. //! //! ``` //! use std::{path::PathBuf, ffi::OsString}; //! //! xflags::xflags! { //! cmd positional-arguments { //! required program: PathBuf //! repeated args: OsString //! } //! } //! ``` //! //! Nesting **cmd** is allowed. `xflag` automatically generates boilerplate //! enums for subcommands: //! //! ```ignore //! 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 fn from_env_or_exit() -> Self { //! Self::from_env_or_exit_() //! } //! pub fn from_env() -> xflags::Result { //! Self::from_env_() //! } //! pub fn from_vec(args: Vec) -> xflags::Result { //! Self::from_vec_(args) //! } //! } //! // generated end //! ``` //! //! Switches are always "inherited". Both `app -v foo` and `app foo -v` produce //! the same result. //! //! 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::xflags! { //! cmd app { //! repeated -v, --verbose //! default cmd foo { optional -s, --switch } //! cmd bar {} //! } //! } //! ``` //! //! Commands, arguments, and switches can be documented. Doc comments become a //! part of generated help: //! //! ``` //! mod flags { //! use std::path::PathBuf; //! //! xflags::xflags! { //! /// Run basic system diagnostics. //! cmd healthck { //! /// Optional configuration file. //! optional config: PathBuf //! /// Verbosity level, can be repeated multiple times. //! repeated -v, --verbose //! } //! } //! } //! //! fn main() { //! match flags::Healthck::from_env() { //! Ok(flags) => { //! run_checks(flags.config, flags.verbose); //! } //! Err(err) => err.exit() //! } //! } //! //! # fn run_checks(_config: Option, _verbosity: u32) {} //! ``` //! //! 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: //! //! ``` //! mod flags { //! xflags::xflags! { //! cmd my-command { //! repeated -v, --verbose //! optional -q, --quiet //! } //! } //! //! impl MyCommand { //! fn validate(&self) -> xflags::Result<()> { //! if self.quiet && self.verbose > 0 { //! return Err(xflags::Error::new( //! "`-q` and `-v` can't be specified at the same time" //! )); //! } //! Ok(()) //! } //! } //! } //! ``` //! //! The `parse_or_exit!` macro is a syntactic sure for `xflags!`, which //! immediately parses the argument, exiting the process if needed. //! `parse_or_exit` only supports single top-level command and doesn't need the //! `cmd` keyword. //! //! ## Limitations //! //! `xflags` follows //! [Fuchsia](https://fuchsia.dev/fuchsia-src/development/api/cli#command_line_arguments) //! conventions for command line arguments. GNU conventions such as grouping //! short-flags (`-xyz`) or gluing short flag and a value `(-fVAL)` are not //! supported. //! //! `xflags` requires the command line interface to be fully static. It's //! impossible to include additional flags at runtime. //! //! Implementation is not fully robust, there might be some residual bugs in //! edge cases. use std::fmt; /// Generates a parser for command line arguments from a DSL. /// /// See the module-level for detailed syntax specification. pub use xflags_macros::{parse_or_exit, xflags}; pub type Result = std::result::Result; /// An error occurred when parssing command line arguments. /// /// Either the command line was syntactically invalid, or `--help` was /// explicitly requested. #[derive(Debug)] pub struct Error { msg: String, help: bool, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.msg, f) } } impl std::error::Error for Error {} impl Error { /// Creates a new `Error` from a given message. /// /// Use this to report custom validation errors. pub fn new(message: impl Into) -> Error { Error { msg: message.into(), help: false } } /// Error that carries `--help` message. pub fn is_help(&self) -> bool { self.help } /// Prints the error and exists the process. pub fn exit(self) -> ! { if self.is_help() { println!("{self}"); std::process::exit(0) } else { eprintln!("{self}"); std::process::exit(2) } } } /// Private impl details for macros. #[doc(hidden)] pub mod rt;