mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
commit
49a67bae33
19 changed files with 278 additions and 32 deletions
1
Makefile
1
Makefile
|
@ -171,6 +171,7 @@ TEST_PROGS := \
|
|||
truncate \
|
||||
tsort \
|
||||
unexpand \
|
||||
uniq \
|
||||
unlink \
|
||||
wc
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ extern crate getopts;
|
|||
extern crate uucore;
|
||||
|
||||
use getopts::{Matches, Options};
|
||||
use std::cmp::min;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write};
|
||||
use std::path::Path;
|
||||
|
@ -45,7 +44,7 @@ impl Uniq {
|
|||
|
||||
for io_line in reader.lines() {
|
||||
let line = crash_if_err!(1, io_line);
|
||||
if !lines.is_empty() && self.cmp_key(&self.skip_fields(&lines[0])) != self.cmp_key(&self.skip_fields(&line)) {
|
||||
if !lines.is_empty() && self.cmp_key(&lines[0]) != self.cmp_key(&line) {
|
||||
let print_delimiter = delimiters == "prepend" || (delimiters == "separate" && first_line_printed);
|
||||
first_line_printed |= self.print_lines(writer, &lines, print_delimiter);
|
||||
lines.truncate(0);
|
||||
|
@ -59,47 +58,41 @@ impl Uniq {
|
|||
}
|
||||
|
||||
fn skip_fields(&self, line: &str) -> String {
|
||||
match self.skip_fields {
|
||||
Some(skip_fields) =>
|
||||
if line.split_whitespace().count() > skip_fields {
|
||||
let mut field = 0;
|
||||
let mut i = 0;
|
||||
while field < skip_fields && i < line.len() {
|
||||
while i < line.len() && line.chars().nth(i).unwrap().is_whitespace() {
|
||||
i = i + 1;
|
||||
}
|
||||
while i < line.len() && !line.chars().nth(i).unwrap().is_whitespace() {
|
||||
i = i + 1;
|
||||
}
|
||||
field = field + 1;
|
||||
if let Some(skip_fields) = self.skip_fields {
|
||||
if line.split_whitespace().count() > skip_fields {
|
||||
let mut field = 0;
|
||||
let mut i = 0;
|
||||
while field < skip_fields && i < line.len() {
|
||||
while i < line.len() && line.chars().nth(i).unwrap().is_whitespace() {
|
||||
i = i + 1;
|
||||
}
|
||||
line[i..].to_owned()
|
||||
} else {
|
||||
"".to_owned()
|
||||
},
|
||||
None => line[..].to_owned()
|
||||
while i < line.len() && !line.chars().nth(i).unwrap().is_whitespace() {
|
||||
i = i + 1;
|
||||
}
|
||||
field = field + 1;
|
||||
}
|
||||
line[i..].to_owned()
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
} else {
|
||||
line[..].to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_key(&self, line: &str) -> String {
|
||||
let len = line.len();
|
||||
let fields_to_check = &self.skip_fields(line);
|
||||
let len = fields_to_check.len();
|
||||
if len > 0 {
|
||||
let slice_start = match self.slice_start {
|
||||
Some(i) => min(i, len - 1),
|
||||
None => 0
|
||||
};
|
||||
let slice_stop = match self.slice_stop {
|
||||
Some(i) => min(slice_start + i, len),
|
||||
None => len
|
||||
};
|
||||
|
||||
line[slice_start..slice_stop].chars()
|
||||
fields_to_check.chars()
|
||||
.skip(self.slice_start.unwrap_or(0))
|
||||
.take(self.slice_stop.unwrap_or(len))
|
||||
.map(|c| match c {
|
||||
'a' ... 'z' if self.ignore_case => ((c as u8) - 32) as char,
|
||||
_ => c,
|
||||
}).collect()
|
||||
} else {
|
||||
line.to_owned()
|
||||
fields_to_check.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
9
tests/fixtures/uniq/skip-1-char.expected
vendored
Normal file
9
tests/fixtures/uniq/skip-1-char.expected
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
aaaaa 1
|
||||
Xbbbb 2
|
||||
XXbbb 2
|
||||
ccccc 3
|
||||
Xdddd 3
|
||||
XXddd 3
|
||||
XXXdd 1
|
||||
eeee
|
||||
fff
|
2
tests/fixtures/uniq/skip-2-fields.expected
vendored
Normal file
2
tests/fixtures/uniq/skip-2-fields.expected
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
aaa aa a
|
||||
aa a
|
6
tests/fixtures/uniq/skip-3-check-2-chars.expected
vendored
Normal file
6
tests/fixtures/uniq/skip-3-check-2-chars.expected
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
aaaaa 1
|
||||
Xbbbb 2
|
||||
ccccc 3
|
||||
Xdddd 3
|
||||
eeee
|
||||
fff
|
7
tests/fixtures/uniq/skip-3-check-5-chars.expected
vendored
Normal file
7
tests/fixtures/uniq/skip-3-check-5-chars.expected
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
aaaaa 1
|
||||
Xbbbb 2
|
||||
ccccc 3
|
||||
Xdddd 3
|
||||
XXXdd 1
|
||||
eeee
|
||||
fff
|
5
tests/fixtures/uniq/skip-5-chars.expected
vendored
Normal file
5
tests/fixtures/uniq/skip-5-chars.expected
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
aaaaa 1
|
||||
Xbbbb 2
|
||||
ccccc 3
|
||||
XXXdd 1
|
||||
eeee
|
12
tests/fixtures/uniq/skip-chars.txt
vendored
Normal file
12
tests/fixtures/uniq/skip-chars.txt
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
aaaaa 1
|
||||
Xbbbb 2
|
||||
XXbbb 2
|
||||
ccccc 3
|
||||
Xcccc 3
|
||||
ccccc 3
|
||||
Xdddd 3
|
||||
XXddd 3
|
||||
XXXdd 1
|
||||
XXXdd 1
|
||||
eeee
|
||||
fff
|
8
tests/fixtures/uniq/skip-fields.txt
vendored
Normal file
8
tests/fixtures/uniq/skip-fields.txt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
aaa aa a
|
||||
ZZZ aa a
|
||||
ZZZ aa a
|
||||
ZZZ bb a
|
||||
ZZZ bb a
|
||||
aa a
|
||||
a
|
||||
|
22
tests/fixtures/uniq/sorted-all-repeated-prepend.expected
vendored
Normal file
22
tests/fixtures/uniq/sorted-all-repeated-prepend.expected
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
bbbbb ⅱ
|
||||
bbbbb ⅱ
|
||||
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
|
||||
fffff ⅲ
|
||||
fffff ⅲ
|
||||
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
|
||||
GGGGG ⅲ
|
||||
GGGGG ⅲ
|
21
tests/fixtures/uniq/sorted-all-repeated-separate.expected
vendored
Normal file
21
tests/fixtures/uniq/sorted-all-repeated-separate.expected
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
bbbbb ⅱ
|
||||
bbbbb ⅱ
|
||||
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
|
||||
fffff ⅲ
|
||||
fffff ⅲ
|
||||
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
|
||||
GGGGG ⅲ
|
||||
GGGGG ⅲ
|
16
tests/fixtures/uniq/sorted-all-repeated.expected
vendored
Normal file
16
tests/fixtures/uniq/sorted-all-repeated.expected
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
bbbbb ⅱ
|
||||
bbbbb ⅱ
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
fffff ⅲ
|
||||
fffff ⅲ
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
GGGGG ⅲ
|
||||
GGGGG ⅲ
|
8
tests/fixtures/uniq/sorted-counts.expected
vendored
Normal file
8
tests/fixtures/uniq/sorted-counts.expected
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
1 aaaaa ⅰ
|
||||
2 bbbbb ⅱ
|
||||
3 ccccc ⅲ
|
||||
4 ddddd ⅲ
|
||||
1 eeeee ⅲ
|
||||
2 fffff ⅲ
|
||||
3 ggggg ⅲ
|
||||
2 GGGGG ⅲ
|
7
tests/fixtures/uniq/sorted-ignore-case.expected
vendored
Normal file
7
tests/fixtures/uniq/sorted-ignore-case.expected
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
aaaaa ⅰ
|
||||
bbbbb ⅱ
|
||||
ccccc ⅲ
|
||||
ddddd ⅲ
|
||||
eeeee ⅲ
|
||||
fffff ⅲ
|
||||
ggggg ⅲ
|
6
tests/fixtures/uniq/sorted-repeated-only.expected
vendored
Normal file
6
tests/fixtures/uniq/sorted-repeated-only.expected
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
bbbbb ⅱ
|
||||
ccccc ⅲ
|
||||
ddddd ⅲ
|
||||
fffff ⅲ
|
||||
ggggg ⅲ
|
||||
GGGGG ⅲ
|
8
tests/fixtures/uniq/sorted-simple.expected
vendored
Normal file
8
tests/fixtures/uniq/sorted-simple.expected
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
aaaaa ⅰ
|
||||
bbbbb ⅱ
|
||||
ccccc ⅲ
|
||||
ddddd ⅲ
|
||||
eeeee ⅲ
|
||||
fffff ⅲ
|
||||
ggggg ⅲ
|
||||
GGGGG ⅲ
|
2
tests/fixtures/uniq/sorted-unique-only.expected
vendored
Normal file
2
tests/fixtures/uniq/sorted-unique-only.expected
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
aaaaa ⅰ
|
||||
eeeee ⅲ
|
18
tests/fixtures/uniq/sorted.txt
vendored
Normal file
18
tests/fixtures/uniq/sorted.txt
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
aaaaa ⅰ
|
||||
bbbbb ⅱ
|
||||
bbbbb ⅱ
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
ccccc ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
ddddd ⅲ
|
||||
eeeee ⅲ
|
||||
fffff ⅲ
|
||||
fffff ⅲ
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
ggggg ⅲ
|
||||
GGGGG ⅲ
|
||||
GGGGG ⅲ
|
95
tests/uniq.rs
Normal file
95
tests/uniq.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
use common::util::*;
|
||||
|
||||
static UTIL_NAME: &'static str = "uniq";
|
||||
|
||||
static INPUT: &'static str = "sorted.txt";
|
||||
static SKIP_CHARS: &'static str = "skip-chars.txt";
|
||||
static SKIP_FIELDS: &'static str = "skip-fields.txt";
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_stdin_default() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.run_piped_stdin(at.read(INPUT));
|
||||
assert_eq!(result.stdout, at.read("sorted-simple.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_default() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.arg(INPUT).run();
|
||||
assert_eq!(result.stdout, at.read("sorted-simple.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_counts() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["-c"]).run_piped_stdin(at.read(INPUT));
|
||||
assert_eq!(result.stdout, at.read("sorted-counts.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_skip_1_char() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["-s1"]).run_piped_stdin(at.read(SKIP_CHARS));
|
||||
assert_eq!(result.stdout, at.read("skip-1-char.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_skip_5_chars() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["-s5"]).run_piped_stdin(at.read(SKIP_CHARS));
|
||||
assert_eq!(result.stdout, at.read("skip-5-chars.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_skip_and_check_2_chars() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["-s3", "-w2"]).run_piped_stdin(at.read(SKIP_CHARS));
|
||||
assert_eq!(result.stdout, at.read("skip-3-check-2-chars.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_skip_1_field() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["-f2"]).run_piped_stdin(at.read(SKIP_FIELDS));
|
||||
assert_eq!(result.stdout, at.read("skip-2-fields.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_all_repeated() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["--all-repeated"]).run_piped_stdin(at.read(INPUT));
|
||||
assert_eq!(result.stdout, at.read("sorted-all-repeated.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_all_repeated_separate() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["--all-repeated", "separate"]).run_piped_stdin(at.read(INPUT));
|
||||
assert_eq!(result.stdout, at.read("sorted-all-repeated-separate.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_all_repeated_prepend() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["--all-repeated", "prepend"]).run_piped_stdin(at.read(INPUT));
|
||||
assert_eq!(result.stdout, at.read("sorted-all-repeated-prepend.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_unique_only() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["-u"]).run_piped_stdin(at.read(INPUT));
|
||||
assert_eq!(result.stdout, at.read("sorted-unique-only.expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_repeated_only() {
|
||||
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||
let result = ucmd.args(&["-d"]).run_piped_stdin(at.read(INPUT));
|
||||
assert_eq!(result.stdout, at.read("sorted-repeated-only.expected"));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue