Skip to content

Commit e1f02bf

Browse files
author
Gunter Schmidt
committed
Merged sdiff logic from PR uutils#159.
This is now a good option parser and has basic sdiff compare functionality. This is a good base for further development.
1 parent d57b0c5 commit e1f02bf

File tree

6 files changed

+177
-64
lines changed

6 files changed

+177
-64
lines changed

Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/diff.rs

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@
44
// files that was distributed with this source code.
55

66
use crate::params::{parse_params, Format};
7-
use crate::utils::report_failure_to_read_input_file;
8-
use crate::{context_diff, ed_diff, normal_diff, side_diff, unified_diff};
7+
use crate::{context_diff, ed_diff, normal_diff, side_diff, unified_diff, utils};
98
use std::env::ArgsOs;
10-
use std::ffi::OsString;
11-
use std::fs;
12-
use std::io::{self, stdout, Read, Write};
9+
use std::io::{self, stdout, Write};
1310
use std::iter::Peekable;
1411
use std::process::{exit, ExitCode};
1512

@@ -40,35 +37,16 @@ pub fn main(opts: Peekable<ArgsOs>) -> ExitCode {
4037
return ExitCode::SUCCESS;
4138
}
4239

43-
// read files
44-
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
45-
if filepath == "-" {
46-
let mut content = Vec::new();
47-
io::stdin().read_to_end(&mut content).and(Ok(content))
48-
} else {
49-
fs::read(filepath)
50-
}
51-
}
52-
let mut io_error = false;
53-
let from_content = match read_file_contents(&params.from) {
54-
Ok(from_content) => from_content,
55-
Err(e) => {
56-
report_failure_to_read_input_file(&params.executable, &params.from, &e);
57-
io_error = true;
58-
vec![]
59-
}
60-
};
61-
let to_content = match read_file_contents(&params.to) {
62-
Ok(to_content) => to_content,
63-
Err(e) => {
64-
report_failure_to_read_input_file(&params.executable, &params.to, &e);
65-
io_error = true;
66-
vec![]
40+
let (from_content, to_content) = match utils::read_both_files(&params.from, &params.to) {
41+
Ok(contents) => contents,
42+
Err((filepath, error)) => {
43+
eprintln!(
44+
"{}",
45+
utils::format_failure_to_read_input_file(&params.executable, &filepath, &error)
46+
);
47+
return ExitCode::from(2);
6748
}
6849
};
69-
if io_error {
70-
return ExitCode::from(2);
71-
}
7250

7351
// run diff
7452
let result: Vec<u8> = match params.format {
@@ -81,7 +59,7 @@ pub fn main(opts: Peekable<ArgsOs>) -> ExitCode {
8159
}),
8260
Format::SideBySide => {
8361
let mut output = stdout().lock();
84-
side_diff::diff(&from_content, &to_content, &mut output, &params)
62+
side_diff::diff(&from_content, &to_content, &mut output, &(&params).into())
8563
}
8664
};
8765
if params.brief && !result.is_empty() {

src/sdiff.rs

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
//! This module holds the core compare logic of sdiff.
2-
mod params_sdiff;
3-
mod params_sdiff_def;
2+
pub mod params_sdiff;
3+
pub mod params_sdiff_def;
44

5-
use std::{env::ArgsOs, fmt::Display, iter::Peekable, process::ExitCode};
5+
use std::{
6+
env::ArgsOs,
7+
fmt::Display,
8+
io::{stdout, Write},
9+
iter::Peekable,
10+
process::ExitCode,
11+
};
612

7-
use crate::sdiff::{params_sdiff::ParamsSdiff, params_sdiff_def::ParamsSdiffOk};
13+
use crate::{
14+
sdiff::{params_sdiff::ParamsSdiff, params_sdiff_def::ParamsSdiffOk},
15+
side_diff, utils,
16+
};
817

918
pub const EXE_NAME: &str = "sdiff";
1019

@@ -59,22 +68,55 @@ pub enum SdiffOk {
5968
/// To centralize error messages and make it easier to use in a lib.
6069
#[derive(Debug, PartialEq)]
6170
pub enum SdiffError {
62-
// Dummy,
71+
OutputError(String),
72+
// (msg)
73+
ReadFileError(String),
6374
}
6475

6576
impl Display for SdiffError {
6677
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67-
// TODO Display errors
68-
write!(f, "Error message of Sdiff")
78+
match self {
79+
SdiffError::OutputError(msg) => write!(f, "{msg}"),
80+
SdiffError::ReadFileError(msg) => write!(f, "{msg}"),
81+
}
6982
}
7083
}
7184

7285
/// This is the main function to compare the files. \
7386
/// Files are limited to u64 bytes and u64 lines.
74-
pub fn sdiff(_params: &ParamsSdiff) -> Result<SdiffOk, SdiffError> {
75-
// TODO sdiff file compare logic
76-
// There seems to be a lot of similarity to diff, mainly a different output.
77-
println!("\nsdiff does not compare files yet.");
78-
println!("{:?} or {:?}?", SdiffOk::Different, SdiffOk::Equal);
79-
Ok(SdiffOk::Equal)
87+
/// TODO sdiff is missing a number of options, currently implemented:
88+
/// * expand_tabs
89+
/// * tabsize
90+
/// * width
91+
pub fn sdiff(params: &ParamsSdiff) -> Result<SdiffOk, SdiffError> {
92+
let (from_content, to_content) = match utils::read_both_files(&params.from, &params.to) {
93+
Ok(contents) => contents,
94+
Err((filepath, error)) => {
95+
let msg = utils::format_failure_to_read_input_file(
96+
&params.util.executable(),
97+
&filepath,
98+
&error,
99+
);
100+
return Err(SdiffError::ReadFileError(msg));
101+
}
102+
};
103+
104+
// run diff
105+
let mut output = stdout().lock();
106+
let result = side_diff::diff(&from_content, &to_content, &mut output, &params.into());
107+
108+
match std::io::stdout().write_all(&result) {
109+
Ok(_) => {
110+
if result.is_empty() {
111+
Ok(SdiffOk::Equal)
112+
} else {
113+
Ok(SdiffOk::Different)
114+
}
115+
}
116+
Err(e) => Err(SdiffError::OutputError(e.to_string())),
117+
}
118+
119+
// println!("\nsdiff does not compare files yet.");
120+
// println!("{:?} or {:?}?", SdiffOk::Different, SdiffOk::Equal);
121+
// Ok(SdiffOk::Equal)
80122
}

src/sdiff/params_sdiff.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
};
1010

1111
/// Holds the given command line arguments except "--version" and "--help".
12-
#[derive(Debug, Default, Clone, Eq, PartialEq)]
12+
#[derive(Debug, Clone, Eq, PartialEq)]
1313
pub struct ParamsSdiff {
1414
/// Identifier
1515
pub util: DiffUtility,
@@ -48,13 +48,13 @@ pub struct ParamsSdiff {
4848
/// -s, --suppress-common-lines do not output common lines
4949
pub suppress_common_lines: bool,
5050
/// --tabsize=NUM tab stops at every NUM (default 8) print columns
51-
pub tabsize: Option<usize>,
51+
pub tabsize: usize,
5252
/// -a, --text treat all files as text
5353
pub text: bool,
5454
/// -v, --version output version information and exit
5555
pub version: bool,
5656
/// -w, --width=NUM output at most NUM (default 130) print columns
57-
pub width: Option<usize>,
57+
pub width: usize,
5858
}
5959

6060
impl ParamsSdiff {
@@ -147,7 +147,7 @@ impl ParamsSdiff {
147147
Ok(w) => w,
148148
Err(_) => return Err(ParamsSdiffError::InvalidNumber(parsed_option.clone())),
149149
};
150-
self.tabsize = Some(t);
150+
self.tabsize = t;
151151

152152
Ok(t)
153153
}
@@ -158,12 +158,42 @@ impl ParamsSdiff {
158158
Ok(w) => w,
159159
Err(_) => return Err(ParamsSdiffError::InvalidNumber(parsed_option.clone())),
160160
};
161-
self.width = Some(w);
161+
self.width = w;
162162

163163
Ok(w)
164164
}
165165
}
166166

167+
impl Default for ParamsSdiff {
168+
fn default() -> Self {
169+
Self {
170+
util: DiffUtility::SDiff,
171+
from: Default::default(),
172+
to: Default::default(),
173+
diff_program: Default::default(),
174+
expand_tabs: Default::default(),
175+
help: Default::default(),
176+
ignore_all_space: Default::default(),
177+
ignore_blank_lines: Default::default(),
178+
ignore_case: Default::default(),
179+
ignore_matching_lines: Default::default(),
180+
ignore_space_change: Default::default(),
181+
ignore_tab_expansion: Default::default(),
182+
ignore_trailing_space: Default::default(),
183+
left_column: Default::default(),
184+
minimal: Default::default(),
185+
output: Default::default(),
186+
speed_large_files: Default::default(),
187+
strip_trailing_cr: Default::default(),
188+
suppress_common_lines: Default::default(),
189+
tabsize: 8,
190+
text: Default::default(),
191+
version: Default::default(),
192+
width: 130,
193+
}
194+
}
195+
}
196+
167197
// Usually assert is used like assert_eq(test result, expected result).
168198
#[cfg(test)]
169199
mod tests {
@@ -269,10 +299,10 @@ mod tests {
269299
speed_large_files: true,
270300
strip_trailing_cr: true,
271301
suppress_common_lines: true,
272-
tabsize: Some(2),
302+
tabsize: 2,
273303
text: true,
274304
version: false,
275-
width: Some(150),
305+
width: 150,
276306
};
277307
assert_eq!(
278308
parse(

src/side_diff.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use diff::Result;
88
use std::{io::Write, vec};
99
use unicode_width::UnicodeWidthStr;
1010

11-
use crate::params::Params;
11+
use crate::sdiff::params_sdiff::ParamsSdiff;
1212

1313
const GUTTER_WIDTH_MIN: usize = 3;
1414

@@ -98,6 +98,34 @@ impl Config {
9898
}
9999
}
100100

101+
/// Params for side_diff, so the functions can be used by multiple modules (diff and sdiff)
102+
#[derive(Default)]
103+
pub struct Params {
104+
pub expand_tabs: bool,
105+
pub tabsize: usize,
106+
pub width: usize,
107+
}
108+
109+
impl From<&crate::params::Params> for Params {
110+
fn from(param: &crate::params::Params) -> Self {
111+
Self {
112+
expand_tabs: param.expand_tabs,
113+
tabsize: param.tabsize,
114+
width: param.width,
115+
}
116+
}
117+
}
118+
119+
impl From<&ParamsSdiff> for Params {
120+
fn from(param: &ParamsSdiff) -> Self {
121+
Self {
122+
expand_tabs: param.expand_tabs,
123+
tabsize: param.tabsize,
124+
width: param.width,
125+
}
126+
}
127+
}
128+
101129
fn format_tabs_and_spaces<T: Write>(
102130
from: usize,
103131
to: usize,

src/utils.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
// files that was distributed with this source code.
55

66
use regex::Regex;
7-
use std::{ffi::OsString, io::Write};
7+
use std::io::{self, Error, Read, Write};
8+
use std::{ffi::OsString, fs};
89
use unicode_width::UnicodeWidthStr;
910

1011
/// Replace tabs by spaces in the input line.
@@ -87,15 +88,22 @@ pub fn format_failure_to_read_input_file(
8788
)
8889
}
8990

90-
pub fn report_failure_to_read_input_file(
91-
executable: &OsString,
92-
filepath: &OsString,
93-
error: &std::io::Error,
94-
) {
95-
eprintln!(
96-
"{}",
97-
format_failure_to_read_input_file(executable, filepath, error)
98-
);
91+
pub fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
92+
if filepath == "-" {
93+
let mut content = Vec::new();
94+
io::stdin().read_to_end(&mut content).and(Ok(content))
95+
} else {
96+
fs::read(filepath)
97+
}
98+
}
99+
100+
pub fn read_both_files(
101+
from: &OsString,
102+
to: &OsString,
103+
) -> Result<(Vec<u8>, Vec<u8>), (OsString, Error)> {
104+
let from_content = read_file_contents(from).map_err(|e| (from.clone(), e))?;
105+
let to_content = read_file_contents(to).map_err(|e| (to.clone(), e))?;
106+
Ok((from_content, to_content))
99107
}
100108

101109
#[cfg(test)]

0 commit comments

Comments
 (0)