[Previous]() [Up](super) [Next](next)
# 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`. 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. 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. ```rust use uutils_args::{Arguments, Options}; use std::ffi::OsString; #[derive(Arguments)] enum Arg { #[arg("-f", "--force")] Force, } #[derive(Default)] struct Settings { force: bool } impl Options for Settings { fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Force => self.force = true, } Ok(()) } } let (settings, operands) = Settings::default().parse(["test"]).unwrap(); assert!(!settings.force); assert_eq!(operands, Vec::::new()); let (settings, operands) = Settings::default().parse(["test", "-f"]).unwrap(); assert!(settings.force); let (settings, operands) = Settings::default().parse(["test", "foo"]).unwrap(); assert!(!settings.force); assert_eq!(operands, vec![OsString::from("foo")]); ``` ## 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}; use std::ffi::OsString; #[derive(Arguments)] enum Arg { #[arg("-f", "--force")] Force, #[arg("-F", "--no-force")] NoForce, } #[derive(Default)] struct Settings { force: bool } impl Options for Settings { fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Force => self.force = true, Arg::NoForce => self.force = false, } Ok(()) } } let (settings, operands) = Settings::default().parse(["test"]).unwrap(); assert!(!settings.force); assert_eq!(operands, Vec::::new()); let (settings, operands) = Settings::default().parse(["test", "-f", "some-operand"]).unwrap(); assert!(settings.force); assert_eq!(operands, vec!["some-operand"]); let (settings, operands) = Settings::default().parse(["test", "-f", "-F", "some-other-operand"]).unwrap(); assert!(!settings.force); assert_eq!(operands, vec!["some-other-operand"]); ``` ## 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), } # # #[derive(Default)] # struct Settings { # name: OsString # } # # impl Options for Settings { # fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { # match arg { # Arg::Name(name) => self.name = name, # } # Ok(()) # } # } # # assert_eq!( # Settings::default().parse(["test"]).unwrap().0.name, # OsString::new(), # ); # assert_eq!( # Settings::default().parse(["test", "--name=John"]).unwrap().0.name, # OsString::from("John"), # ); ``` ## 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 for Settings { # fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { # match arg { # Arg::Name(name) => self.name = name, # } # Ok(()) # } # } # # assert_eq!( # Settings::default().parse(["test", "--name"]).unwrap().0.name, # OsString::from("anonymous"), # ); # assert_eq!( # Settings::default().parse(["test", "--name=John"]).unwrap().0.name, # OsString::from("John"), # ); ``` ## 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), } # # #[derive(Default)] # struct Settings { # force: bool # } # # impl Options for Settings { # fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { # match arg { # Arg::Force(b) => self.force = b, # } # Ok(()) # } # } # # 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); ``` 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), } # # #[derive(Default)] # struct Settings { # sort: String # } # # impl Options for Settings { # fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { # match arg { # Arg::Sort(s) => self.sort = s, # } # Ok(()) # } # } # # 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")); ```
[Previous]() [Up](super) [Next](next)