Git Product home page Git Product logo

pico-args's Introduction

pico-args

Build Status Crates.io Documentation Rust 1.32+

An ultra simple CLI arguments parser.

If you think that this library doesn't support some feature, it's probably intentional.

  • No help generation
  • Only flags, options, free arguments and subcommands are supported
  • Options can be separated by a space, = or nothing. See build features
  • Arguments can be in any order
  • Non UTF-8 arguments are supported

Build features

  • eq-separator

    Allows parsing arguments separated by =
    This feature adds about 1KiB to the resulting binary

  • short-space-opt

    Makes the space between short keys and their values optional (e.g. -w10)
    If eq-separator is enabled, then it takes precedence and the '=' is not included.
    If eq-separator is disabled, then -K=value gives an error instead of returning "=value".
    The optional space is only applicable for short keys because --keyvalue would be ambiguous

  • combined-flags

    Allows combination of flags, e.g. -abc instead of -a -b -c
    If short-space-opt or eq-separator are enabled, you must parse flags after values, to prevent ambiguities

Limitations

The main fundamental limitation of pico-args is that it parses arguments in an arbitrary order. This is because we have a sort of "streaming" API and we don't know all the keys/arguments beforehand. This could lead to some unexpected behaviors. Specifically, let's say you have a following arguments:

--arg1 --arg2 value

If your parser tries to parse --arg1 as key-value first, than its value would be --arg2 and not value, because the parser simply takes the "next" argument. A properer parser would knew that --arg2 is a key and will return an error, since the value is missing.

If your parser tries to parse --arg2 as a flag first and then --arg1 as key-value, then its value would be value, because --arg2 was already removed by the parser and the arguments list looks like --arg1 value to the parser.

If such behavior is unacceptable to your application, then you have to use a more high-level arguments parsing library.

Alternatives

The core idea of pico-args is to provide some "sugar" for arguments parsing without a lot of overhead (binary or compilation time wise). There are no point in comparing parsing features since pico-args supports only the bare minimum. Here is a great comparison of various arguments parsing libraries.

License

MIT

pico-args's People

Contributors

alexwennerberg avatar flyingcakes85 avatar glts avatar hdamron17 avatar jneem avatar jyn514 avatar kjeremy avatar lensvol avatar liigo avatar matklad avatar razrfalcon avatar riquito avatar sigmasd avatar slyshyko avatar wezm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pico-args's Issues

Multiple value arguments

Hey, thanks for the crate. Is it possible to have a multi value argument in pico args? ex.

./some.bin -f one two three

I've tried using args.values_from_str(["-f", "--flag"]).unwrap_or(vec![]), with a corresponding struct having a value for flag set to Vec<String> but this ends up giving me a vec with just the string one. I've also looked around the docs and it's unclear to me if this is possible or not.

Custom Error per subcommand

Is it possible to have a custom error display whenever a some subcommand is entered incorrectly? Also is it possible to know what arguments have been correctly supplied?

With clap when someone enters the incorrect arguments it outputs

error: The following required arguments were not provided:
    --file <file>
    --secret <secret>

USAGE:
    program create <folder> --file <file> --secret <secret>

For more information try --help

If file was provided then --secret <secret> is red.
If secret was provided then --file <file> is red.
If neither were provided both were red.

I can handle colors in the terminal. Is it possible to get the necessary information with pico args.

Add support for -- arguments

I want to have arguments like this:

    cargo deadlinks [--dir <directory>] [options] [-- <CARGO_ARGS>]

Right now I have to call free() and manually parse the arguments. It would be nice to have built-in support for this. I'm imagining Arguments::dash_dash() -> (Vec<String>, Option<Vec<String>>) (or maybe just (Vec<String>, Vec<String>) for simplicity). It would behave like free() except that it parses out the -- for you.

Compile clap with no default features

The comparison is not exactly fair because clap has few optional features enabled by default:

  • vec_map sacrifices some compile time (one more dependency) and binary bloat for runtime speedup
  • suggestions enables "did you mean foo?" messages, one more dependency + bloat
  • color for colorizing help messages, few more dependencies + bloat

Could you please compile clap with default-features = false? Although I don't expect incredible improvements, I'm curious how much impact these have on clap.

P.S. The same story with structopt

Measure incremental build time

