Merge pull request #113 from BenWiederhake/dev-fallible-apply

Make Options::apply fallible
This commit is contained in:
Daniel Hofstetter
2025-04-01 10:50:24 +02:00
committed by GitHub
27 changed files with 848 additions and 73 deletions
+2 -1
View File
@@ -67,11 +67,12 @@ struct Settings {
// To implement `Options`, we only need to provide the `apply` method.
// The `parse` method will be automatically generated.
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Caps => self.caps = true,
Arg::ExclamationMarks(n) => self.exclamation_marks += n,
}
Ok(())
}
}
+13 -3
View File
@@ -108,7 +108,7 @@ uutils and when we opened as issue for it, it was discarded. This makes sense
from `clap`'s perspective, but it shows that the priorities between `clap` and
uutils diverge.
## Problem 6: It's stringly typed
## Problem 7: It's stringly typed
`clap`'s arguments are identified by strings. This leads to code like this:
@@ -135,14 +135,14 @@ deal, but a bit annoying.
Of course, we wouldn't have this problem if we were able to use the derive API.
## Problem 7: Reading help string from a file
## Problem 8: Reading help string from a file
In `uutils` our help strings can get quite long. Therefore, we like to extract
those to an external file. With `clap` this means that we need to do some custom
preprocessing on this file to extract the information for the several pieces of
the help string that `clap` supports.
## Problem 8: No markdown support
## Problem 9: No markdown support
Granted, this is not really a problem, but more of a nice-to-have. We have
online documentation for the utils, based on the help strings and these are
@@ -150,6 +150,16 @@ rendered from markdown. Ideally, our argument parser supports markdown too, so
that we can have nicely rendered help strings which have (roughly) the same
appearance in the terminal and online.
## Problem 10: No position-dependent argument-error prioritization
This is the question of which error to print if both `-A` and `-B` are given,
and both are individually an error somehow. In case of the GNU tools, only the
first error is printed, and then the program is aborted.
This also is not really a problem, but since it can be reasonably easily
achieved by simply raising an error during argument application, this enables
matching more closely the exact behavior of the GNU tools.
## Good things about `clap`
Alright, enough problems. Let's praise `clap` a bit, because it's an excellent
+11 -6
View File
@@ -94,10 +94,11 @@ enum Arg {
struct Settings { a: bool }
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::A => self.a = true,
}
Ok(())
}
}
@@ -137,10 +138,11 @@ impl Default for Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::A => self.a = false,
}
Ok(())
}
}
@@ -175,10 +177,11 @@ enum Arg {
struct Settings { a: u8 }
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::A => self.a += 1,
}
Ok(())
}
}
@@ -215,10 +218,11 @@ enum Arg {
struct Settings { a: OsString }
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::A(s) => self.a = s,
}
Ok(())
}
}
@@ -255,10 +259,11 @@ enum Arg {
struct Settings { a: Vec<OsString> }
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::A(s) => self.a.push(s),
}
Ok(())
}
}
@@ -271,4 +276,4 @@ let a = Settings::default().parse(std::env::args_os()).unwrap().0.a;
[Up](super)
[Next](next)
</div>
</div>
+13 -7
View File
@@ -59,10 +59,11 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Force => self.force = true,
}
Ok(())
}
}
@@ -100,11 +101,12 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Force => self.force = true,
Arg::NoForce => self.force = false,
}
Ok(())
}
}
@@ -160,10 +162,11 @@ enum Arg {
# }
#
# impl Options<Arg> for Settings {
# fn apply(&mut self, arg: Arg) {
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
# match arg {
# Arg::Name(name) => self.name = name,
# }
# Ok(())
# }
# }
#
@@ -197,10 +200,11 @@ enum Arg {
# }
#
# impl Options<Arg> for Settings {
# fn apply(&mut self, arg: Arg) {
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
# match arg {
# Arg::Name(name) => self.name = name,
# }
# Ok(())
# }
# }
#
@@ -234,10 +238,11 @@ enum Arg {
# }
#
# impl Options<Arg> for Settings {
# fn apply(&mut self, arg: Arg) {
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
# match arg {
# Arg::Force(b) => self.force = b,
# }
# Ok(())
# }
# }
#
@@ -269,10 +274,11 @@ enum Arg {
# }
#
# impl Options<Arg> for Settings {
# fn apply(&mut self, arg: Arg) {
# fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
# match arg {
# Arg::Sort(s) => self.sort = s,
# }
# Ok(())
# }
# }
#
@@ -287,4 +293,4 @@ enum Arg {
[Up](super)
[Next](next)
</div>
</div>
+3 -3
View File
@@ -22,18 +22,18 @@ enum Arg {
// Completion is derived from the `Number` type, through the `Value` trait
/// Give it a number!
#[arg("-n N", "--number=N")]
Number(Number),
Number(#[allow(unused)] Number),
// Completion is derived from the `PathBuf` type
/// Give it a path!
#[arg("-p P", "--path=P")]
Path(PathBuf),
Path(#[allow(unused)] PathBuf),
}
struct Settings;
impl Options<Arg> for Settings {
fn apply(&mut self, _arg: Arg) {
fn apply(&mut self, _arg: Arg) -> Result<(), uutils_args::Error> {
panic!("Compile with the 'parse-is-complete' feature!")
}
}
+2 -1
View File
@@ -34,11 +34,12 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Min(n) => self.n1 = n,
Arg::Plus(n) => self.n2 = n,
}
Ok(())
}
}
+2 -1
View File
@@ -22,12 +22,13 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Name(n) => self.name = n,
Arg::Count(c) => self.count = c,
Arg::Hidden => {}
}
Ok(())
}
}
+2 -1
View File
@@ -25,10 +25,11 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Color(c) => self.color = c,
}
Ok(())
}
}
+2 -2
View File
@@ -166,7 +166,7 @@ impl<T: Arguments> ArgumentIter<T> {
/// call [`Options::apply`] on the result until the arguments are exhausted.
pub trait Options<Arg: Arguments>: Sized {
/// Apply a single argument to the options.
fn apply(&mut self, arg: Arg);
fn apply(&mut self, arg: Arg) -> Result<(), Error>;
/// Parse an iterator of arguments into the options
#[allow(unused_mut)]
@@ -191,7 +191,7 @@ pub trait Options<Arg: Arguments>: Sized {
{
let mut iter = ArgumentIter::<Arg>::from_args(args);
while let Some(arg) = iter.next_arg()? {
self.apply(arg);
self.apply(arg)?;
}
Ok((self, iter.positional_arguments))
}
+3
View File
@@ -16,6 +16,9 @@ mod cat;
#[path = "coreutils/cksum.rs"]
mod cksum;
#[path = "coreutils/date.rs"]
mod date;
#[path = "coreutils/dd.rs"]
mod dd;
+2 -1
View File
@@ -46,7 +46,7 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Binary => self.binary = true,
Arg::Check => self.check = true,
@@ -57,6 +57,7 @@ impl Options<Arg> for Settings {
Arg::Strict => self.strict = true,
Arg::Warn => self.check_output = CheckOutput::Warn,
}
Ok(())
}
}
+2 -1
View File
@@ -34,13 +34,14 @@ impl Default for Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Decode => self.decode = true,
Arg::IgnoreGarbage => self.ignore_garbage = true,
Arg::Wrap(0) => self.wrap = None,
Arg::Wrap(x) => self.wrap = Some(x),
}
Ok(())
}
}
+2 -1
View File
@@ -26,7 +26,7 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Multiple => self.multiple = true,
Arg::Suffix(s) => {
@@ -35,6 +35,7 @@ impl Options<Arg> for Settings {
}
Arg::Zero => self.zero = true,
}
Ok(())
}
}
+2 -1
View File
@@ -48,7 +48,7 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::ShowAll => {
self.show_tabs = true;
@@ -70,6 +70,7 @@ impl Options<Arg> for Settings {
Arg::NumberNonblank => self.number = NumberingMode::NonEmpty,
Arg::SqueezeBlank => self.squeeze_blank = true,
}
Ok(())
}
}
+2 -1
View File
@@ -30,7 +30,7 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Binary => self.binary = Tristate::True,
Arg::Text => self.binary = Tristate::False,
@@ -47,6 +47,7 @@ impl Options<Arg> for Settings {
self.tag = Tristate::False;
}
}
Ok(())
}
}
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -32,7 +32,7 @@ enum Arg {
Bs(usize),
#[arg("cbs=BYTES")]
Cbs(usize),
Cbs(#[allow(unused)] usize),
#[arg("skip=BYTES", "iseek=BYTES")]
Skip(u64),
@@ -47,13 +47,13 @@ enum Arg {
Status(StatusLevel),
#[arg("conv=CONVERSIONS")]
Conv(String),
Conv(#[allow(unused)] String),
#[arg("iflag=FLAGS")]
Iflag(String),
Iflag(#[allow(unused)] String),
#[arg("oflag=FLAGS")]
Oflag(String),
Oflag(#[allow(unused)] String),
}
#[derive(Debug, PartialEq, Eq)]
@@ -92,7 +92,7 @@ impl Default for Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Infile(f) => self.infile = Some(f),
Arg::Outfile(f) => self.outfile = Some(f),
@@ -111,6 +111,7 @@ impl Options<Arg> for Settings {
Arg::Iflag(_f) => todo!(),
Arg::Oflag(_f) => todo!(),
}
Ok(())
}
}
+2 -1
View File
@@ -24,12 +24,13 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::NoNewline => self.trailing_newline = false,
Arg::EnableEscape => self.escape = true,
Arg::DisableEscape => self.escape = false,
}
Ok(())
}
}
+2 -1
View File
@@ -188,7 +188,7 @@ struct Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::Bytes(n) => {
self.mode = Mode::Bytes;
@@ -202,6 +202,7 @@ impl Options<Arg> for Settings {
Arg::Verbose => self.verbose = true,
Arg::Zero => self.zero = true,
}
Ok(())
}
}
+2 -1
View File
@@ -360,7 +360,7 @@ impl Default for Settings {
}
impl Options<Arg> for Settings {
fn apply(&mut self, arg: Arg) {
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
match arg {
Arg::All => self.which_files = Files::All,
Arg::AlmostAll => self.which_files = Files::AlmostAll,
@@ -417,6 +417,7 @@ impl Options<Arg> for Settings {
}
Arg::GroupDirectoriesFirst => self.group_directories_first = true,
}
Ok(())
}
}

Some files were not shown because too many files have changed in this diff Show More