mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +00:00
join: switch to clap
This commit is contained in:
parent
d28e09de04
commit
2a6d550f4b
2 changed files with 63 additions and 63 deletions
|
@ -9,7 +9,7 @@ name = "uu_join"
|
||||||
path = "join.rs"
|
path = "join.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
getopts = "0.2.14"
|
clap = "2.24.1"
|
||||||
uucore = { path="../uucore" }
|
uucore = { path="../uucore" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|
124
src/join/join.rs
124
src/join/join.rs
|
@ -9,7 +9,7 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern crate getopts;
|
extern crate clap;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
@ -17,6 +17,7 @@ extern crate uucore;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Lines, Stdin, stdin};
|
use std::io::{BufRead, BufReader, Lines, Stdin, stdin};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
static NAME: &'static str = "join";
|
static NAME: &'static str = "join";
|
||||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
@ -206,52 +207,59 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uumain(args: Vec<String>) -> i32 {
|
pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
|
let matches = App::new(NAME)
|
||||||
|
.version(VERSION)
|
||||||
|
.about(
|
||||||
|
"For each pair of input lines with identical join fields, write a line to
|
||||||
|
standard output. The default join field is the first, delimited by blanks.
|
||||||
|
|
||||||
|
When FILE1 or FILE2 (not both) is -, read standard input.")
|
||||||
|
.help_message("display this help and exit")
|
||||||
|
.version_message("display version and exit")
|
||||||
|
.arg(Arg::with_name("a")
|
||||||
|
.short("a")
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&["1", "2"])
|
||||||
|
.value_name("FILENUM")
|
||||||
|
.help("also print unpairable lines from file FILENUM, where
|
||||||
|
FILENUM is 1 or 2, corresponding to FILE1 or FILE2"))
|
||||||
|
.arg(Arg::with_name("i")
|
||||||
|
.short("i")
|
||||||
|
.long("ignore-case")
|
||||||
|
.help("ignore differences in case when comparing fields"))
|
||||||
|
.arg(Arg::with_name("j")
|
||||||
|
.short("j")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("FIELD")
|
||||||
|
.help("equivalent to '-1 FIELD -2 FIELD'"))
|
||||||
|
.arg(Arg::with_name("1")
|
||||||
|
.short("1")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("FIELD")
|
||||||
|
.help("join on this FIELD of file 1"))
|
||||||
|
.arg(Arg::with_name("2")
|
||||||
|
.short("2")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("FIELD")
|
||||||
|
.help("join on this FIELD of file 2"))
|
||||||
|
.arg(Arg::with_name("file1")
|
||||||
|
.required(true)
|
||||||
|
.value_name("FILE1")
|
||||||
|
.hidden(true))
|
||||||
|
.arg(Arg::with_name("file2")
|
||||||
|
.required(true)
|
||||||
|
.value_name("FILE2")
|
||||||
|
.hidden(true))
|
||||||
|
.get_matches_from(args);
|
||||||
|
|
||||||
|
let keys = parse_field_number(matches.value_of("j"));
|
||||||
|
let key1 = parse_field_number(matches.value_of("1"));
|
||||||
|
let key2 = parse_field_number(matches.value_of("2"));
|
||||||
|
|
||||||
let mut settings: Settings = Default::default();
|
let mut settings: Settings = Default::default();
|
||||||
let mut opts = getopts::Options::new();
|
settings.print_unpaired = match matches.value_of("a") {
|
||||||
|
|
||||||
opts.optflag("h", "help", "display this help and exit");
|
|
||||||
opts.optopt(
|
|
||||||
"a",
|
|
||||||
"",
|
|
||||||
"also print unpairable lines from file FILENUM, where FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
|
|
||||||
"FILENUM"
|
|
||||||
);
|
|
||||||
opts.optflag(
|
|
||||||
"i",
|
|
||||||
"ignore-case",
|
|
||||||
"ignore differences in case when comparing fields",
|
|
||||||
);
|
|
||||||
opts.optopt("j", "", "equivalent to '-1 FIELD -2 FIELD'", "FIELD");
|
|
||||||
opts.optopt("1", "", "join on this FIELD of file 1", "FIELD");
|
|
||||||
opts.optopt("2", "", "join on this FIELD of file 2", "FIELD");
|
|
||||||
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(f) => crash!(1, "Invalid options\n{}", f),
|
|
||||||
};
|
|
||||||
|
|
||||||
if matches.opt_present("help") {
|
|
||||||
let msg = format!(
|
|
||||||
"{0} {1}
|
|
||||||
Usage:
|
|
||||||
{0} [OPTION]... FILE1 FILE2
|
|
||||||
|
|
||||||
For each pair of input lines with identical join fields, write a line to
|
|
||||||
standard output. The default join field is the first, delimited by blanks.",
|
|
||||||
NAME,
|
|
||||||
VERSION
|
|
||||||
);
|
|
||||||
print!("{}", opts.usage(&msg));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let keys = parse_field_number(matches.opt_str("j"));
|
|
||||||
let key1 = parse_field_number(matches.opt_str("1"));
|
|
||||||
let key2 = parse_field_number(matches.opt_str("2"));
|
|
||||||
|
|
||||||
settings.print_unpaired = match matches.opt_str("a") {
|
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
match &value[..] {
|
match value {
|
||||||
"1" => FileNum::File1,
|
"1" => FileNum::File1,
|
||||||
"2" => FileNum::File2,
|
"2" => FileNum::File2,
|
||||||
value => crash!(1, "invalid file number: {}", value),
|
value => crash!(1, "invalid file number: {}", value),
|
||||||
|
@ -259,40 +267,32 @@ standard output. The default join field is the first, delimited by blanks.",
|
||||||
}
|
}
|
||||||
None => FileNum::None,
|
None => FileNum::None,
|
||||||
};
|
};
|
||||||
settings.ignore_case = matches.opt_present("ignore-case");
|
settings.ignore_case = matches.is_present("i");
|
||||||
settings.key1 = get_field_number(keys, key1);
|
settings.key1 = get_field_number(keys, key1);
|
||||||
settings.key2 = get_field_number(keys, key2);
|
settings.key2 = get_field_number(keys, key2);
|
||||||
|
|
||||||
let files = matches.free;
|
let file1 = matches.value_of("file1").unwrap();
|
||||||
let file_count = files.len();
|
let file2 = matches.value_of("file2").unwrap();
|
||||||
|
|
||||||
if file_count < 1 {
|
if file1 == "-" && file2 == "-" {
|
||||||
crash!(1, "missing operand");
|
|
||||||
} else if file_count < 2 {
|
|
||||||
crash!(1, "missing operand after '{}'", files[0]);
|
|
||||||
} else if file_count > 2 {
|
|
||||||
crash!(1, "extra operand '{}'", files[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if files[0] == "-" && files[1] == "-" {
|
|
||||||
crash!(1, "both files cannot be standard input");
|
crash!(1, "both files cannot be standard input");
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(files, &settings)
|
exec(file1, file2, &settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(files: Vec<String>, settings: &Settings) -> i32 {
|
fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
|
||||||
let stdin = stdin();
|
let stdin = stdin();
|
||||||
|
|
||||||
let mut state1 = State::new(
|
let mut state1 = State::new(
|
||||||
&files[0],
|
&file1,
|
||||||
&stdin,
|
&stdin,
|
||||||
settings.key1,
|
settings.key1,
|
||||||
settings.print_unpaired == FileNum::File1,
|
settings.print_unpaired == FileNum::File1,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut state2 = State::new(
|
let mut state2 = State::new(
|
||||||
&files[1],
|
&file2,
|
||||||
&stdin,
|
&stdin,
|
||||||
settings.key2,
|
settings.key2,
|
||||||
settings.print_unpaired == FileNum::File2,
|
settings.print_unpaired == FileNum::File2,
|
||||||
|
@ -349,7 +349,7 @@ fn get_field_number(keys: Option<usize>, key: Option<usize>) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the specified field string as a natural number and return it.
|
/// Parse the specified field string as a natural number and return it.
|
||||||
fn parse_field_number(value: Option<String>) -> Option<usize> {
|
fn parse_field_number(value: Option<&str>) -> Option<usize> {
|
||||||
match value {
|
match value {
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
match value.parse() {
|
match value.parse() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue