Files

297 lines
8.8 KiB
Markdown
Raw Permalink Normal View History

2023-12-19 14:39:22 +01:00
<style>
.chapters p {
display: grid;
grid-template-columns: repeat(3, 6em);
justify-content: space-between;
}
.chapters a {
text-align: center;
font-family: "Fira Sans",Arial,NanumBarunGothic,sans-serif;
border: 1px solid var(--link-color);
border-radius: 4px;
padding: 3px 10px;
}
.chapters p a[href=""] {
pointer-events: none;
color: var(--scrollbar-thumb-background-color);
border: 1px solid var(--scrollbar-thumb-background-color);
}
</style>
<div class="chapters">
[Previous]()
[Up](super)
[Next](next)
</div>
2023-12-13 18:53:30 +01:00
# Quick Start
A parser consists of two parts:
- an `enum` implementing [`Arguments`](crate::Arguments)
- an `struct` implementing [`Options`](crate::Options)
The `enum` defines all the arguments that your application accepts. The `struct` represents all configuration options for the application. In other words, the `struct` is the internal representation of the options, while the `enum` is the external representation.
## A single flag
We can create arguments by annotating a variant of an `enum` deriving [`Arguments`](crate::Arguments) with the `arg` attribute. This attribute takes strings that define the arguments. A short flag, for instance, looks like `"-f"` and a long flag looks like `"--flag"`. The full syntax for the arguments specifications can be found in the documentation for the [`Arguments` derive macro](derive@crate::Arguments)
To represent the program configuration we create a struct called `Settings`, which implements `Options<Arg>`. When an argument is encountered, we _apply_ it to the `Settings` struct. In this case, we set the `force` field of `Settings` to `true` if `Arg::Force` is parsed.
2023-12-19 12:30:45 +01:00
Any arguments that are not flags are returned as well as part of the tuple returned by `parse`. These do not have special treatment in this library.
2023-12-13 18:53:30 +01:00
```rust
use uutils_args::{Arguments, Options};
2023-12-19 12:30:45 +01:00
use std::ffi::OsString;
2023-12-13 18:53:30 +01:00
#[derive(Arguments)]
enum Arg {
#[arg("-f", "--force")]
Force,
}
2023-12-14 11:39:58 +01:00
#[derive(Default)]
2023-12-13 18:53:30 +01:00
struct Settings {
force: bool
}
impl Options<Arg> for Settings {
2024-03-29 03:32:30 +01:00
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
2023-12-13 18:53:30 +01:00
match arg {
Arg::Force => self.force = true,
}
2024-03-29 03:32:30 +01:00
Ok(())
2023-12-13 18:53:30 +01:00
}
}
2023-12-19 15:13:12 +01:00
let (settings, operands) = Settings::default().parse(["test"]).unwrap();
2023-12-19 12:30:45 +01:00
assert!(!settings.force);
assert_eq!(operands, Vec::<OsString>::new());
2023-12-19 15:13:12 +01:00
let (settings, operands) = Settings::default().parse(["test", "-f"]).unwrap();
2023-12-19 12:30:45 +01:00
assert!(settings.force);
2023-12-19 15:13:12 +01:00
let (settings, operands) = Settings::default().parse(["test", "foo"]).unwrap();
2023-12-19 12:30:45 +01:00
assert!(!settings.force);
assert_eq!(operands, vec![OsString::from("foo")]);
2023-12-13 18:53:30 +01:00
```
## Two overriding flags
Of course, we can define multiple flags. If these arguments change the same fields of `Settings`, then they will override. This is important: by default none of the arguments will "conflict", they will always simply be processed in order.
```rust
use uutils_args::{Arguments, Options};
2023-12-19 12:30:45 +01:00
use std::ffi::OsString;
2023-12-13 18:53:30 +01:00
#[derive(Arguments)]
enum Arg {
#[arg("-f", "--force")]
Force,
#[arg("-F", "--no-force")]
NoForce,
}
2023-12-14 11:39:58 +01:00
#[derive(Default)]
2023-12-13 18:53:30 +01:00
struct Settings {
force: bool
}
impl Options<Arg> for Settings {
2024-03-29 03:32:30 +01:00
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
2023-12-13 18:53:30 +01:00
match arg {
Arg::Force => self.force = true,
Arg::NoForce => self.force = false,
}
2024-03-29 03:32:30 +01:00
Ok(())
2023-12-13 18:53:30 +01:00
}
}
2023-12-19 15:13:12 +01:00
let (settings, operands) = Settings::default().parse(["test"]).unwrap();
2023-12-19 12:30:45 +01:00
assert!(!settings.force);
assert_eq!(operands, Vec::<OsString>::new());
2023-12-19 15:13:12 +01:00
let (settings, operands) = Settings::default().parse(["test", "-f", "some-operand"]).unwrap();
2023-12-19 12:30:45 +01:00
assert!(settings.force);
assert_eq!(operands, vec!["some-operand"]);
2023-12-19 15:13:12 +01:00
let (settings, operands) = Settings::default().parse(["test", "-f", "-F", "some-other-operand"]).unwrap();
assert!(!settings.force);
2023-12-19 12:30:45 +01:00
assert_eq!(operands, vec!["some-other-operand"]);
2023-12-13 18:53:30 +01:00
```
## Help strings
We can document our flags in two ways: by giving them a docstring or by giving the `arg` attribute a `help` argument. Note that the `help` argument will take precedence over the docstring.
```rust
use uutils_args::Arguments;
#[derive(Arguments)]
enum Arg {
/// Force!
#[arg("-f", "--force")]
Force,
#[arg("-F", "--no-force", help = "No! Don't force!")]
NoForce,
}
```
## Arguments with required values
So far, our arguments have been simple flags that do not take any arguments, but `uutils-args` supports much more! If we want an argument for our option, the corresponding variant on our `enum` needs to take an argument too.
> **Note**: In the example below, we use `OsString`. A regular `String` works too, but is generally discouraged in `coreutils`, because we often have to support text with invalid UTF-8.
```rust
# use uutils_args::{Arguments, Options};
# use std::ffi::OsString;
#
#[derive(Arguments)]
enum Arg {
#[arg("-n NAME", "--name=NAME")]
Name(OsString),
}
#
2023-12-14 11:39:58 +01:00
# #[derive(Default)]
2023-12-13 18:53:30 +01:00
# struct Settings {
# name: OsString
# }
#
# impl Options<Arg> for Settings {
2024-03-29 03:32:30 +01:00
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
2023-12-13 18:53:30 +01:00
# match arg {
# Arg::Name(name) => self.name = name,
# }
2024-03-29 03:32:30 +01:00
# Ok(())
2023-12-13 18:53:30 +01:00
# }
# }
#
# assert_eq!(
2023-12-19 15:13:12 +01:00
# Settings::default().parse(["test"]).unwrap().0.name,
2023-12-14 11:39:58 +01:00
# OsString::new(),
2023-12-13 18:53:30 +01:00
# );
# assert_eq!(
2023-12-19 15:13:12 +01:00
# Settings::default().parse(["test", "--name=John"]).unwrap().0.name,
2023-12-14 11:39:58 +01:00
# OsString::from("John"),
2023-12-13 18:53:30 +01:00
# );
```
## Arguments with optional values
Arguments with optional values are possible, too. However, we have to give a value to be used if the value is not given. Below, we set that value to `OsString::from("anonymous")`, with the `value` argument of `arg`.
```rust
# use uutils_args::{Arguments, Options};
# use std::ffi::OsString;
#
#[derive(Arguments)]
enum Arg {
#[arg("-n[NAME]", "--name[=NAME]", value = OsString::from("anonymous"))]
Name(OsString),
}
#
# #[derive(Default, Debug, PartialEq, Eq)]
# struct Settings {
# name: OsString
# }
#
# impl Options<Arg> for Settings {
2024-03-29 03:32:30 +01:00
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
2023-12-13 18:53:30 +01:00
# match arg {
# Arg::Name(name) => self.name = name,
# }
2024-03-29 03:32:30 +01:00
# Ok(())
2023-12-13 18:53:30 +01:00
# }
# }
#
# assert_eq!(
2023-12-19 15:13:12 +01:00
# Settings::default().parse(["test", "--name"]).unwrap().0.name,
2023-12-14 11:39:58 +01:00
# OsString::from("anonymous"),
2023-12-13 18:53:30 +01:00
# );
# assert_eq!(
2023-12-19 15:13:12 +01:00
# Settings::default().parse(["test", "--name=John"]).unwrap().0.name,
2023-12-14 11:39:58 +01:00
# OsString::from("John"),
2023-12-13 18:53:30 +01:00
# );
```
## Multiple arguments per variant
Here's a neat trick: you can use multiple `arg` attributes per variant. Recall the `--force/--no-force` example above. We could have written that as follows:
```rust
# use uutils_args::{Arguments, Options};
#
#[derive(Arguments)]
enum Arg {
#[arg("-f", "--force", value = true, help = "enable force")]
#[arg("-F", "--no-force", value = false, help = "disable force")]
Force(bool),
}
#
2023-12-14 11:39:58 +01:00
# #[derive(Default)]
2023-12-13 18:53:30 +01:00
# struct Settings {
# force: bool
# }
#
# impl Options<Arg> for Settings {
2024-03-29 03:32:30 +01:00
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
2023-12-13 18:53:30 +01:00
# match arg {
# Arg::Force(b) => self.force = b,
# }
2024-03-29 03:32:30 +01:00
# Ok(())
2023-12-13 18:53:30 +01:00
# }
# }
#
2023-12-19 15:13:12 +01:00
# assert!(!Settings::default().parse(["test"]).unwrap().0.force);
# assert!(Settings::default().parse(["test", "-f"]).unwrap().0.force);
# assert!(!Settings::default().parse(["test", "-F"]).unwrap().0.force);
2023-12-13 18:53:30 +01:00
```
This is particularly interesting for defining "shortcut" arguments. For example, `ls` takes a `--sort=WORD` argument, that defines how the files should be sorted. But it also has shorthands like `-t`, which is the same as `--sort=time`. All of these can be implemented on one variant:
> **Note**: The `--sort` argument should not take a `String` as value. We've done that here for illustrative purposes. It should actually use an `enum` with the `Value` trait.
```rust
# use uutils_args::{Arguments, Options};
#
#[derive(Arguments)]
enum Arg {
#[arg("--sort=WORD", help = "Sort by WORD")]
#[arg("-t", value = String::from("time"), help = "Sort by time")]
#[arg("-U", value = String::from("none"), help = "Do not sort")]
#[arg("-v", value = String::from("version"), help = "Sort by version")]
#[arg("-X", value = String::from("extension"), help = "Sort by extension")]
Sort(String),
}
#
2023-12-14 11:39:58 +01:00
# #[derive(Default)]
2023-12-13 18:53:30 +01:00
# struct Settings {
# sort: String
# }
#
# impl Options<Arg> for Settings {
2024-03-29 03:32:30 +01:00
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
2023-12-13 18:53:30 +01:00
# match arg {
# Arg::Sort(s) => self.sort = s,
# }
2024-03-29 03:32:30 +01:00
# Ok(())
2023-12-13 18:53:30 +01:00
# }
# }
#
2023-12-19 15:13:12 +01:00
# assert_eq!(Settings::default().parse(["test"]).unwrap().0.sort, String::new());
# assert_eq!(Settings::default().parse(["test", "--sort=time"]).unwrap().0.sort, String::from("time"));
# assert_eq!(Settings::default().parse(["test", "-t"]).unwrap().0.sort, String::from("time"));
2023-12-13 18:53:30 +01:00
```
2023-12-19 14:39:22 +01:00
<div class="chapters">
[Previous]()
[Up](super)
[Next](next)
2024-03-29 03:32:30 +01:00
</div>