type-cli is a convenient, strongly-typed command-line interface parser.
To start, let's create an interface for grep.
use type_cli::CLI;
#[derive(CLI)]
struct Grep(String, String);
fn main() {
    let Grep(pattern, file) = Grep::process();
    let pattern = regex::Regex::new(&pattern).unwrap();
    eprintln!("Searching for `{}` in {}", pattern, file);
}Now, if we run the binary with arguments, they'll be properly parsed. And if we miss an argument, it'll give a helpful error.
$ grep foo* myFile
Searching for `foo*` in myFile
$ grep foo*
Expected an argument at position `2`
However, this isn't exactly a faithful grep interface: in grep, the file is optional. Plus, that unwrap() is a little gross.
use type_cli::CLI;
#[derive(CLI)]
struct Grep(regex::Regex, #[optional] Option<String>);
fn main() {
    match Grep::process() {
        Grep(pattern, Some(file)) => eprintln!("Serching for `{}` in {}", pattern, file),
        Grep(pattern, None) => eprintln!("Searching for `{}` in stdin", pattern),
    }
}What's that? We're accepting a Regex directly as an argument? In type-cli, any type that implements FromStr can be an argument.
Any parsing errors will be gracefully passed back to the user without you having to worry about it.
$ grep foo(
Error parsing positional argument `1`:
regex parse error:
    foo(
       ^
error: unclosed group
Here, you can also see that optional arguments must be annotated with #[optional].
$ grep foo* myFile
Serching for `foo*` in myFile
$ grep foo*
Searching for `foo*` in stdin
This interface still isn't faithful though; grep allows multiple files to be searched.
use type_cli::CLI;
#[derive(CLI)]
struct Grep(regex::Regex, #[variadic] Vec<String>);
fn main(){
    let Grep(pattern, file_list) = Grep::process();
    if file_list.is_empty() {
        eprintln!("Searching for `{}` in stdin", pattern);
    } else {
        eprint!("Searching for `{}` in ", pattern);
        file_list.iter().for_each(|f| eprint!("{}, ", f));
    }
}If you annote the final field with #[variadic], it will parse an arbitrary number of arguments.
This works for any collection that implements FromIterator.
$ grep foo*
Searching for `foo*` in stdin
$grep foo* myFile yourFile ourFile
Searching for `foo*` in myFile, yourFile, ourFile,
This still isn't ideal, though. None of the fields have names, and there's no flags or options! Clearly, tuple structs are limiting us.
use type_cli::CLI;
#[derive(CLI)]
struct Grep {
    pattern: regex::Regex,
    #[named]
    file: String,
    #[flag(short = "i")]
    ignore_case: bool,
}
fn main() {
    let Grep { pattern, file, ignore_case } = Grep::process();
    eprint!("Searching for `{}` in {}", pattern, file);
    if ignore_case {
        eprint!(", ignoring case");
    }
    eprintln!();
}Named arguments are annoted with #[named], and that allows them to be passed to the command in any order.
By default, named arguments are still required, but they can also be marked with #[optional].
$ grep foo*
Expected an argument named `--file`
$ grep foo* --file myFile
Searching for `foo*` in myFile
Flags are annoted with #[flag], and are completely optional boolean or integer flags.
You can optionally specify a shorter form with #[flag(short = "a")] (this form also works for named arguments).
$ grep foo* --file myFile --ignore-case
Searching for `foo*` in myFile, ignoring case
$ grep foo* --file myFile -i
Searching for `foo*` in myFile, ignoring case
This seems well and good, but what if I want multiple commands in my application?
use type_cli::CLI;
#[derive(CLI)]
enum Cargo {
    New(String),
    Build {
        #[named] #[optional]
        target: Option<String>,
        #[flag]
        release: bool,
    },
    Clippy {
        #[flag]
        pedantic: bool,
    }
}
fn main() {
    match Cargo::process() {
        Cargo::New(name) => eprintln!("Creating new crate `{}`", name),
        Cargo::Build { target, release } => {
            let target = target.as_deref().unwrap_or("windows");
            if release {
                eprintln!("Building for {} in release", target);
            } else {
                eprintln!("Building for {}", target);
            }
        }
        Cargo::Clippy { pedantic: true } => eprintln!("Annoyingly checking your code."),
        Cargo::Clippy { pedantic: false } => eprintln!("Checking your code."),
    }
}If you derive CLI on an enum, each variant will represent a subcommand.
Each subcommand is parsed with the same syntax as before.
Rust's pascal case will be automatically converted to the standard for shells:
SubCommand -> sub-command
$ cargo new myCrate
Creating new crate `myCrate`
$ cargo build
Building for windows
$ cargo build --target linux
Building for linux
$ cargo build --target linux --release
Building for linux in release
$ cargo clippy
Checking your code.
$ cargo clippy --pedantic
Annoyingly checking your code.
What about documentation?
use type_cli::CLI;
#[derive(CLI)]
#[help = "Build manager tool for rust"]
enum Cargo {
    New(String),
    #[help = "Build the current crate."]
    Build {
        #[named] #[optional]
        #[help = "the target platform"]
        target: Option<String>,
        #[flag]
        #[help = "build for release mode"]
        release: bool,
    },
    #[help = "Lint your code"]
    Clippy {
        #[flag]
        #[help = "include annoying and subjective lints"]
        pedantic: bool,
    }
}type-cli will automatically generate a help screen for your commands.
If you annote a subcommand or argument with #[help = ""], it will include your short description.
When shown, it will be sent to stderr and the process will exit with a nonzero status.
$ cargo
Help - cargo
Build manager tool for rust
SUBCOMMANDS:
    new
    build       Build the current crate.
    clippy      Lint your code
For enums, this will be shown if the command is called without specifying a subcommand.
$ cargo build --help
Help - build
Build the current crate.
ARGUMENTS:
    --target    the target platform     [optional]
FLAGS:
    --release   build for release mode
$ cargo clippy -h
Help - clippy
Lint your code
FLAGS:
    --pedantic  include annoying and subjective lints
For structs or subcommands, this will be called if the flag --help or -h is passed.
Help messages are not currently supported for tuple structs.