The readme says that build time is measured after cargo clean. It's much more common to want fast incremental build times to iterate faster (especially in gamedev but most projects would benefit from optimizing incremental builds).

The easiest way to measure this is to append a newline or comment into main.rs before building (though i get the feeling that editing a function might produce more meaningful results but i am not sure how granular rustc's and LLVM's caching is).

Projects that take incremental builds seriously are also probably already using several improvements such as a nightly rustc, -Zshare-generics=y and either lld or mold linter - an example config is here. This is pretty significant and can reduce incremental builds for example from 12 s to 2 s. It might be interesting to do the comparison using this config for a more convincing argument why pico-args is the best choice.

Confusing Display output for *ArgumentParsingFailed errors

When Error::Utf8ArgumentParsingFailed and Error::ArgumentParsingFailed are printed via Display the result looks something like this:

failed to parse '5.4' cause invalid IP address
failed to parse a binary argument cause invalid IP address

This wording seems a bit confusing. I’d like to suggest using a colon to separate the two clauses:

failed to parse '5.4': invalid IP address
failed to parse a binary argument: invalid IP address

Combined flags question

Hey I had a question about combined flags -- is this something that you don't want to support because of the minimalist principles of this library, or just something that you haven't implemented, but would be open to?

Ignore unused arguments

Currently, free_* and finish methods will return an error if one of the arguments starts with a -. But turns out that this is completely incorrect.

  1. - is not an argument, it's a "read from stdin" operator (we actually handle this one)
  2. -- is not an argument, it's a dash-dash operator (or whatever it's called). Example: cargo run -- --my-arg
  3. -1 is not an argument, it's a negative number
  4. -.txt is not argument, it's a valid file name

Since I have no idea how to handle this, I've decided to remove it completely. So it's the caller problem now.

@matklad Hi, afaik you're using this library in ra and I don't want to suddenly break anything (it would be in 0.4 anyway). Does this change affects your use case or you fine with it? It will basically remove the unused arguments left: ... errors.

Repeated args

I want to support repeated -v arguments, where more copies of that argument implies more verbose output. This code works:

while args.contains("-v") {
    verbosity += 1;
}

But I was concerned by the part of the docs where it says that contains may only be called once for each flag. Is this behavior intended, and if so would you accept a docs PR to clarify it?

Implement Iterator for Arguments

Hi,
What do you think about

impl Iterator for Arguments {
    type Item = OsString;
    fn next(&mut self) -> Option<Self::Item> {
        if !self.0.is_empty() {
            Some(self.0.remove(0))
        } else {
            None
        }
    }
}

Here is a usage example:
cmd name size -m size --zero

struct Args {
    file_name: OsString,
    file_size: OsString,
    max_ram_usage: Option<OsString>,
    zero: bool,
}

fn main() -> Result<()> {
    let mut pico_args = pico_args::Arguments::from_env();
    let args = Args {
        file_name: pico_args.next().ok_or("no file name specified")?,
        file_size: pico_args.next().ok_or("no file size specified")?,
        max_ram_usage: pico_args.opt_value_from_str(["-m", "--max-ram-usage"])?,
        zero: pico_args.contains("--zero"),
    };
    pico_args.finish()?;

option aliases or additional short or long forms?

Hi @RazrFalcon ,

pico_args works fine with 2 options (short and long or two long options), but can't work with additional options / aliases?

Typical is one short and one long form.

force: args.contains(["-f", "--force"]),

Two long forms also compile wihout error.

force: args.contains(["--force", "--sudo"]),

But how to add additional "aliases" for options or subcommands like

shutdown: args.contains(["--shutdown", "--poweroff", "-s", "-p"]),
  1. Is it possible to add more than 2 options?
  2. I'm new with rust so maybe a stupid question... Is it possible to generate an subcommand alias? For example poweroff should be the same than shutdown?
       Some("poweroff") => {                                      // additional subcommand alias should be "shutdown"...
           Ok(AppArgs::SubcPoweroff {                             
               force: args.contains(["-f", "--force"]),
           })                                                   
       } 

define order in which repeated options are returned

Currently the code supports repeated options so

./program --input first-input --input second-input

can work just fine, but it returns the second one before the first one. I'm okay with this behavior (I can reverse them), but would prefer for this to be documented, so I wouldn't find my code breaking due to an implementation change.

non-'static keys

I'd like to be able to have keys that are not &'static, since I'll have to compute some keys at runtime in order to avoid a theoretical combinatoric explosion. I could do this by simply leaking memory (and it's not much), but is it really so expensive to use to_string() in the error cases? Or is there some other reason why you require keys to be &'static str?

