| //! `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: |
| //! <path> |
| //! 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> { |
| //! Self::from_env_() |
| //! } |
| //! pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> { |
| //! 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 |
| //! } |
| //! } |
| //! ``` |
| //! |
| //! You can create aliases if desired, which is as simple as adding extra names to the `cmd` definition. |
| //! In this case, `run` can be called as `run`, `r` and `exec`: |
| //! |
| //! ```rust |
| //! xflags::xflags! { |
| //! cmd run r exec {} |
| //! } |
| //! ``` |
| //! |
| //! 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> { |
| //! 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". 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<std::path::PathBuf>, _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<T, E = Error> = std::result::Result<T, E>; |
| |
| /// 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<String>) -> 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; |