Skip to content

Commit 2132d42

Browse files
authored
Merge pull request #289 from gcanat/naive_histo_diff
Histogram diff
2 parents 536319b + b5441e6 commit 2132d42

File tree

11 files changed

+691
-674
lines changed

11 files changed

+691
-674
lines changed

text/diff.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,11 @@ fn check_difference(args: Args) -> io::Result<DiffExitStatus> {
136136
let path2_is_file = fs::metadata(&path2)?.is_file();
137137

138138
if path1_is_file && path2_is_file {
139-
return FileDiff::file_diff(path1, path2, &format_options, None);
139+
FileDiff::file_diff(path1, path2, &format_options, None)
140140
} else if !path1_is_file && !path2_is_file {
141-
return DirDiff::dir_diff(path1, path2, &format_options, args.recurse);
141+
DirDiff::dir_diff(path1, path2, &format_options, args.recurse)
142142
} else {
143-
return FileDiff::file_dir_diff(path1, path2, &format_options);
143+
FileDiff::file_dir_diff(path1, path2, &format_options)
144144
}
145145
}
146146

text/diff_util/change.rs

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@ impl ChangeData {
88
pub fn new(ln1: usize, ln2: usize) -> Self {
99
Self { ln1, ln2 }
1010
}
11-
12-
pub fn ln1(&self) -> usize {
13-
self.ln1
14-
}
15-
16-
pub fn ln2(&self) -> usize {
17-
self.ln2
18-
}
1911
}
2012

2113
#[derive(Clone, Copy, Debug, Default, Hash)]
@@ -29,38 +21,6 @@ pub enum Change {
2921
}
3022

