Available on crate feature extradocs only.
Expand description

 

↑ Project documentation ↑

Tutorials →

§Introduction and design goals

A quick intro. What, why and how

bpaf is a lightweight and flexible command line parser that uses both combinatoric and derive style API

Combinatoric API usually means a bit more typing but no dependency on proc macros and more help from the IDE, derive API uses proc macro to save on typing but your IDE will be less likely to help you. Picking one API style does not lock you out from using the other style, you can mix and match both in a single parser

§Examples of both styles

Combinatoric example
use bpaf::*;

#[derive(Debug, Clone)]
pub struct Options {
    message: String,
}

pub fn options() -> OptionParser<Options> {
    let message = positional("MESSAGE").help("Message to print in a big friendly letters");
    construct!(Options { message }).to_options()
}

fn main() {
    println!("{:?}", options().run())
}
Derive example
use bpaf::*;

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// Message to print in a big friendly letters
    #[bpaf(positional("MESSAGE"))]
    message: String,
}

fn main() {
    println!("{:?}", options().run())
}
Output

With everything in place users should be able to pass their arguments

$ app "Hello world"
Options { message: "Hello world" }

As well as read the help message generated by the library

$ app --help

Usage: app MESSAGE

Available positional items:
MESSAGE
Message to print in a big friendly letters

Available options:
-h, --help
Prints help information

§Design goals

§Parse, don’t validate

bpaf tries hard to let you move as many invariants about the user input you are trying to parse into rust types: for mutually exclusive options you can get enum with exclusive items going into separate branches, and you can collect results into types like BTreeSet, or whatever custom type you might have with custom parsing. Ideas for making invalid states unrepresentable and using parsing over validation are not new.

That said you can also validate your inputs if this fits your situation better. If you want to ensure that the sum of every numeric field must be divisible by both 3 and 5, but only when it’s Thursday - you can do that too.

§Flexibility

While aiming to be a general-purpose command line parser bpaf offers a few backdoors that allow you to parse pretty much anything you want: chained commands, custom blocks of options, DOS-style options (/ofile.pas), dd style options (if=file of=out), etc. A similar idea applies to what the parser can produce - if your app operates with boxed string slices internally - bpaf will give you Box<str> instead of String if you ask it to.

The only restriction is that you cannot use information from items parsed earlier (but not the fact that something was parsed successfully or not) to decide to how to parse further options, and even then you can side step this restriction by passing some shared state as a parameter to the parsers.

§Reusability

Parsers in bpaf are not monolithic and you can share their parts across multiple binaries, workspace members or even independent projects. Say you have multiple binaries in a workspace that perform different operations on some input. You can declare a parser for the input specifically, along with all the validations, help messages or shell dynamic completion functions you need and use it across all the binaries alongside the arguments specific to those binaries.

§Composition, transformation

Parsers in bpaf are not finalized either, say you have a parser that describes a single input for your program, it can take multiple arguments or perform extra validations, etc. You can always compose this parser with any other parser to produce tuples of both results for example. Or to make it so parser runs multiple times and collects results into a Vec.

§Performance

While performance is an explicit non-goal - bpaf does nothing that would pessimize it either, so performance is on par or better compared to other fully featured parsers.

§Correctness

bpaf would parse only items it can represent and will reject anything it cannot represent in the output. Say your parser accepts both --intel and --att flags, but encodes the result into enum Style { Intel, Att }, bpaf will accept those flags separately, but not if they are used both at once. If the parser later collects multiple styles into a Vec<Style> then it will accept any combinationof those flags.

§User friendly

bpaf tries to provide user-friendly error messages, and suggestions for typos but also scripts for shell completion, man pages and markdown documentation for the web.

 

↑ Project documentation ↑

Tutorials →