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, Argument
s
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 ident
s 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
Argument
s. 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 (likeenv::args
), using thematch_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§
- Argument
Iterator - An iterator over a string’s arguments - equivalent to an
iter::once(&str)
if the argument is long or bare, orstr::chars()
for each combined short argument (except returning&str
s for ergonomic reasons).
Enums§
- Argument
- A single argument that can be matched from an
ArgumentIterator
.