`free_value_from_fn`

I'd love to see a method added like

/// Parses a value without a key.
///
/// Uses a specified function for value conversion.
/// Must be used only once for each option.
/// Will return an error, if any flags are left, or if no arguments are left.
pub fn free_value_from_fn<T>(
    &mut self, 
    f: fn(_: &str) -> Result<T, String>
) -> Result<Option<T>, Error> 

This would really be a convenience around free, but it would allow me to structure my code more nicely.

BTW, I'm thinking of using pico-args as part of the basis for a replacement for ClapMe. ClapMe (obviously) uses clap, but there isn't really a nice way to specify interdependent sets of arguments with clap. I was going to write my own argument parser from scratch, but gave what's out there one last look and your project looks just about right. I'll have to generate help myself anyhow, and you basically save me the tedium of writing the actual parsing code, letting me get to the bits that will slow down the compile! :)

Amusingly, waiting for clap to compile is part of what made me want to stop using it. I love that pico-args doesn't do anything that I would need to reimplement! My current gripe is just that I want parsing each of the positional arguments to be identical to other parses.

BTW, another design option would be to implement From<()> for Keys, which would indicate a positional argument. That would be even nicer for me, but only by a teeny tiny bit, and I can see why you might not like that as an API design, since it would mean that the order of calling the value_from... functions would impact the semantics, which is a bit ugly.

Consider all arguments after "--" free arguments

I recently rewrote the argument parsing for cargo-cupply-chain and one thing I had to work around was the forwarding of arguments. In this case everything after "--" isn't matched by the program itself but forwarded to another program, which worked fine for e.g. "-- foo bar" but it rejected "-- foo --bar" because it was an unused argument.

Ideally everything after " -- " should be considered a free argument.

Example:
"/bin/foo --bar --moo=5 -- --lurg" would match bar and moo, but lurg would be among the free arguments.

`cargo test --release` fails

While cargo test passes for me, cargo test --release fails a few tests.

failures:

---- invalid_flag_02 stdout ----
note: test did not panic as expected
---- invalid_flag_03 stdout ----
note: test did not panic as expected
---- invalid_flag_04 stdout ----
note: test did not panic as expected

failures:
    invalid_flag_02
    invalid_flag_03
    invalid_flag_04

I think this is because the From impls use debug_assert, which does nothing in release builds. Oddly, invalid_flag_01 still passed, but using cargo test -- --nocapture I can see that it's hitting different asserts in debug/release builds, so it still satisfies #[should_panic].

Note: we test release builds in Fedora so our full rpm build+check all uses the same thing.

1.0.0

Have you considered releasing 1.0.0? If not, is there anything you have in mind that you'd like to add/change first?

pico-args is a really nice crate and seems to have been stable for a long time with very few issues raised against it.

Forbid flags between options.

This is a valid code now:

    let mut args = Arguments::from_vec(to_vec(&["-w", "-h", "20"]));
    args.contains("-h");
    let value: Result<u32, Error> = args.value_from_str("-w");
    assert_eq!(value.unwrap(), 20);

but it should be an error, I guess.

Optional space between key and value

Hi, we are using this library for the Saltwater C Compiler and I wanted to ask if it would be possible to make the space between key and value optional for short form arguments (e.g. -I/usr/local/include). These are commonly used and it would increase compatibility with other compilers. I am willing to implement this as an optional feature like eq-separator; I just want to check if there is a reason it has not been implemented yet.

Are the `debug_asserts` important?

I'm using pico-args in a tiny binary where I am space-concious, so thank you for creating it ❤️

Is there a particular reason you decided to use debug_asserts to check that flags start with - or --?
My CLI interface looks like fern fmt here and it works in release but crashes when I write tests against it.

Alternatively I could add support for having multiple subcommands?

I'd be happy to open a PR for it.

Example with subcommands that take arguements

I am looking to replace clap with pico-args. I am not sure how to make a subcommand that takes two required arguments and one optional one. Is this possible with pico-args right now and if so how would I go about that?

docs how to test parse_args()

when look at example using parse_args() how to test? how to test and say unwrap_or() work or -h exit right? i try someth in mod test but no luck

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.