Crate arg_kit

Source
Expand description

A featherweight toolkit to help iterate over long and short commandline arguments concisely. It’s a comfy middleground between bloated libraries like clap and painstakingly hacking something together yourself for each new project.

It doesn’t handle taking positional arguments, but you can manually consume later values in order of appearance. Do you really need bloated proc macros when collecting arguments can be simplified to a .next()? You have zero indication of what’s going on under the hood, so you can’t implement your own behaviour.

All the functionality boils down to (&str).as_arg(), which checks if the string begins with - or --, and returns an appropriate iterator for either a single long argument or each grouped short argument.
Putting it in practice, the most basic code looks like this:

let mut argv = std::env::args();
// Skip executable
argv.next();
// For each separate argument
while let Some(args) = argv.next() {
    // For the single long argument or each combined short argument
    for arg in args.as_arg() {
        match arg {
            Argument::Long("help") | Argument::Short("h") => {
                eprintln!("{HELP_TEXT}");
            },
            Argument::Long("value") | Argument::Short("v") => {
                let value: isize = argv.next()?.parse()?;
                do_something(value);
            },
            unknown => {
                eprintln!("Unknown argument {unknown}");
            }
        }
    }
}

Note that in this case it is best to use while let Some(var) = iter.next() over for var in iter. This is because for actually takes ownership of the iterator, so you can’t go to the next argument in the match body to grab a positional value with a simple iter.next().unwrap().


That’s still a lot of boilerplate. To make it a bit narrower, Arguments can be constructed with the arg macro:

assert_eq!(Argument::Long("long"), arg!(--long));
assert_eq!(Argument::Short("s"), arg!(-s));
assert_eq!(Argument::Bare("other"), arg!("other"));

match arg {
    arg!(--help) | arg!(-h) => {
        ...
    },
    arg!(--a-long-argument) => {
        ...
    },
    _ => {},
}

Usually you match both one long and short argument at once, so you can also combine exactly one of each in arg!(--help | -h) or arg!(-h | --help).


There’s still a few layers of indentation that can be cut down on though, since they would rarely change. You can replace the for and match with a single match_arg:

let mut argv = std::env::args();
// Skip executable
argv.next();
while let Some(args) = argv.next() {
    match_arg!(args; {
        arg!(-h | --help) => {
            ...
        },
        _ => {},
    });
}

Or if you don’t need any control between the while and for, you can cut right to the meat of the logic and opt for for_args:

let mut argv = std::env::args();
// Skip executable
argv.next();
for_args!(argv; {
    arg!(-h | --help) => {
        ...
    },
    _ => {},
});

Note that if you need to match (secret?) arguments with spaces, funky characters Rust doesn’t recognize as part of an identifier (excluding dashes which there’s an edge case for), or if you want to match arbitrary long, short, or bare arguments separately, you’ll need to manually construct Argument::Long(var), Argument::Short(var), or Argument::Bare(var).

If you’re wondering why the Short variant carries a &str rather than a char, it’s to make it possible to bind them to the same match variable, and also because macros in arg can’t convert idents into a char.
If you accidentally try matching arg!(-abc), it will silently fail, and there can’t be any checks for it. Blame Rust I guess.

Macros§

arg
Quick way to contruct Arguments. Supports long and short arguments by prepending one or two dashes, or “bare” arguments that aren’t classified as either long or short.
for_args
Matches each part of each argument in a &str iterator (like env::args), using the match_arg macro.
match_arg
Matches each part of a single argument - once if it’s a long argument (eg. --help), or for each character of combined short arguments (eg. -abc).

Structs§

ArgumentIterator
An iterator over a string’s arguments - equivalent to an iter::once(&str) if the argument is long or bare, or str::chars() for each combined short argument (except returning &strs for ergonomic reasons).

Enums§

Argument
A single argument that can be matched from an ArgumentIterator.

Traits§

AsArgumentIterator