Skip to content
/ parkour Public

A fast, extensible, command-line arguments parser

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

Aloso/parkour

parkour

A fast, extensible, command-line arguments parser.

Documentation Crates.io Downloads License-MIT License-Apache Checks

Introduction 📚

The most popular argument parser, clap, allows you list all the possible arguments and their constraints, and then gives you a dynamic, stringly-typed object containing all the values. Usually these values are then manually extracted into structs and enums to access the values more conveniently and get the advantages of a static type system (example).

Parkour uses a different approach: Instead of parsing the arguments into an intermediate, stringly-typed object, it parses them directly into the types you want, so there's no cumbersome conversion. For types outside the standard library, you need to implement a trait, but in most cases this can be done with a simple derive macro.

This has several advantages:

  • It is very flexible: Every aspect of argument parsing can be tailored to your needs.
  • It is strongly typed: Many errors can be caught at compile time, so you waste less time debugging.
  • It is zero-cost: If you don't need a feature, you don't have to use it. Parkour should also be pretty fast, but don't take my word for it, benchmark it 😉

Status

Parkour started as an experiment and is very new (about 1 week old at the time of writing). Expect frequent breaking changes. If you like what you see, consider supporting this work by

  • Reading the docs
  • Trying it out
  • Giving feedback in this issue
  • Opening issues or sending PRs

Right now, parkour lacks some important features, which I intend to implement:

  • Auto-generated help messages
  • A DSL to write (sub)commands more ergonomically
  • More powerful derive macros
  • Error messages with ANSI colors

Example

use parkour::prelude::*;

#[derive(FromInputValue)]
enum ColorMode {
    Always,
    Auto,
    Never,
}

struct Command {
    color_mode: ColorMode,
    file: String,
}

impl FromInput<'static> for Command {
    type Context = ();

    fn from_input(input: &mut ArgsInput, _: &Self::Context)
        -> Result<Self, parkour::Error> {
        // discard the first argument
        input.bump_argument().unwrap();

        let mut file = None;
        let mut color_mode = None;

        while !input.is_empty() {
            if input.parse_long_flag("help") || input.parse_short_flag("h") {
                println!("Usage: run [-h,--help] [--color,-c auto|always|never] FILE");
                return Err(parkour::Error::early_exit());
            }
            if SetOnce(&mut color_mode)
                .apply(input, &Flag::LongShort("color", "c").into())? {
                continue;
            }
            if SetPositional(&mut file).apply(input, &"FILE")? {
                continue;
            }
            input.expect_empty()?;
        }

        Ok(Command {
            color_mode: color_mode.unwrap_or(ColorMode::Auto),
            file: file.ok_or_else(|| parkour::Error::missing_argument("FILE"))?,
        })
    }
}

In the future, I'd like to support a syntax like this:

use parkour::prelude::*;

#[derive(FromInput)]
#[parkour(main, help)]
#[arg(long = "version", short = "V", function = print_help)]
struct Command {
    #[parkour(default = ColorMode::Auto)]
    #[arg(long = "color", short = "c")]
    color_mode: ColorMode,

    #[arg(positional)]
    subcommand: Option<Subcommand>,
}

#[derive(FromInputValue)]
enum ColorMode {
    Always,
    Auto,
    Never,
}

#[derive(FromInput)]
enum Subcommand {
    Foo(Foo),
    Bar(Bar),
}

#[derive(FromInput)]
#[parkour(subcommand)]
struct Foo {
    #[parkour(default = 0, max = 3)]
    #[arg(long, short, action = Inc)]
    verbose: u8,

    #[arg(positional)]
    values: Vec<String>,
}

#[derive(FromInput)]
#[parkour(subcommand)]
struct Bar {
    #[parkour(default = false)]
    #[arg(long, short)]
    dry_run: bool,
}

fn print_version(_: &mut ArgsInput) -> parkour::Result<()> {
    println!("command {}", env!("CARGO_PKG_VERSION"));
    Err(parkour::Error::early_exit())
}

Code of Conduct 🤝

Please be friendly and respectful to others. This should be a place where everyone can feel safe, therefore I intend to enforce the Rust code of conduct.

License

This project is licensed under either of

at your option.

About

A fast, extensible, command-line arguments parser

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages