2025-04-01 17:59:03 +02:00
|
|
|
[](https://discord.gg/wQVJbvJ)
|
|
|
|
|
[](https://github.com/uutils/uutils-args/blob/main/LICENSE)
|
|
|
|
|
[](https://deps.rs/repo/github/uutils/uutils-args)
|
|
|
|
|
|
|
|
|
|
[](https://codecov.io/gh/uutils/uutils-args)
|
|
|
|
|

|
|
|
|
|
|
2022-11-27 00:10:46 +01:00
|
|
|
# uutils-args
|
2023-06-04 13:54:02 +02:00
|
|
|
|
2025-04-01 17:59:03 +02:00
|
|
|
Argument parsing for the [uutils](https://www.github.com/uutils/) projects.
|
2023-11-30 11:31:33 +01:00
|
|
|
|
|
|
|
|
It is designed to be flexible, while providing default
|
2025-04-01 17:59:03 +02:00
|
|
|
behaviour that aligns with GNU coreutils and other tools.
|
2023-11-30 11:31:33 +01:00
|
|
|
|
|
|
|
|
## Features
|
|
|
|
|
|
2023-12-14 12:28:24 +01:00
|
|
|
- A derive macro for declarative argument definition.
|
|
|
|
|
- Automatic help generation.
|
|
|
|
|
- Positional and optional arguments.
|
|
|
|
|
- Automatically parsing values into Rust types.
|
|
|
|
|
- Define a custom exit code on errors.
|
|
|
|
|
- Automatically accept unambiguous abbreviations of long options.
|
|
|
|
|
- Handles invalid UTF-8 gracefully.
|
2023-11-30 11:31:33 +01:00
|
|
|
|
|
|
|
|
## When you should not use this library
|
|
|
|
|
|
|
|
|
|
The goal of this library is to make it easy to build applications that
|
|
|
|
|
mimic the behaviour of the GNU coreutils. There are other applications
|
|
|
|
|
that have similar behaviour, which are C application that use `getopt`
|
|
|
|
|
and `getopt_long`. If you want to mimic that behaviour exactly, this
|
|
|
|
|
is the library for you. If you want to write basically anything else,
|
2023-12-05 09:28:04 +01:00
|
|
|
you should probably pick another argument parser (for example: [clap](https://github.com/clap-rs/clap)).
|
2023-11-30 11:31:33 +01:00
|
|
|
|
|
|
|
|
## Getting Started
|
|
|
|
|
|
|
|
|
|
Parsing with this library consists of two "phases". In the first
|
|
|
|
|
phase, the arguments are mapped to an iterator of an `enum`
|
|
|
|
|
implementing [`Arguments`]. The second phase is mapping these
|
|
|
|
|
arguments onto a `struct` implementing [`Options`]. By defining
|
|
|
|
|
your arguments this way, there is a clear divide between the public
|
|
|
|
|
API and the internal representation of the settings of your app.
|
|
|
|
|
|
|
|
|
|
For more information on these traits, see their respective documentation:
|
|
|
|
|
|
|
|
|
|
- [`Arguments`]
|
|
|
|
|
- [`Options`]
|
|
|
|
|
|
|
|
|
|
Below is a minimal example of a full CLI application using this library.
|
|
|
|
|
|
|
|
|
|
```rust
|
2023-12-02 17:40:52 +01:00
|
|
|
use uutils_args::{Arguments, Options};
|
2023-11-30 11:31:33 +01:00
|
|
|
|
|
|
|
|
#[derive(Arguments)]
|
|
|
|
|
enum Arg {
|
|
|
|
|
// The doc strings below will be part of the `--help` text
|
|
|
|
|
// First we define a simple flag:
|
2023-12-02 17:40:52 +01:00
|
|
|
/// Transform input text to uppercase
|
|
|
|
|
#[arg("-c", "--caps")]
|
|
|
|
|
Caps,
|
2023-12-14 12:28:24 +01:00
|
|
|
|
|
|
|
|
// This option takes a value:
|
2023-11-30 11:31:33 +01:00
|
|
|
/// Add exclamation marks to output
|
2023-11-30 12:40:16 +01:00
|
|
|
#[arg("-e N", "--exclaim=N")]
|
2023-11-30 11:31:33 +01:00
|
|
|
ExclamationMarks(u8),
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-02 17:40:52 +01:00
|
|
|
#[derive(Default)]
|
2023-11-30 11:31:33 +01:00
|
|
|
struct Settings {
|
|
|
|
|
caps: bool,
|
|
|
|
|
exclamation_marks: u8,
|
|
|
|
|
text: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// To implement `Options`, we only need to provide the `apply` method.
|
|
|
|
|
// The `parse` method will be automatically generated.
|
|
|
|
|
impl Options<Arg> for Settings {
|
2024-03-29 03:32:30 +01:00
|
|
|
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
|
2023-11-30 11:31:33 +01:00
|
|
|
match arg {
|
2023-12-02 17:40:52 +01:00
|
|
|
Arg::Caps => self.caps = true,
|
2023-11-30 11:31:33 +01:00
|
|
|
Arg::ExclamationMarks(n) => self.exclamation_marks += n,
|
|
|
|
|
}
|
2024-03-29 03:32:30 +01:00
|
|
|
Ok(())
|
2023-11-30 11:31:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-15 23:29:25 +01:00
|
|
|
fn run(args: &[&str]) -> String {
|
2023-12-19 15:13:12 +01:00
|
|
|
let (s, operands) = Settings::default().parse(args).unwrap();
|
2023-12-14 12:28:24 +01:00
|
|
|
let text = operands.iter().map(|s| s.to_string_lossy()).collect::<Vec<_>>().join(" ");
|
2023-11-30 11:31:33 +01:00
|
|
|
let mut output = if s.caps {
|
2023-12-14 12:28:24 +01:00
|
|
|
text.to_uppercase()
|
2023-11-30 11:31:33 +01:00
|
|
|
} else {
|
2023-12-14 12:28:24 +01:00
|
|
|
text
|
2023-11-30 11:31:33 +01:00
|
|
|
};
|
|
|
|
|
for i in 0..s.exclamation_marks {
|
|
|
|
|
output.push('!');
|
|
|
|
|
}
|
|
|
|
|
output
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The first argument is the binary name. In this example it's ignored.
|
2023-12-02 17:40:52 +01:00
|
|
|
assert_eq!(run(&["shout", "hello"]), "hello");
|
|
|
|
|
assert_eq!(run(&["shout", "-e3", "hello"]), "hello!!!");
|
|
|
|
|
assert_eq!(run(&["shout", "-e", "3", "hello"]), "hello!!!");
|
|
|
|
|
assert_eq!(run(&["shout", "--caps", "hello"]), "HELLO");
|
|
|
|
|
assert_eq!(run(&["shout", "-e3", "-c", "hello"]), "HELLO!!!");
|
|
|
|
|
assert_eq!(run(&["shout", "-e3", "-c", "hello", "world"]), "HELLO WORLD!!!");
|
2023-11-30 11:31:33 +01:00
|
|
|
```
|
|
|
|
|
|
2023-12-02 17:40:52 +01:00
|
|
|
## Value parsing
|
2023-11-30 11:31:33 +01:00
|
|
|
|
2023-12-02 17:40:52 +01:00
|
|
|
To make it easier to implement [`Arguments`] and [`Options`], there is the
|
|
|
|
|
[`Value`] trait, which allows for easy parsing from `OsStr` to any type
|
|
|
|
|
implementing [`Value`]. This crate also provides a derive macro for
|
|
|
|
|
this trait.
|
2023-11-30 11:31:33 +01:00
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
The following files contain examples of commands defined with
|
|
|
|
|
`uutils_args`:
|
|
|
|
|
|
2023-12-02 17:10:59 +01:00
|
|
|
- [hello world](https://github.com/uutils/uutils-args/blob/main/examples/hello_world.rs)
|
|
|
|
|
- [arch](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/arch.rs)
|
|
|
|
|
- [b2sum](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/b2sum.rs)
|
|
|
|
|
- [base32](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/base32.rs)
|
|
|
|
|
- [basename](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/basename.rs)
|
|
|
|
|
- [cat](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/cat.rs)
|
|
|
|
|
- [echo](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/echo.rs)
|
|
|
|
|
- [ls](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/ls.rs)
|
|
|
|
|
- [mktemp](https://github.com/uutils/uutils-args/blob/main/tests/coreutils/mktemp.rs)
|