From 1bb0237281a405fd55e2ae08e70c8ef9754d642f Mon Sep 17 00:00:00 2001 From: Justin Tracey Date: Thu, 12 Aug 2021 23:32:29 -0400 Subject: [PATCH] join: add support for full outer joins --- src/uu/join/src/join.rs | 37 +++++++++++++------ tests/by-util/test_join.rs | 24 ++++++++++++ tests/fixtures/join/fields_3.txt | 1 + .../join/suppress_joined_outer.expected | 4 ++ .../join/unpaired_lines_format.expected | 1 + .../join/unpaired_lines_outer.expected | 10 +++++ 6 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 tests/fixtures/join/suppress_joined_outer.expected create mode 100644 tests/fixtures/join/unpaired_lines_outer.expected diff --git a/src/uu/join/src/join.rs b/src/uu/join/src/join.rs index 60721f212..d108a08ef 100644 --- a/src/uu/join/src/join.rs +++ b/src/uu/join/src/join.rs @@ -19,7 +19,6 @@ static NAME: &str = "join"; #[derive(Copy, Clone, PartialEq)] enum FileNum { - None, File1, File2, } @@ -41,7 +40,8 @@ enum CheckOrder { struct Settings { key1: usize, key2: usize, - print_unpaired: FileNum, + print_unpaired1: bool, + print_unpaired2: bool, print_joined: bool, ignore_case: bool, separator: Sep, @@ -57,7 +57,8 @@ impl Default for Settings { Settings { key1: 0, key2: 0, - print_unpaired: FileNum::None, + print_unpaired1: false, + print_unpaired2: false, print_joined: true, ignore_case: false, separator: Sep::Whitespaces, @@ -243,7 +244,7 @@ impl<'a> State<'a> { name: &'a str, stdin: &'a Stdin, key: usize, - print_unpaired: FileNum, + print_unpaired: bool, ) -> State<'a> { let f = if name == "-" { Box::new(stdin.lock()) as Box @@ -258,7 +259,7 @@ impl<'a> State<'a> { key, file_name: name, file_num, - print_unpaired: print_unpaired == file_num, + print_unpaired, lines: f.lines(), seq: Vec::new(), max_fields: None, @@ -450,11 +451,19 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let mut settings: Settings = Default::default(); - if let Some(value) = matches.value_of("v") { - settings.print_unpaired = parse_file_number(value); + let v_values = matches.values_of("v"); + if v_values.is_some() { settings.print_joined = false; - } else if let Some(value) = matches.value_of("a") { - settings.print_unpaired = parse_file_number(value); + } + + let unpaired = v_values + .unwrap_or_default() + .chain(matches.values_of("a").unwrap_or_default()); + for file_num in unpaired { + match parse_file_number(file_num) { + FileNum::File1 => settings.print_unpaired1 = true, + FileNum::File2 => settings.print_unpaired2 = true, + } } settings.ignore_case = matches.is_present("i"); @@ -520,7 +529,8 @@ When FILE1 or FILE2 (not both) is -, read standard input.", .arg( Arg::with_name("a") .short("a") - .takes_value(true) + .multiple(true) + .number_of_values(1) .possible_values(&["1", "2"]) .value_name("FILENUM") .help( @@ -531,6 +541,9 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2", .arg( Arg::with_name("v") .short("v") + .multiple(true) + .number_of_values(1) + .possible_values(&["1", "2"]) .value_name("FILENUM") .help("like -a FILENUM, but suppress joined output lines"), ) @@ -617,7 +630,7 @@ fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 { file1, &stdin, settings.key1, - settings.print_unpaired, + settings.print_unpaired1, ); let mut state2 = State::new( @@ -625,7 +638,7 @@ fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 { file2, &stdin, settings.key2, - settings.print_unpaired, + settings.print_unpaired2, ); let input = Input::new( diff --git a/tests/by-util/test_join.rs b/tests/by-util/test_join.rs index 8dbdadba7..1cab8361a 100644 --- a/tests/by-util/test_join.rs +++ b/tests/by-util/test_join.rs @@ -94,6 +94,18 @@ fn unpaired_lines() { .arg("2") .succeeds() .stdout_only_fixture("unpaired_lines.expected"); + + new_ucmd!() + .arg("fields_3.txt") + .arg("fields_2.txt") + .arg("-1") + .arg("2") + .arg("-a") + .arg("1") + .arg("-a") + .arg("2") + .succeeds() + .stdout_only_fixture("unpaired_lines_outer.expected"); } #[test] @@ -107,6 +119,18 @@ fn suppress_joined() { .arg("2") .succeeds() .stdout_only_fixture("suppress_joined.expected"); + + new_ucmd!() + .arg("fields_3.txt") + .arg("fields_2.txt") + .arg("-1") + .arg("2") + .arg("-a") + .arg("1") + .arg("-v") + .arg("2") + .succeeds() + .stdout_only_fixture("suppress_joined_outer.expected"); } #[test] diff --git a/tests/fixtures/join/fields_3.txt b/tests/fixtures/join/fields_3.txt index 4c5c0e779..95847c266 100644 --- a/tests/fixtures/join/fields_3.txt +++ b/tests/fixtures/join/fields_3.txt @@ -4,3 +4,4 @@ c 4 h f 5 i g 6 j h 7 k +i 99 l diff --git a/tests/fixtures/join/suppress_joined_outer.expected b/tests/fixtures/join/suppress_joined_outer.expected new file mode 100644 index 000000000..9d079f8bc --- /dev/null +++ b/tests/fixtures/join/suppress_joined_outer.expected @@ -0,0 +1,4 @@ +1 a +8 h +9 i +99 i l diff --git a/tests/fixtures/join/unpaired_lines_format.expected b/tests/fixtures/join/unpaired_lines_format.expected index d1324aa66..57aca84af 100644 --- a/tests/fixtures/join/unpaired_lines_format.expected +++ b/tests/fixtures/join/unpaired_lines_format.expected @@ -4,3 +4,4 @@ i 5 f j 6 g k 7 h + l 99 i diff --git a/tests/fixtures/join/unpaired_lines_outer.expected b/tests/fixtures/join/unpaired_lines_outer.expected new file mode 100644 index 000000000..0b4c9cd81 --- /dev/null +++ b/tests/fixtures/join/unpaired_lines_outer.expected @@ -0,0 +1,10 @@ +1 a +2 a f b +3 b g c +4 c h d +5 f i e +6 g j f +7 h k g +8 h +9 i +99 i l