From 4fa2b6ed982a24cc3d0010f04a1c02f2e7413ae7 Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Fri, 29 Mar 2024 03:32:30 +0100 Subject: [PATCH 1/5] make Options::apply fallible --- README.md | 3 ++- docs/guide/port.md | 17 +++++++++------ docs/guide/quick.md | 20 +++++++++++------- examples/completion.rs | 2 +- examples/deprecated.rs | 3 ++- examples/hello_world.rs | 3 ++- examples/value.rs | 3 ++- src/lib.rs | 4 ++-- tests/coreutils/b2sum.rs | 3 ++- tests/coreutils/base32.rs | 3 ++- tests/coreutils/basename.rs | 3 ++- tests/coreutils/cat.rs | 3 ++- tests/coreutils/cksum.rs | 3 ++- tests/coreutils/dd.rs | 3 ++- tests/coreutils/echo.rs | 3 ++- tests/coreutils/head.rs | 3 ++- tests/coreutils/ls.rs | 3 ++- tests/coreutils/mktemp.rs | 3 ++- tests/coreutils/shuf.rs | 3 ++- tests/coreutils/tail.rs | 3 ++- tests/coreutils/uniq.rs | 5 +++-- tests/flags.rs | 41 +++++++++++++++++++++++------------- tests/options.rs | 42 ++++++++++++++++++++++++------------- tests/options_first.rs | 3 ++- 24 files changed, 118 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index d27b71e..15801e5 100644 --- a/README.md +++ b/README.md @@ -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 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(()) } } diff --git a/docs/guide/port.md b/docs/guide/port.md index f7d6070..990f59b 100644 --- a/docs/guide/port.md +++ b/docs/guide/port.md @@ -94,10 +94,11 @@ enum Arg { struct Settings { a: bool } impl Options 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 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 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 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 } impl Options 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) - \ No newline at end of file + diff --git a/docs/guide/quick.md b/docs/guide/quick.md index 31f515b..5de6afb 100644 --- a/docs/guide/quick.md +++ b/docs/guide/quick.md @@ -59,10 +59,11 @@ struct Settings { } impl Options 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 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 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 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 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 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) - \ No newline at end of file + diff --git a/examples/completion.rs b/examples/completion.rs index d68e372..bcfda9a 100644 --- a/examples/completion.rs +++ b/examples/completion.rs @@ -33,7 +33,7 @@ enum Arg { struct Settings; impl Options 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!") } } diff --git a/examples/deprecated.rs b/examples/deprecated.rs index 2a6d5bd..71db541 100644 --- a/examples/deprecated.rs +++ b/examples/deprecated.rs @@ -34,11 +34,12 @@ struct Settings { } impl Options 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(()) } } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 7bd4eb0..8df6742 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -22,12 +22,13 @@ struct Settings { } impl Options 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(()) } } diff --git a/examples/value.rs b/examples/value.rs index 5359192..67926a1 100644 --- a/examples/value.rs +++ b/examples/value.rs @@ -25,10 +25,11 @@ struct Settings { } impl Options 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(()) } } diff --git a/src/lib.rs b/src/lib.rs index 5164734..e7b7058 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,7 +166,7 @@ impl ArgumentIter { /// call [`Options::apply`] on the result until the arguments are exhausted. pub trait Options: 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: Sized { { let mut iter = ArgumentIter::::from_args(args); while let Some(arg) = iter.next_arg()? { - self.apply(arg); + self.apply(arg)?; } Ok((self, iter.positional_arguments)) } diff --git a/tests/coreutils/b2sum.rs b/tests/coreutils/b2sum.rs index 3f24c71..a2081e3 100644 --- a/tests/coreutils/b2sum.rs +++ b/tests/coreutils/b2sum.rs @@ -46,7 +46,7 @@ struct Settings { } impl Options 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 for Settings { Arg::Strict => self.strict = true, Arg::Warn => self.check_output = CheckOutput::Warn, } + Ok(()) } } diff --git a/tests/coreutils/base32.rs b/tests/coreutils/base32.rs index 24ce251..e44ad9a 100644 --- a/tests/coreutils/base32.rs +++ b/tests/coreutils/base32.rs @@ -34,13 +34,14 @@ impl Default for Settings { } impl Options 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(()) } } diff --git a/tests/coreutils/basename.rs b/tests/coreutils/basename.rs index 9324fe3..473610e 100644 --- a/tests/coreutils/basename.rs +++ b/tests/coreutils/basename.rs @@ -26,7 +26,7 @@ struct Settings { } impl Options 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 for Settings { } Arg::Zero => self.zero = true, } + Ok(()) } } diff --git a/tests/coreutils/cat.rs b/tests/coreutils/cat.rs index 03df331..8b8da96 100644 --- a/tests/coreutils/cat.rs +++ b/tests/coreutils/cat.rs @@ -48,7 +48,7 @@ struct Settings { } impl Options 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 for Settings { Arg::NumberNonblank => self.number = NumberingMode::NonEmpty, Arg::SqueezeBlank => self.squeeze_blank = true, } + Ok(()) } } diff --git a/tests/coreutils/cksum.rs b/tests/coreutils/cksum.rs index 71682fd..b6a8086 100644 --- a/tests/coreutils/cksum.rs +++ b/tests/coreutils/cksum.rs @@ -30,7 +30,7 @@ struct Settings { } impl Options 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 for Settings { self.tag = Tristate::False; } } + Ok(()) } } diff --git a/tests/coreutils/dd.rs b/tests/coreutils/dd.rs index 8c938d8..b8b5358 100644 --- a/tests/coreutils/dd.rs +++ b/tests/coreutils/dd.rs @@ -92,7 +92,7 @@ impl Default for Settings { } impl Options 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 for Settings { Arg::Iflag(_f) => todo!(), Arg::Oflag(_f) => todo!(), } + Ok(()) } } diff --git a/tests/coreutils/echo.rs b/tests/coreutils/echo.rs index 2d561f2..89c3fd7 100644 --- a/tests/coreutils/echo.rs +++ b/tests/coreutils/echo.rs @@ -24,12 +24,13 @@ struct Settings { } impl Options 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(()) } } diff --git a/tests/coreutils/head.rs b/tests/coreutils/head.rs index cdf8326..90d6375 100644 --- a/tests/coreutils/head.rs +++ b/tests/coreutils/head.rs @@ -188,7 +188,7 @@ struct Settings { } impl Options 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 for Settings { Arg::Verbose => self.verbose = true, Arg::Zero => self.zero = true, } + Ok(()) } } diff --git a/tests/coreutils/ls.rs b/tests/coreutils/ls.rs index 9673926..4eca547 100644 --- a/tests/coreutils/ls.rs +++ b/tests/coreutils/ls.rs @@ -360,7 +360,7 @@ impl Default for Settings { } impl Options 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 for Settings { } Arg::GroupDirectoriesFirst => self.group_directories_first = true, } + Ok(()) } } diff --git a/tests/coreutils/mktemp.rs b/tests/coreutils/mktemp.rs index 2410f7d..fb5608f 100644 --- a/tests/coreutils/mktemp.rs +++ b/tests/coreutils/mktemp.rs @@ -40,7 +40,7 @@ struct Settings { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Directory => self.directory = true, Arg::DryRun => self.dry_run = true, @@ -49,6 +49,7 @@ impl Options for Settings { Arg::TreatAsTemplate => self.treat_as_template = true, Arg::TmpDir(dir) => self.tmp_dir = Some(dir), } + Ok(()) } } diff --git a/tests/coreutils/shuf.rs b/tests/coreutils/shuf.rs index 185e0b2..04ff88b 100644 --- a/tests/coreutils/shuf.rs +++ b/tests/coreutils/shuf.rs @@ -23,11 +23,12 @@ struct Settings { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Echo => self.echo = true, Arg::Zero => self.zero = true, } + Ok(()) } } diff --git a/tests/coreutils/tail.rs b/tests/coreutils/tail.rs index 2c9573e..c302547 100644 --- a/tests/coreutils/tail.rs +++ b/tests/coreutils/tail.rs @@ -244,7 +244,7 @@ struct Settings { } impl Options 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; @@ -268,6 +268,7 @@ impl Options for Settings { Arg::Zero => self.zero = true, Arg::PresumeInputPipe => self.presume_input_pipe = true, } + Ok(()) } } diff --git a/tests/coreutils/uniq.rs b/tests/coreutils/uniq.rs index 28a3264..dbcce1d 100644 --- a/tests/coreutils/uniq.rs +++ b/tests/coreutils/uniq.rs @@ -65,7 +65,7 @@ struct Settings { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::SkipFields(n) => { self.skip_fields = Some(n); @@ -100,6 +100,7 @@ impl Options for Settings { Arg::ZeroTerminated => { self.zero_terminated = true; } - } + }; + Ok(()) } } diff --git a/tests/flags.rs b/tests/flags.rs index c9877d1..b292c1b 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -14,10 +14,11 @@ fn one_flag() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Foo => self.foo = true, } + Ok(()) } } @@ -42,11 +43,12 @@ fn two_flags() { } impl Options 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, Arg::B => self.b = true, } + Ok(()) } } @@ -82,8 +84,9 @@ fn long_and_short_flag() { } impl Options for Settings { - fn apply(&mut self, Arg::Foo: Arg) { + fn apply(&mut self, Arg::Foo: Arg) -> Result<(), uutils_args::Error> { self.foo = true; + Ok(()) } } @@ -106,8 +109,9 @@ fn short_alias() { } impl Options for Settings { - fn apply(&mut self, Arg::Foo: Arg) { + fn apply(&mut self, Arg::Foo: Arg) -> Result<(), uutils_args::Error> { self.foo = true; + Ok(()) } } @@ -128,8 +132,9 @@ fn long_alias() { } impl Options for Settings { - fn apply(&mut self, Arg::Foo: Arg) { + fn apply(&mut self, Arg::Foo: Arg) -> Result<(), uutils_args::Error> { self.foo = true; + Ok(()) } } @@ -153,11 +158,12 @@ fn short_and_long_alias() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Foo => self.foo = true, Arg::Bar => self.bar = true, } + Ok(()) } } @@ -209,7 +215,7 @@ fn xyz_map_to_abc() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::X => { self.a = true; @@ -225,6 +231,7 @@ fn xyz_map_to_abc() { self.c = true; } } + Ok(()) } } @@ -282,11 +289,12 @@ fn non_rust_ident() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::FooBar => self.a = true, Arg::Super => self.b = true, } + Ok(()) } } @@ -312,8 +320,9 @@ fn number_flag() { } impl Options for Settings { - fn apply(&mut self, Arg::One: Arg) { + fn apply(&mut self, Arg::One: Arg) -> Result<(), uutils_args::Error> { self.one = true; + Ok(()) } } @@ -336,11 +345,12 @@ fn false_bool() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { self.foo = match arg { Arg::A => true, Arg::B => false, - } + }; + Ok(()) } } @@ -378,8 +388,9 @@ fn verbosity() { } impl Options for Settings { - fn apply(&mut self, Arg::Verbosity: Arg) { + fn apply(&mut self, Arg::Verbosity: Arg) -> Result<(), uutils_args::Error> { self.verbosity += 1; + Ok(()) } } @@ -429,12 +440,13 @@ fn infer_long_args() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::All => self.all = true, Arg::AlmostAll => self.almost_all = true, Arg::Author => self.author = true, } + Ok(()) } } @@ -482,12 +494,13 @@ fn enum_flag() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { self.foo = match arg { Arg::Foo => SomeEnum::Foo, Arg::Bar => SomeEnum::Bar, Arg::Baz => SomeEnum::Baz, }; + Ok(()) } } diff --git a/tests/options.rs b/tests/options.rs index 2475db0..b9d2902 100644 --- a/tests/options.rs +++ b/tests/options.rs @@ -16,8 +16,9 @@ fn string_option() { } impl Options for Settings { - fn apply(&mut self, Arg::Message(s): Arg) { - self.message = s + fn apply(&mut self, Arg::Message(s): Arg) -> Result<(), uutils_args::Error> { + self.message = s; + Ok(()) } } @@ -56,8 +57,9 @@ fn enum_option() { } impl Options for Settings { - fn apply(&mut self, Arg::Format(f): Arg) { + fn apply(&mut self, Arg::Format(f): Arg) -> Result<(), uutils_args::Error> { self.format = f; + Ok(()) } } @@ -103,8 +105,9 @@ fn enum_option_with_fields() { } impl Options for Settings { - fn apply(&mut self, Arg::Indent(i): Arg) { + fn apply(&mut self, Arg::Indent(i): Arg) -> Result<(), uutils_args::Error> { self.indent = i; + Ok(()) } } @@ -160,8 +163,9 @@ fn enum_with_complex_from_value() { } impl Options for Settings { - fn apply(&mut self, Arg::Indent(i): Arg) { + fn apply(&mut self, Arg::Indent(i): Arg) -> Result<(), uutils_args::Error> { self.indent = i; + Ok(()) } } @@ -208,8 +212,9 @@ fn color() { } impl Options for Settings { - fn apply(&mut self, Arg::Color(c): Arg) { + fn apply(&mut self, Arg::Color(c): Arg) -> Result<(), uutils_args::Error> { self.color = c.unwrap_or(Color::Always); + Ok(()) } } @@ -283,7 +288,7 @@ fn actions() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Message(m) => { self.last_message.clone_from(&m); @@ -292,6 +297,7 @@ fn actions() { Arg::Send => self.send = true, Arg::Receive => self.send = false, } + Ok(()) } } @@ -317,11 +323,12 @@ fn width() { } impl Options for Settings { - fn apply(&mut self, Arg::Width(w): Arg) { + fn apply(&mut self, Arg::Width(w): Arg) -> Result<(), uutils_args::Error> { self.width = match w { 0 => None, x => Some(x), - } + }; + Ok(()) } } @@ -367,7 +374,7 @@ fn integers() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { self.n = match arg { Arg::U8(x) => x as i128, Arg::U16(x) => x as i128, @@ -379,7 +386,8 @@ fn integers() { Arg::I32(x) => x as i128, Arg::I64(x) => x as i128, Arg::I128(x) => x, - } + }; + Ok(()) } } @@ -454,8 +462,9 @@ fn ls_classify() { } impl Options for Settings { - fn apply(&mut self, Arg::Classify(c): Arg) { + fn apply(&mut self, Arg::Classify(c): Arg) -> Result<(), uutils_args::Error> { self.classify = c; + Ok(()) } } @@ -507,8 +516,9 @@ fn mktemp_tmpdir() { } impl Options for Settings { - fn apply(&mut self, Arg::TmpDir(dir): Arg) { + fn apply(&mut self, Arg::TmpDir(dir): Arg) -> Result<(), uutils_args::Error> { self.tmpdir = Some(dir); + Ok(()) } } @@ -581,11 +591,12 @@ fn deprecated() { } impl Options 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(()) } } @@ -621,10 +632,11 @@ fn empty_value() { struct Settings {} impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Val(_) => {} } + Ok(()) } } } diff --git a/tests/options_first.rs b/tests/options_first.rs index ee9bc73..c7fa0e7 100644 --- a/tests/options_first.rs +++ b/tests/options_first.rs @@ -18,10 +18,11 @@ fn timeout_like() { } impl Options for Settings { - fn apply(&mut self, arg: Arg) { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { match arg { Arg::Verbose => self.verbose = true, } + Ok(()) } } From a49426d0bab00e9e94aa36537f99dc07b1ddd216 Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Fri, 29 Mar 2024 03:51:00 +0100 Subject: [PATCH 2/5] document new feature and add example / test --- docs/design/problems_with_clap.md | 16 ++++++++++++--- tests/flags.rs | 33 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/docs/design/problems_with_clap.md b/docs/design/problems_with_clap.md index d3c8774..308b2ac 100644 --- a/docs/design/problems_with_clap.md +++ b/docs/design/problems_with_clap.md @@ -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 diff --git a/tests/flags.rs b/tests/flags.rs index b292c1b..8d4569b 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -517,3 +517,36 @@ fn enum_flag() { SomeEnum::Baz, ); } + +#[test] +fn simple_error() { + #[derive(Arguments)] + enum Arg { + #[arg("-f", "--foo")] + Foo, + } + + #[derive(Debug, Default)] + struct Settings {} + + impl Options for Settings { + fn apply(&mut self, _arg: Arg) -> Result<(), uutils_args::Error> { + Err(uutils_args::Error { + exit_code: 42, + kind: uutils_args::ErrorKind::UnexpectedArgument( + "This is an example error".to_owned(), + ), + }) + } + } + + let settings_or_error = Settings::default().parse(["test", "-f"]); + let the_error = settings_or_error.expect_err("should have propagated error"); + assert_eq!(the_error.exit_code, 42); + match the_error.kind { + uutils_args::ErrorKind::UnexpectedArgument(err_str) => { + assert_eq!(err_str, "This is an example error") + } + _ => panic!("wrong error kind: {:?}", the_error.kind), + } +} From f80153534269a9fe611a25f75d9e2889681a675f Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Fri, 29 Mar 2024 05:34:38 +0100 Subject: [PATCH 3/5] shuf: skip test that would exit the test suite too early --- tests/coreutils/shuf.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/coreutils/shuf.rs b/tests/coreutils/shuf.rs index 04ff88b..ff44e94 100644 --- a/tests/coreutils/shuf.rs +++ b/tests/coreutils/shuf.rs @@ -87,6 +87,7 @@ fn noarg_is_file_zero() { } #[test] +#[ignore = "exits too early"] fn the_help() { let settings = parse(&["shuf", "--help"]).unwrap(); assert_eq!( From 29d94e1f61354befd4be9ea40d990d160dc0dd0e Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Fri, 29 Mar 2024 05:35:35 +0100 Subject: [PATCH 4/5] write date output format logic with exhaustive tests Also, exhausting tests. --- tests/coreutils.rs | 3 + tests/coreutils/date.rs | 712 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 715 insertions(+) create mode 100644 tests/coreutils/date.rs diff --git a/tests/coreutils.rs b/tests/coreutils.rs index de16e8d..eca5376 100644 --- a/tests/coreutils.rs +++ b/tests/coreutils.rs @@ -16,6 +16,9 @@ mod cat; #[path = "coreutils/cksum.rs"] mod cksum; +#[path = "coreutils/date.rs"] +mod date; + #[path = "coreutils/dd.rs"] mod dd; diff --git a/tests/coreutils/date.rs b/tests/coreutils/date.rs new file mode 100644 index 0000000..efe6bd0 --- /dev/null +++ b/tests/coreutils/date.rs @@ -0,0 +1,712 @@ +use std::ffi::OsString; +use uutils_args::{Arguments, Options, Value}; + +// Note: "+%s"-style format options aren't covered here, but should be! + +// +%s +// -I[FMT], --iso-8601[=FMT] output date/time in ISO 8601 format. +// -R, --rfc-email output date and time in RFC 5322 format. +// --rfc-3339=FMT output date/time in RFC 3339 format. +// date, hours, minutes, seconds, ns +// date, seconds, ns + +#[derive(Default, Debug, PartialEq, Eq, Value)] +enum Iso8601Format { + #[default] + #[value("date")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("dat")] + #[value("da")] + #[value("d")] + Date, + + #[value("hours")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("hour")] + #[value("hou")] + #[value("ho")] + #[value("h")] + Hours, + + #[value("minutes")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("minute")] + #[value("minut")] + #[value("minu")] + #[value("min")] + #[value("mi")] + #[value("m")] + Minutes, + + #[value("seconds")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("second")] + #[value("secon")] + #[value("seco")] + #[value("sec")] + #[value("se")] + #[value("s")] + Seconds, + + #[value("ns")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("n")] + Ns, +} + +#[derive(Debug, PartialEq, Eq, Value)] +enum Rfc3339Format { + #[value("date")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("dat")] + #[value("da")] + #[value("d")] + Date, + + #[value("seconds")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("second")] + #[value("secon")] + #[value("seco")] + #[value("sec")] + #[value("se")] + #[value("s")] + Seconds, + + #[value("ns")] + // TODO: Express the concept "accepts prefixes" more nicely. + #[value("n")] + Ns, +} + +#[derive(Arguments)] +enum Arg { + #[arg("-I[FMT]")] + #[arg("--iso-8601[=FMT]")] + Iso(Iso8601Format), + + #[arg("--rfc-3339=FMT")] + Rfc3339(Rfc3339Format), + + #[arg("-R")] + #[arg("--rfc-email")] + RfcEmail, +} + +#[derive(Debug, Default, PartialEq, Eq)] +enum Format { + #[default] + Unspecified, + Iso8601(Iso8601Format), + Rfc3339(Rfc3339Format), + RfcEmail, + // FromString(OsString), +} + +#[derive(Debug, Default, PartialEq, Eq)] +struct Settings { + chosen_format: Format, +} + +const MAGIC_MULTI_OUTPUT_ARG: &str = "! multiformat"; + +impl Options for Settings { + fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> { + if self.chosen_format != Format::Unspecified { + return Err(uutils_args::Error { + exit_code: 1, + kind: uutils_args::ErrorKind::UnexpectedArgument(MAGIC_MULTI_OUTPUT_ARG.to_owned()), + }); + } + match arg { + Arg::Iso(iso) => self.chosen_format = Format::Iso8601(iso), + Arg::Rfc3339(rfc3339) => self.chosen_format = Format::Rfc3339(rfc3339), + Arg::RfcEmail => self.chosen_format = Format::RfcEmail, + } + Ok(()) + } +} + +#[test] +fn noarg() { + let (settings, operands) = Settings::default().parse(["date"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Unspecified); +} + +#[test] +fn iso_short_noarg() { + let (settings, operands) = Settings::default().parse(["date", "-I"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_short_arg_direct_date() { + let (settings, operands) = Settings::default().parse(["date", "-Idate"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_short_arg_equal_date() { + // Not accepted by GNU, but we want to accept it. + let (settings, operands) = Settings::default().parse(["date", "-I=date"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_short_arg_space_date() { + let (settings, operands) = Settings::default().parse(["date", "-I", "date"]).unwrap(); + // Must not be interpreted as an argument to "-I". + assert_eq!(operands, vec!["date"]); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_short_arg_direct_minutes() { + let (settings, operands) = Settings::default().parse(["date", "-Iminutes"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Minutes) + ); +} + +#[test] +fn iso_short_arg_equal_minutes() { + // Not accepted by GNU, but we want to accept it. + let (settings, operands) = Settings::default().parse(["date", "-I=minutes"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Minutes) + ); +} + +#[test] +fn iso_short_arg_space_minutes() { + let (settings, operands) = Settings::default() + .parse(["date", "-I", "minutes"]) + .unwrap(); + // Must not be interpreted as an argument to "-I". + assert_eq!(operands, vec!["minutes"]); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_short_arg_invalid() { + let the_err = Settings::default() + .parse(["date", "-Idefinitely_invalid"]) + .unwrap_err(); + // Must not be interpreted as an argument to "-I". + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::ParsingFailed { option, value, .. } => { + assert_eq!(option, "-I"); + assert_eq!(value, "definitely_invalid"); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn iso_short_arg_equal_hours() { + let (settings, operands) = Settings::default().parse(["date", "-I=hours"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Hours) + ); +} + +#[test] +fn iso_short_arg_equal_seconds() { + let (settings, operands) = Settings::default().parse(["date", "-I=seconds"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Seconds) + ); +} + +#[test] +fn iso_short_arg_equal_ns() { + let (settings, operands) = Settings::default().parse(["date", "-I=ns"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Ns)); +} + +#[test] +fn iso_short_arg_equal_hour_singular() { + let (settings, operands) = Settings::default().parse(["date", "-I=hour"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Hours) + ); +} + +#[test] +fn iso_short_arg_equal_second_singular() { + let (settings, operands) = Settings::default().parse(["date", "-I=second"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Seconds) + ); +} + +#[test] +fn iso_short_arg_equal_minute_singular() { + let (settings, operands) = Settings::default().parse(["date", "-I=minute"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Minutes) + ); +} + +#[test] +fn iso_short_arg_equal_n_singular() { + let (settings, operands) = Settings::default().parse(["date", "-I=n"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Ns)); +} + +#[test] +fn iso_long_noarg() { + let (settings, operands) = Settings::default().parse(["date", "--iso-8601"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_long_equal_date() { + let (settings, operands) = Settings::default() + .parse(["date", "--iso-8601=date"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_long_equal_hour() { + let (settings, operands) = Settings::default() + .parse(["date", "--iso-8601=hour"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!( + settings.chosen_format, + Format::Iso8601(Iso8601Format::Hours) + ); +} + +#[test] +fn iso_long_space_hour() { + let (settings, operands) = Settings::default() + .parse(["date", "--iso-8601", "hour"]) + .unwrap(); + // Must not be interpreted as an argument to "-I". + assert_eq!(operands, vec!["hour"]); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Date)); +} + +#[test] +fn iso_long_equal_n() { + let (settings, operands) = Settings::default().parse(["date", "--iso-8601=n"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Iso8601(Iso8601Format::Ns)); +} + +#[test] +fn rfc3339_noarg() { + let the_err = Settings::default() + .parse(["date", "--rfc-3339"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::MissingValue { option } => { + assert_eq!(option, Some("--rfc-3339".to_owned())); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc3339_equal_date() { + let (settings, operands) = Settings::default() + .parse(["date", "--rfc-3339=date"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Rfc3339(Rfc3339Format::Date)); +} + +#[test] +fn rfc3339_equal_ns() { + let (settings, operands) = Settings::default() + .parse(["date", "--rfc-3339=ns"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Rfc3339(Rfc3339Format::Ns)); +} + +#[test] +fn rfc3339_equal_n_singular() { + let (settings, operands) = Settings::default().parse(["date", "--rfc-3339=n"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Rfc3339(Rfc3339Format::Ns)); +} + +#[test] +fn rfc3339_equal_minutes() { + let the_err = Settings::default() + .parse(["date", "--rfc-3339=minutes"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::ParsingFailed { option, value, .. } => { + assert_eq!(option, "--rfc-3339"); + assert_eq!(value, "minutes"); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc3339_space_date() { + let (settings, operands) = Settings::default() + .parse(["date", "--rfc-3339", "date"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Rfc3339(Rfc3339Format::Date)); +} + +#[test] +fn rfc3339_space_ns() { + let (settings, operands) = Settings::default() + .parse(["date", "--rfc-3339", "ns"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Rfc3339(Rfc3339Format::Ns)); +} + +#[test] +fn rfc3339_space_n_singular() { + let (settings, operands) = Settings::default() + .parse(["date", "--rfc-3339", "n"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Rfc3339(Rfc3339Format::Ns)); +} + +#[test] +fn rfc3339_space_minutes() { + let the_err = Settings::default() + .parse(["date", "--rfc-3339", "minutes"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::ParsingFailed { option, value, .. } => { + assert_eq!(option, "--rfc-3339"); + assert_eq!(value, "minutes"); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_email_short() { + let (settings, operands) = Settings::default().parse(["date", "-R"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::RfcEmail); +} + +#[test] +fn rfc_email_long() { + let (settings, operands) = Settings::default().parse(["date", "--rfc-email"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::RfcEmail); +} + +#[test] +fn rfc_clash_isoshort_isoshort() { + let the_err = Settings::default().parse(["date", "-I", "-I"]).unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isoshort_isolong() { + let the_err = Settings::default() + .parse(["date", "-I", "--iso-8601"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isoshort_rfc3339() { + let the_err = Settings::default() + .parse(["date", "-I", "--rfc-3339=date"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isoshort_rfcemailshort() { + let the_err = Settings::default().parse(["date", "-I", "-R"]).unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isoshort_rfcemaillong() { + let the_err = Settings::default() + .parse(["date", "-I", "--rfc-email"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isolong_isoshort() { + let the_err = Settings::default() + .parse(["date", "--iso-8601", "-I"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isolong_isolong() { + let the_err = Settings::default() + .parse(["date", "--iso-8601", "--iso-8601"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isolong_rfc3339() { + let the_err = Settings::default() + .parse(["date", "--iso-8601", "--rfc-3339=date"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isolong_rfcemailshort() { + let the_err = Settings::default() + .parse(["date", "--iso-8601", "-R"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_isolong_rfcemaillong() { + let the_err = Settings::default() + .parse(["date", "--iso-8601", "--rfc-email"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_rfcemailshort_isoshort() { + let the_err = Settings::default().parse(["date", "-R", "-I"]).unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_rfcemailshort_isolong() { + let the_err = Settings::default() + .parse(["date", "-R", "--iso-8601"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_rfcemailshort_rfc3339() { + let the_err = Settings::default() + .parse(["date", "-R", "--rfc-3339=date"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_rfcemailshort_rfcemailshort() { + let the_err = Settings::default().parse(["date", "-R", "-R"]).unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +fn rfc_clash_rfcemailshort_rfcemaillong() { + let the_err = Settings::default() + .parse(["date", "-R", "--rfc-email"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +#[test] +#[ignore = "exits too early, but works correctly"] +fn default_show_help() { + let (settings, operands) = Settings::default().parse(&["date", "--help"]).unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::Unspecified); +} + +#[test] +#[ignore = "BROKEN, exits too early"] +fn rfcemail_show_help() { + let (settings, operands) = Settings::default() + .parse(&["date", "-R", "--help"]) + .unwrap(); + assert_eq!(operands, Vec::::new()); + assert_eq!(settings.chosen_format, Format::RfcEmail); +} + +#[test] +fn multi_output_has_priority() { + let the_err = Settings::default() + .parse(&["date", "-R", "-R", "--help"]) + .unwrap_err(); + assert_eq!(the_err.exit_code, 1); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} + +/// https://github.com/uutils/coreutils/issues/4254#issuecomment-2026446634 +#[test] +fn priority_demo() { + // Earliest faulty argument is the first argument, must complaint about that: + let the_err = Settings::default() + .parse(&["date", "-Idefinitely_invalid", "-R", "-R"]) + .unwrap_err(); + match the_err.kind { + uutils_args::ErrorKind::ParsingFailed { option, value, .. } => { + assert_eq!(option, "-I"); + assert_eq!(value, "definitely_invalid"); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } + // Earliest faulty argument is the second argument, must complaint about that: + let the_err = Settings::default() + .parse(&["date", "-R", "-R", "-Idefinitely_invalid"]) + .unwrap_err(); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } + // Earliest faulty argument is the second argument, must complaint about that: + let the_err = Settings::default() + .parse(&["date", "-R", "-Idefinitely_invalid", "-R"]) + .unwrap_err(); + match the_err.kind { + uutils_args::ErrorKind::ParsingFailed { option, value, .. } => { + assert_eq!(option, "-I"); + assert_eq!(value, "definitely_invalid"); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } + // Earliest faulty argument is the second argument, must complaint about that: + let the_err = Settings::default() + .parse(&["date", "-R", "-Ins", "-R"]) + .unwrap_err(); + match the_err.kind { + uutils_args::ErrorKind::UnexpectedArgument(arg) => { + assert_eq!(arg, MAGIC_MULTI_OUTPUT_ARG); + } + _ => panic!("wrong error kind: {:?}", the_err.kind), + } +} From e2cdf411b3f52170be9b6f433f49abd0590007a7 Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Fri, 29 Mar 2024 14:05:32 +0100 Subject: [PATCH 5/5] Mark unused spaces to satisfy clippy This causes CI failures, so let's fix this. --- examples/completion.rs | 4 ++-- tests/coreutils/dd.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/completion.rs b/examples/completion.rs index bcfda9a..f9e4cce 100644 --- a/examples/completion.rs +++ b/examples/completion.rs @@ -22,12 +22,12 @@ 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; diff --git a/tests/coreutils/dd.rs b/tests/coreutils/dd.rs index b8b5358..32cbe1a 100644 --- a/tests/coreutils/dd.rs +++ b/tests/coreutils/dd.rs @@ -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)]