You've already forked uutils-args
mirror of
https://github.com/uutils/uutils-args.git
synced 2026-06-10 16:13:08 -07:00
203 lines
6.5 KiB
Rust
203 lines
6.5 KiB
Rust
use uutils_args::{Arguments, Options};
|
|
|
|
#[derive(Debug, Clone, Arguments)]
|
|
enum Arg {
|
|
#[arg("-b", "--binary")]
|
|
Binary,
|
|
|
|
#[arg("-t", "--text")]
|
|
Text,
|
|
|
|
#[arg("--tag")]
|
|
Tag,
|
|
|
|
#[arg("--untagged")]
|
|
Untagged,
|
|
}
|
|
|
|
#[derive(Default, Debug, PartialEq)]
|
|
enum Tristate {
|
|
True,
|
|
#[default]
|
|
Unset,
|
|
False,
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
struct Settings {
|
|
binary: Tristate,
|
|
tag: Tristate,
|
|
}
|
|
|
|
impl Options<Arg> for Settings {
|
|
fn apply(&mut self, arg: Arg) -> Result<(), uutils_args::Error> {
|
|
match arg {
|
|
Arg::Binary => self.binary = Tristate::True,
|
|
Arg::Text => self.binary = Tristate::False,
|
|
Arg::Tag => {
|
|
// https://github.com/uutils/coreutils/issues/6364
|
|
self.binary = Tristate::Unset;
|
|
self.tag = Tristate::True;
|
|
}
|
|
Arg::Untagged => {
|
|
// https://github.com/uutils/coreutils/issues/6364
|
|
if self.tag == Tristate::True {
|
|
self.binary = Tristate::Unset;
|
|
}
|
|
self.tag = Tristate::False;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
enum ResultingFormat {
|
|
UntaggedText,
|
|
UntaggedBinary,
|
|
Tagged,
|
|
ErrorInstead,
|
|
}
|
|
|
|
impl Settings {
|
|
fn format(&self) -> ResultingFormat {
|
|
// Interpret "Unset" as "tagged":
|
|
if self.tag != Tristate::False {
|
|
// -> Tagged.
|
|
// Error only if the user explicitly requests the text format:
|
|
if self.binary == Tristate::False {
|
|
ResultingFormat::ErrorInstead
|
|
} else {
|
|
ResultingFormat::Tagged
|
|
}
|
|
} else {
|
|
// -> Untagged.
|
|
// Binary only if the user explicitly requests it:
|
|
if self.binary == Tristate::True {
|
|
ResultingFormat::UntaggedBinary
|
|
} else {
|
|
ResultingFormat::UntaggedText
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convenience function for testing
|
|
#[cfg(test)]
|
|
fn assert_format(args: &[&str], expected: ResultingFormat) {
|
|
let mut full_argv = vec!["bin_name"];
|
|
full_argv.extend(args);
|
|
let result = Settings::default().parse(full_argv).unwrap();
|
|
assert_eq!(
|
|
(result.0.format(), result.1.as_slice()),
|
|
(expected, [].as_slice()),
|
|
"{:?}",
|
|
args
|
|
);
|
|
}
|
|
|
|
// These tests basically force the reader to make the same conclusions as
|
|
// https://github.com/uutils/coreutils/issues/6364
|
|
// Quotes from the issue are marked with a leading ">".
|
|
|
|
#[test]
|
|
fn binary_text_toggle_in_tagged() {
|
|
// > Observe that -b/-t seems to be doing precisely what we would hope for: toggle between binary/text mode:
|
|
// -b/-t/--tagged switch between tagged/error behavior
|
|
assert_format(&[], ResultingFormat::Tagged);
|
|
assert_format(&["-t"], ResultingFormat::ErrorInstead);
|
|
assert_format(&["-t", "-b"], ResultingFormat::Tagged);
|
|
assert_format(&["-t", "--tag"], ResultingFormat::Tagged);
|
|
}
|
|
|
|
#[test]
|
|
fn binary_text_toggle_in_untagged() {
|
|
// Once we're in untagged format, -b/-t switch between binary/text behavior
|
|
assert_format(&["--untagged"], ResultingFormat::UntaggedText);
|
|
assert_format(&["--untagged", "-t"], ResultingFormat::UntaggedText);
|
|
assert_format(&["--untagged", "-b"], ResultingFormat::UntaggedBinary);
|
|
assert_format(&["--untagged", "-t", "-b"], ResultingFormat::UntaggedBinary);
|
|
assert_format(&["--untagged", "-b", "-t"], ResultingFormat::UntaggedText);
|
|
}
|
|
|
|
// > Observe that --tag/--untagged seems to be the flags that have the weird behavior attached to
|
|
// > them. In particular, the T state seems to be more that one actual state, probably
|
|
// > differentiated along the "text-binary-axis".
|
|
|
|
#[test]
|
|
fn nondeterministic_edges() {
|
|
// Same behavior:
|
|
assert_format(&[], ResultingFormat::Tagged);
|
|
assert_format(&["-b"], ResultingFormat::Tagged);
|
|
// But must have different internal state:
|
|
assert_format(&["--untagged"], ResultingFormat::UntaggedText);
|
|
assert_format(&["-b", "--untagged"], ResultingFormat::UntaggedBinary);
|
|
}
|
|
|
|
#[test]
|
|
fn selfloops() {
|
|
// "T"
|
|
assert_format(&[], ResultingFormat::Tagged);
|
|
assert_format(&["-b"], ResultingFormat::Tagged);
|
|
assert_format(&["--tag"], ResultingFormat::Tagged);
|
|
assert_format(&["-b", "--tag"], ResultingFormat::Tagged);
|
|
// "E"
|
|
assert_format(&["-t"], ResultingFormat::ErrorInstead);
|
|
assert_format(&["-t", "-t"], ResultingFormat::ErrorInstead);
|
|
// "A"
|
|
assert_format(&["-b", "--untagged"], ResultingFormat::UntaggedBinary);
|
|
assert_format(&["-b", "--untagged", "-b"], ResultingFormat::UntaggedBinary);
|
|
assert_format(
|
|
&["-b", "--untagged", "--untagged"],
|
|
ResultingFormat::UntaggedBinary,
|
|
);
|
|
// "S"
|
|
assert_format(&["--untagged"], ResultingFormat::UntaggedText);
|
|
assert_format(&["--untagged", "-t"], ResultingFormat::UntaggedText);
|
|
assert_format(&["--untagged", "--untagged"], ResultingFormat::UntaggedText);
|
|
}
|
|
|
|
#[test]
|
|
fn other_diagonals() {
|
|
// From "A" and "S" ...
|
|
assert_format(&["-b", "--untagged"], ResultingFormat::UntaggedBinary);
|
|
assert_format(&["--untagged"], ResultingFormat::UntaggedText);
|
|
// ... to "T":
|
|
assert_format(&["-b", "--untagged", "--tag"], ResultingFormat::Tagged);
|
|
assert_format(&["--untagged", "--tag"], ResultingFormat::Tagged);
|
|
// From "E" to "S":
|
|
assert_format(&["-t"], ResultingFormat::ErrorInstead);
|
|
assert_format(&["-t", "--untagged"], ResultingFormat::UntaggedText);
|
|
}
|
|
|
|
#[test]
|
|
fn suffix_b_u_not_deterministic() {
|
|
// > Ending in bU does not determine the result:
|
|
assert_format(&["-b", "--untagged"], ResultingFormat::UntaggedBinary);
|
|
assert_format(
|
|
&["--tag", "-b", "--untagged"],
|
|
ResultingFormat::UntaggedText,
|
|
);
|
|
assert_format(
|
|
&["--untagged", "-b", "--untagged"],
|
|
ResultingFormat::UntaggedBinary,
|
|
);
|
|
assert_format(
|
|
&["-b", "--untagged", "-b", "--untagged"],
|
|
ResultingFormat::UntaggedBinary,
|
|
);
|
|
assert_format(
|
|
&["--tag", "--untagged", "-b", "--untagged"],
|
|
ResultingFormat::UntaggedBinary,
|
|
);
|
|
assert_format(
|
|
&["--untagged", "--tag", "-b", "--untagged"],
|
|
ResultingFormat::UntaggedText,
|
|
);
|
|
// > Therefore, U does not set the binary-ness to a constant, but rather depends on the tagged-ness.
|
|
}
|
|
|
|
// I *think* that this battery of tests fully specifies the full behavior.
|
|
// In any case, brute-forcing all of the 4^n combinations up to 5 arguments
|
|
// shows no counter-examples, so this implementation is definitely a good match.
|