mirror of
https://github.com/uutils/diffutils.git
synced 2026-06-10 15:48:59 -07:00
Take utility name as first parameter on diffutils
This is in preparation for adding the other diffutils commands, cmp, diff3, sdiff. We use a similar strategy to uutils/coreutils, with the single binary acting as one of the supported tools if called through a symlink with the appropriate name. When using the multi-tool binary directly, the utility needds to be the first parameter.
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
||||
// This file is part of the uutils diffutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use crate::params::{parse_params, Format};
|
||||
use crate::utils::report_failure_to_read_input_file;
|
||||
use crate::{context_diff, ed_diff, normal_diff, unified_diff};
|
||||
use std::env::ArgsOs;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::process::{exit, ExitCode};
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
pub(crate) fn main(opts: Peekable<ArgsOs>) -> ExitCode {
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
};
|
||||
if params.from == "-" && params.to == "-"
|
||||
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||
{
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
// read files
|
||||
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
|
||||
if filepath == "-" {
|
||||
let mut content = Vec::new();
|
||||
io::stdin().read_to_end(&mut content).and(Ok(content))
|
||||
} else {
|
||||
fs::read(filepath)
|
||||
}
|
||||
}
|
||||
let mut io_error = false;
|
||||
let from_content = match read_file_contents(¶ms.from) {
|
||||
Ok(from_content) => from_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.from, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
let to_content = match read_file_contents(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.to, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
if io_error {
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
}
|
||||
}
|
||||
+56
-98
@@ -3,15 +3,16 @@
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use crate::params::{parse_params, Format};
|
||||
use regex::Regex;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::process::{exit, ExitCode};
|
||||
use std::{
|
||||
env::ArgsOs,
|
||||
ffi::OsString,
|
||||
iter::Peekable,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
mod context_diff;
|
||||
mod diff;
|
||||
mod ed_diff;
|
||||
mod macros;
|
||||
mod normal_diff;
|
||||
@@ -19,103 +20,60 @@ mod params;
|
||||
mod unified_diff;
|
||||
mod utils;
|
||||
|
||||
fn report_failure_to_read_input_file(
|
||||
executable: &OsString,
|
||||
filepath: &OsString,
|
||||
error: &std::io::Error,
|
||||
) {
|
||||
// std::io::Error's display trait outputs "{detail} (os error {code})"
|
||||
// but we want only the {detail} (error string) part
|
||||
let error_code_re = Regex::new(r"\ \(os\ error\ \d+\)$").unwrap();
|
||||
eprintln!(
|
||||
"{}: {}: {}",
|
||||
executable.to_string_lossy(),
|
||||
filepath.to_string_lossy(),
|
||||
error_code_re.replace(error.to_string().as_str(), ""),
|
||||
);
|
||||
/// # Panics
|
||||
/// Panics if the binary path cannot be determined
|
||||
fn binary_path(args: &mut Peekable<ArgsOs>) -> PathBuf {
|
||||
match args.peek() {
|
||||
Some(ref s) if !s.is_empty() => PathBuf::from(s),
|
||||
_ => std::env::current_exe().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(binary_path: &Path) -> Option<&str> {
|
||||
binary_path.file_stem()?.to_str()
|
||||
}
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn usage(name: &str) {
|
||||
println!("{name} {VERSION} (multi-call binary)\n");
|
||||
println!("Usage: {name} [function [arguments...]]\n");
|
||||
println!("Currently defined functions:\n");
|
||||
println!(" diff\n");
|
||||
}
|
||||
|
||||
fn second_arg_error(name: &str) -> ! {
|
||||
println!("Expected utility name as second argument, got nothing.");
|
||||
usage(name);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
fn main() -> ExitCode {
|
||||
let opts = env::args_os();
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
let mut args = std::env::args_os().peekable();
|
||||
|
||||
let exe_path = binary_path(&mut args);
|
||||
let exe_name = name(&exe_path).unwrap_or_else(|| {
|
||||
usage("<unknown binary>");
|
||||
std::process::exit(1);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
};
|
||||
if params.from == "-" && params.to == "-"
|
||||
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||
{
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
// read files
|
||||
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
|
||||
if filepath == "-" {
|
||||
let mut content = Vec::new();
|
||||
io::stdin().read_to_end(&mut content).and(Ok(content))
|
||||
} else {
|
||||
fs::read(filepath)
|
||||
}
|
||||
}
|
||||
let mut io_error = false;
|
||||
let from_content = match read_file_contents(¶ms.from) {
|
||||
Ok(from_content) => from_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.from, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
let to_content = match read_file_contents(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.to, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
if io_error {
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
let util_name = if exe_name == "diffutils" {
|
||||
// Discard the item we peeked.
|
||||
let _ = args.next();
|
||||
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
args.peek()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| second_arg_error(exe_name))
|
||||
} else {
|
||||
OsString::from(exe_name)
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
|
||||
match util_name.to_str() {
|
||||
Some("diff") => diff::main(args),
|
||||
Some(name) => {
|
||||
usage(&format!("{}: utility not supported", name));
|
||||
ExitCode::from(1)
|
||||
}
|
||||
None => second_arg_error(exe_name),
|
||||
}
|
||||
}
|
||||
|
||||
+108
-28
@@ -1,4 +1,5 @@
|
||||
use std::ffi::OsString;
|
||||
use std::iter::Peekable;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use regex::Regex;
|
||||
@@ -41,8 +42,7 @@ impl Default for Params {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params, String> {
|
||||
let mut opts = opts.into_iter().peekable();
|
||||
pub fn parse_params<I: Iterator<Item = OsString>>(mut opts: Peekable<I>) -> Result<Params, String> {
|
||||
// parse CLI
|
||||
|
||||
let Some(executable) = opts.next() else {
|
||||
@@ -323,7 +323,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -336,6 +341,7 @@ mod tests {
|
||||
[os("diff"), os("--normal"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -350,7 +356,12 @@ mod tests {
|
||||
format: Format::Ed,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os(arg), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os(arg), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -368,7 +379,7 @@ mod tests {
|
||||
format: Format::Context,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
for args in [
|
||||
@@ -390,7 +401,7 @@ mod tests {
|
||||
context_count: 42,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -410,7 +421,7 @@ mod tests {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert!(parse_params(params.iter().map(|x| os(x))).is_err());
|
||||
assert!(parse_params(params.iter().map(|x| os(x)).peekable()).is_err());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
@@ -427,7 +438,7 @@ mod tests {
|
||||
format: Format::Unified,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
for args in [
|
||||
@@ -449,7 +460,7 @@ mod tests {
|
||||
context_count: 42,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -469,7 +480,7 @@ mod tests {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert!(parse_params(params.iter().map(|x| os(x))).is_err());
|
||||
assert!(parse_params(params.iter().map(|x| os(x)).peekable()).is_err());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
@@ -487,6 +498,7 @@ mod tests {
|
||||
[os("diff"), os("-u54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -502,6 +514,7 @@ mod tests {
|
||||
[os("diff"), os("-U54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -517,6 +530,7 @@ mod tests {
|
||||
[os("diff"), os("-U"), os("54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -532,6 +546,7 @@ mod tests {
|
||||
[os("diff"), os("-c54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -544,7 +559,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -554,7 +574,12 @@ mod tests {
|
||||
report_identical_files: true,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-s"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("-s"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -573,6 +598,7 @@ mod tests {
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -585,7 +611,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -595,7 +626,12 @@ mod tests {
|
||||
brief: true,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-q"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("-q"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -609,6 +645,7 @@ mod tests {
|
||||
[os("diff"), os("--brief"), os("foo"), os("bar"),]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -621,7 +658,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
for option in ["-t", "--expand-tabs"] {
|
||||
assert_eq!(
|
||||
@@ -636,6 +678,7 @@ mod tests {
|
||||
[os("diff"), os(option), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -649,7 +692,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -663,6 +711,7 @@ mod tests {
|
||||
[os("diff"), os("--tabsize=0"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -677,36 +726,42 @@ mod tests {
|
||||
[os("diff"), os("--tabsize=42"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize="), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=r2"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=-1"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=r2"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
@@ -718,6 +773,7 @@ mod tests {
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
@@ -730,7 +786,12 @@ mod tests {
|
||||
to: os("-h"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("--"), os("-g"), os("-h")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("--"), os("-g"), os("-h")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
@@ -742,7 +803,7 @@ mod tests {
|
||||
to: os("-"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("-")].iter().cloned())
|
||||
parse_params([os("diff"), os("foo"), os("-")].iter().cloned().peekable())
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -751,7 +812,7 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-"), os("bar")].iter().cloned())
|
||||
parse_params([os("diff"), os("-"), os("bar")].iter().cloned().peekable())
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -760,27 +821,45 @@ mod tests {
|
||||
to: os("-"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-"), os("-")].iter().cloned())
|
||||
parse_params([os("diff"), os("-"), os("-")].iter().cloned().peekable())
|
||||
);
|
||||
assert!(parse_params([os("diff"), os("foo"), os("bar"), os("-")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-"), os("-"), os("-")].iter().cloned()).is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("foo"), os("bar"), os("-")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("-"), os("-"), os("-")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
#[test]
|
||||
fn missing_arguments() {
|
||||
assert!(parse_params([os("diff")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("foo")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff")].iter().cloned().peekable()).is_err());
|
||||
assert!(parse_params([os("diff"), os("foo")].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn unknown_argument() {
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("-g"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(
|
||||
parse_params([os("diff"), os("-g"), os("foo"), os("bar")].iter().cloned()).is_err()
|
||||
parse_params([os("diff"), os("-g"), os("bar")].iter().cloned().peekable()).is_err()
|
||||
);
|
||||
assert!(parse_params([os("diff"), os("-g"), os("bar")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-g")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-g")].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn empty() {
|
||||
assert!(parse_params([].iter().cloned()).is_err());
|
||||
assert!(parse_params([].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn conflicting_output_styles() {
|
||||
@@ -797,6 +876,7 @@ mod tests {
|
||||
[os("diff"), os(arg1), os(arg2), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
+18
-1
@@ -3,8 +3,9 @@
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use std::io::Write;
|
||||
use std::{ffi::OsString, io::Write};
|
||||
|
||||
use regex::Regex;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// Replace tabs by spaces in the input line.
|
||||
@@ -71,6 +72,22 @@ pub fn get_modification_time(file_path: &str) -> String {
|
||||
modification_time
|
||||
}
|
||||
|
||||
pub fn report_failure_to_read_input_file(
|
||||
executable: &OsString,
|
||||
filepath: &OsString,
|
||||
error: &std::io::Error,
|
||||
) {
|
||||
// std::io::Error's display trait outputs "{detail} (os error {code})"
|
||||
// but we want only the {detail} (error string) part
|
||||
let error_code_re = Regex::new(r"\ \(os\ error\ \d+\)$").unwrap();
|
||||
eprintln!(
|
||||
"{}: {}: {}",
|
||||
executable.to_string_lossy(),
|
||||
filepath.to_string_lossy(),
|
||||
error_code_re.replace(error.to_string().as_str(), ""),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user