Skip to content
/ palc Public

Prototype of a command line argument parser with several opposite design goals from clap.

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

oxalica/palc

Repository files navigation

palc

crates.io docs.rs

Prototype of a command line argument parser with several opposite design goals from clap.

⚠️ This project is in alpha stage and is not ready for production yet. The API is subject to change. Feedbacks are welcome.

Check ./test-suite/src/bin/*-palc.rs for example usages.

Similar: Compatible1 Derive API

palc is an un-opinionated2 derive-based argument parser. We choose to align with the clap 4.0 derive API: Parser, Args and Subcommand macros with almost compatible command(..) and arg(..) attributes.

In most cases, switching between clap derive and palc derive is as easy as changing a line in Cargo.toml and relevant use statements. No vendor locking. Writing your CLI structs first before deciding which crate to use.

Similar: Full-featured, out of the box

palc also aim to provide a decent CLI experience under default features: help generations, non-UTF-8 support, argument constraints, Args composition, subcommands, you name it.

Though some of clap features are not-yet-implemented.

Yet Implemented features
  • Argument behaviors:

    • Boolean flags --verbose.

    • Named arguments --long value, -svalue

      • Bundled short arguments -czf
      • '='-separator --long=v -f=v.
      • Aliases.
      • Reject hyphen values.
      • Allow hyphen values.
      • Space-delimited multi-values.
      • Custom-delimited multi-values.
      • Multi-values with value-terminator.
    • Unnamed/free/positional arguments FILE.

      • Force no named arguments --.
      • Greedy/tail arguments (arg(trailing_var_arg)).
      • Last arguments after -- (arg(last)).
      • Allow hyphen values.
    • Counting number of occurrence (ArgAction::Count).

    • Custom ArgAction.

    • Custom number of values (arg(num_args)).

    • Overrides.

    • List of magic argument types with automatic default behaviors:

      • T where T: TryFrom<&OsStr> || TryFrom<&str> || FromStr (named & unnamed)
      • bool (named)
      • Option<T> (named)
      • Option<Option<T>> (named) FIXME: The semantic disagrees with clap yet.
      • Vec<T> (named & unnamed)
      • Option<Vec<T>> (named & unnamed)
      • Vec<Vec<T>>
      • Option<Vec<Vec<T>>>
    • Default values. (arg(default_value_t))

      • Default pre-parsed string value. (arg(default_value))
        • Note: The provided string value will be parsed at runtime if the argument is missing. This will cause codegen degradation due to panic handling, and typos cannot be caught statically. Always use arg(default_value_t) if possible.
      • Default missing values.
      • Default from env.
  • Argument value parsing:

    • derive(ValueEnum)
      • value(rename_all)
      • value(name)
      • value(skip)
      • value(help)
    • Non-UTF-8 inputs PathBuf, OsString.
    • Automatically picked custom parser via From<OsString>, From<String> or FromStr.
    • arg(ignore_case)
      • Note: Only ValueEnum that has no UPPERCASE variants are supported yet, due to implementation limitation.
  • Argument validations:

    • Reject duplicated arguments.
    • Required.
      • Conditional required.
    • Conflicts.
    • Exclusive.
    • Args groups (one and only one argument).
  • Composition:

    • arg(flatten).
      • Note that non-flatten arguments always take precedence over flatten arguments.
      • Flatten named arguments.
      • Flatten unnamed arguments.
    • Subcommands.
      • Argv0 as subcommand (multi-call binary).
      • Prefer parsing subcommand over unnamed arguments.
      • Global args.
        • Note: Current implementation has limitations on the number of values it takes. And it only propagates up if the inner Args cannot accept the named arguments -- that is -- only one innermost Args on the ancestor chain will receive it, not all.
  • Help generation. Note: Help text is only for human consumption. The precise format is unstable, may change at any time and is not expected to exactly follow clap's help format (although that is our general direction).

    • Long help --help.
    • Short help -h.
    • Version --version.
    • Custom header and footer.
    • Hiding.
    • Possible values of enums.
    • Default values via arg(default_value{,_t}).
    • Custom help subcommand or flags.
  • Helpful error messages.

    • Error argument and reason.
    • Expected format.
    • Error suggestions ("did you mean").
    • Custom help template.
  • Term features:

    • Colored output.
    • Wrap on terminal width.
      • We do not plan to implement this for now because its drawback outweighs its benefits. Word splitting and text rendering length with Unicode support is be very tricky and costly. It also hurts output reproducibility.
  • Reflection.

  • Completions.

Different: Only via derive macros, statically

The only way to define a CLI parser in palc is via derive-macros. It is not possible to manually write impl or even construct it dynamically. Argument parsers are prepared, validated and generated during compile time. The runtime does nothing other than parsing, thus has no startup overhead. Also no insta-panics at runtime!

On the contrary, clap only works on builder API under the hood and its derive API translates attributes to its builder API. The parser is still composed, verified, and then executed at runtime. This suffers from startup time penalty.

This implies we do more work in proc-macro while rustc does less work on generated code. In compilation time benchmarks, we outperform clap-derive in both full build and incremental build.

Different: Binary size aware

Despite how many features we have, we keep binary overhead in check. Our goal is to give a size overhead that deserves its features, without unreasonable or meaningless bloat.

Unlike other min-size-centric projects, eg. pico-args or gumdrop, we choose NOT to sacrifice CLI user experience, or force CLI designers to write more (repetitive) code. We are striving for a good balance between features and their cost.

In the benchmarks (./bench.txt), binary size of small-to-medium CLI structs using palc is comparable to and sometimes smaller than argh.


Credit

The derive interface is inspired and mimicking clap's derive interface.

The palc runtime design is inspired by miniserde.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Footnotes

  1. Due to design differences, some attributes cannot be implemented statically or require a different syntax. TODO: Document all attributes and notable differences with clap.

  2. argh say they are "opinionated" as an excuse of subjective and "creative" choice on derive attribute names and letter case restrictions. We are against these.

About

Prototype of a command line argument parser with several opposite design goals from clap.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Sponsor this project