3123
impl Change {
32-
pub fn is_none(&self) -> bool {
33-
*self == Change::None
34-
}
35-
36-
pub fn is_unchanged(&self) -> bool {
37-
*self == Change::Unchanged(Default::default())
38-
}
39-
40-
pub fn is_insert(&self) -> bool {
41-
*self == Change::Insert(Default::default())
42-
}
43-
44-
pub fn is_delete(&self) -> bool {
45-
*self == Change::Delete(Default::default())
46-
}
47-
48-
pub fn is_substitute(&self) -> bool {
49-
*self == Change::Substitute(Default::default())
50-
}
51-
52-
/// returns (ln1,ln2)
53-
/// panics if self is None
54-
pub fn get_lns(&self) -> (usize, usize) {
55-
match self {
56-
Change::None => Default::default(),
57-
Change::Unchanged(data) => (data.ln1, data.ln2),
58-
Change::Insert(data) => (data.ln1, data.ln2),
59-
Change::Delete(data) => (data.ln1, data.ln2),
60-
Change::Substitute(data) => (data.ln1, data.ln2),
61-
}
62-
}
63-
6424
pub fn get_ln1(&self) -> usize {
6525
match self {
6626
Change::None => panic!("Change::None is not allowed in hunk."),
@@ -81,16 +41,3 @@ impl Change {
8141
}
8242
}
8343
}
84-
85-
impl PartialEq for Change {
86-
fn eq(&self, other: &Self) -> bool {
87-
match (self, other) {
88-
(Self::None, Self::None) => true,
89-
(Self::Unchanged(_), Self::Unchanged(_)) => true,
90-
(Self::Insert(_), Self::Insert(_)) => true,
91-
(Self::Delete(_), Self::Delete(_)) => true,
92-
(Self::Substitute(_), Self::Substitute(_)) => true,
93-
_ => false,
94-
}
95-
}
96-
}

text/diff_util/constants.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
pub const EXIT_STATUS_NO_DIFFERENCE: u8 = 0;
44
pub const EXIT_STATUS_DIFFERENCE: u8 = 1;
55
pub const EXIT_STATUS_TROUBLE: u8 = 2;
6-
pub const NO_NEW_LINE_AT_END_OF_FILE: &'static str = "\\ No newline at end of file";
7-
pub const COULD_NOT_UNWRAP_FILENAME: &'static str = "Could not unwrap filename!";
6+
pub const NO_NEW_LINE_AT_END_OF_FILE: &str = "\\ No newline at end of file";
7+
pub const COULD_NOT_UNWRAP_FILENAME: &str = "Could not unwrap filename!";
88
pub const UTF8_NOT_ALLOWED_BYTES: [u8; 26] = [
99
0, 1, 2, 3, 4, 5, 6, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30, 31,
1010
127,

text/diff_util/dir_diff.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<'a> DirDiff<'a> {
3838
let mut dir2: DirData = DirData::load(path2)?;
3939

4040
let mut dir_diff = DirDiff::new(&mut dir1, &mut dir2, format_options, recursive);
41-
return dir_diff.analyze();
41+
dir_diff.analyze()
4242
}
4343

4444
fn analyze(&mut self) -> io::Result<DiffExitStatus> {
@@ -48,16 +48,15 @@ impl<'a> DirDiff<'a> {
4848
let is_file = dir_data
4949
.files()
5050
.get_key_value(file_name)
51-
.expect(
52-
format!(
51+
.unwrap_or_else(|| {
52+
panic!(
5353
"Could not find file in {}",
5454
dir_data
5555
.path()
5656
.to_str()
5757
.unwrap_or(COULD_NOT_UNWRAP_FILENAME)
5858
)
59-
.as_str(),
60-
)
59+
})
6160
.1
6261
.file_type()?
6362
.is_file();
@@ -217,6 +216,6 @@ impl<'a> DirDiff<'a> {
217216
}
218217
}
219218

220-
return Ok(exit_status);
219+
Ok(exit_status)
221220
}
222221
}

text/diff_util/file_data.rs

Lines changed: 58 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,42 @@
1-
use std::{
2-
fs::File,
3-
io::{self, BufReader, Read},
4-
path::PathBuf,
5-
time::SystemTime,
6-
};
1+
use std::{fs::File, io, mem::take, path::PathBuf, str::from_utf8, time::SystemTime};
72

8-
use super::{change::Change, constants::COULD_NOT_UNWRAP_FILENAME};
3+
use super::constants::COULD_NOT_UNWRAP_FILENAME;
94

10-
pub struct FileData {
5+
#[derive(Debug)]
6+
pub struct FileData<'a> {
117
path: PathBuf,
12-
lines: Vec<String>,
13-
changes: Vec<Change>,
8+
lines: Vec<&'a str>,
149
modified: SystemTime,
1510
ends_with_newline: bool,
1611
}
1712

18-
impl FileData {
13+
impl<'a> FileData<'a> {
1914
pub fn ends_with_newline(&self) -> bool {
2015
self.ends_with_newline
2116
}
2217

23-
pub fn get_file(path: PathBuf) -> io::Result<Self> {
24-
let file = File::open(path.clone())?;
18+
pub fn get_file(
19+
path: PathBuf,
20+
lines: Vec<&'a str>,
21+
ends_with_newline: bool,
22+
) -> io::Result<Self> {
23+
let file = File::open(&path)?;
2524
let modified = file.metadata()?.modified()?;
26-
let mut buf_reader = BufReader::new(file);
27-
let mut content = String::new();
28-
buf_reader.read_to_string(&mut content)?;
2925

30-
let mut lines = content
31-
.lines()
32-
.map(|line| line.to_string())
33-
.collect::<Vec<String>>();
34-
35-
let ends_with_newline = content.ends_with("\n");
36-
37-
if ends_with_newline {
38-
lines.push(String::from(""));
39-
}
40-
41-
let changes = vec![Change::None; lines.len()];
42-
43-
let result = Self {
26+
Ok(Self {
4427
path,
4528
lines,
46-
changes,
4729
modified,
4830
ends_with_newline,
49-
};
50-
51-
Ok(result)
31+
})
5232
}
5333

54-
pub fn get_context_identifier(&self, change_index: usize) -> &str {
55-
match self.changes[change_index] {
56-
Change::None => " ",
57-
Change::Unchanged(_) => " ",
58-
Change::Insert(_) => "+",
59-
Change::Delete(_) => "-",
60-
Change::Substitute(_) => "!",
61-
}
62-
}
63-
64-
pub fn lines(&self) -> &Vec<String> {
34+
pub fn lines(&self) -> &Vec<&str> {
6535
&self.lines
6636
}
6737

68-
pub fn line(&self, index: usize) -> &String {
69-
&self.lines[index]
38+
pub fn line(&self, index: usize) -> &str {
39+
self.lines[index]
7040
}
7141

7242
pub fn modified(&self) -> SystemTime {
@@ -80,35 +50,54 @@ impl FileData {
8050
}
8151
}
8252

83-
return COULD_NOT_UNWRAP_FILENAME;
53+
COULD_NOT_UNWRAP_FILENAME
8454
}
8555

86-
pub fn set_change(&mut self, change: Change, index: usize) {
87-
self.changes[index] = change;
56+
pub fn path(&self) -> &str {
57+
self.path.to_str().unwrap_or(COULD_NOT_UNWRAP_FILENAME)
8858
}
59+
}
8960

90-
pub fn expected_changed_in_range(
91-
&self,
92-
start: usize,
93-
end: usize,
94-
expected_changes: &Vec<fn(&Change) -> bool>,
95-
) -> bool {
96-
for i in start..=end {
97-
for expected_change in expected_changes {
98-
if expected_change(&self.changes[i]) {
99-
return true;
100-
}
101-
}
102-
}
61+
pub struct LineReader<'a> {
62+
content: &'a [u8],
63+
ends_with_newline: bool,
64+
}
10365

104-
return false;
66+
impl<'a> LineReader<'a> {
67+
pub fn new(content: &'a [u8]) -> Self {
68+
let ends_with_newline = content.last() == Some(&b'\n');
69+
Self {
70+
content,
71+
ends_with_newline,
72+
}
10573
}
106-
107-
pub fn change(&self, index: usize) -> &Change {
108-
&self.changes[index]
74+
pub fn ends_with_newline(&self) -> bool {
75+
self.ends_with_newline
10976
}
77+
}
11078

111-
pub fn path(&self) -> &str {
112-
self.path.to_str().unwrap_or(COULD_NOT_UNWRAP_FILENAME)
79+
impl<'a> Iterator for LineReader<'a> {
80+
type Item = &'a str;
81+
82+
fn next(&mut self) -> Option<Self::Item> {
83+
let mut carriage = false;
84+
let mut iter = self.content.iter().enumerate();
85+
let mut line_len = loop {
86+
match iter.next() {
87+
Some((i, b'\n')) => break i + 1,
88+
None => {
89+
return (!self.content.is_empty()).then(|| {
90+
from_utf8(take(&mut self.content)).expect("Failed to convert to str")
91+
});
92+
}
93+
Some((_, &it)) => carriage = it == b'\r',
94+
}
95+
};
96+
let (line, rest) = self.content.split_at(line_len);
97+
if carriage {
98+
line_len -= 1;
99+
}
100+
self.content = rest;
101+
Some(from_utf8(&line[..line_len - 1]).expect("Failed to convert to str"))
113102
}
114103
}

0 commit comments

Comments
 (0)