Reformat code with rustfmt
This commit is contained in:
+47
-37
@@ -5,46 +5,56 @@ use difflib::sequencematcher::SequenceMatcher;
|
|||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// unified_diff
|
// unified_diff
|
||||||
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
||||||
let diff = difflib::unified_diff(&first_text, &second_text, "Original", "Current",
|
let diff = difflib::unified_diff(&first_text,
|
||||||
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3);
|
&second_text,
|
||||||
for line in &diff {
|
"Original",
|
||||||
println!("{:?}", line);
|
"Current",
|
||||||
}
|
"2005-01-26 23:30:50",
|
||||||
|
"2010-04-02 10:20:52",
|
||||||
|
3);
|
||||||
|
for line in &diff {
|
||||||
|
println!("{:?}", line);
|
||||||
|
}
|
||||||
|
|
||||||
//context_diff
|
//context_diff
|
||||||
let diff = difflib::context_diff(&first_text, &second_text, "Original", "Current",
|
let diff = difflib::context_diff(&first_text,
|
||||||
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3);
|
&second_text,
|
||||||
for line in &diff {
|
"Original",
|
||||||
println!("{:?}", line);
|
"Current",
|
||||||
}
|
"2005-01-26 23:30:50",
|
||||||
|
"2010-04-02 10:20:52",
|
||||||
|
3);
|
||||||
|
for line in &diff {
|
||||||
|
println!("{:?}", line);
|
||||||
|
}
|
||||||
|
|
||||||
//get_close_matches
|
//get_close_matches
|
||||||
let words = vec!["ape", "apple", "peach", "puppy"];
|
let words = vec!["ape", "apple", "peach", "puppy"];
|
||||||
let result = difflib::get_close_matches("appel", words, 3, 0.6);
|
let result = difflib::get_close_matches("appel", words, 3, 0.6);
|
||||||
println!("{:?}", result);
|
println!("{:?}", result);
|
||||||
|
|
||||||
//Differ examples
|
//Differ examples
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let diff = differ.compare(&first_text, &second_text);
|
let diff = differ.compare(&first_text, &second_text);
|
||||||
for line in &diff {
|
for line in &diff {
|
||||||
println!("{:?}", line);
|
println!("{:?}", line);
|
||||||
}
|
}
|
||||||
|
|
||||||
//SequenceMatcher examples
|
//SequenceMatcher examples
|
||||||
let mut matcher = SequenceMatcher::new("one two three four", "zero one tree four");
|
let mut matcher = SequenceMatcher::new("one two three four", "zero one tree four");
|
||||||
let m = matcher.find_longest_match(0, 18, 0, 18);
|
let m = matcher.find_longest_match(0, 18, 0, 18);
|
||||||
println!("{:?}", m);
|
println!("{:?}", m);
|
||||||
let all_matches = matcher.get_matching_blocks();
|
let all_matches = matcher.get_matching_blocks();
|
||||||
println!("{:?}", all_matches);
|
println!("{:?}", all_matches);
|
||||||
let opcode = matcher.get_opcodes();
|
let opcode = matcher.get_opcodes();
|
||||||
println!("{:?}", opcode);
|
println!("{:?}", opcode);
|
||||||
let grouped_opcodes = matcher.get_grouped_opcodes(2);
|
let grouped_opcodes = matcher.get_grouped_opcodes(2);
|
||||||
println!("{:?}", grouped_opcodes);
|
println!("{:?}", grouped_opcodes);
|
||||||
let ratio = matcher.ratio();
|
let ratio = matcher.ratio();
|
||||||
println!("{:?}", ratio);
|
println!("{:?}", ratio);
|
||||||
matcher.set_seqs("aaaaa", "aaaab");
|
matcher.set_seqs("aaaaa", "aaaab");
|
||||||
println!("{:?}", matcher.ratio());
|
println!("{:?}", matcher.ratio());
|
||||||
}
|
}
|
||||||
|
|||||||
+276
-195
@@ -5,216 +5,297 @@ use std::cmp;
|
|||||||
|
|
||||||
pub struct Differ {
|
pub struct Differ {
|
||||||
pub line_junk: Option<fn(&str) -> bool>,
|
pub line_junk: Option<fn(&str) -> bool>,
|
||||||
pub char_junk: Option<fn(&str) -> bool>
|
pub char_junk: Option<fn(&str) -> bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Differ{
|
impl Differ {
|
||||||
pub fn new() -> Differ {
|
pub fn new() -> Differ {
|
||||||
Differ{
|
Differ {
|
||||||
line_junk: None,
|
line_junk: None,
|
||||||
char_junk: None
|
char_junk: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare<T: ?Sized + Sequence>(&self, first_sequence: &T, second_sequence: &T) -> Vec<String> {
|
pub fn compare<T: ?Sized + Sequence>(&self,
|
||||||
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
first_sequence: &T,
|
||||||
matcher.set_is_junk(self.line_junk);
|
second_sequence: &T)
|
||||||
let mut res = Vec::new();
|
-> Vec<String> {
|
||||||
for opcode in matcher.get_opcodes(){
|
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
||||||
let mut gen = Vec::new();
|
matcher.set_is_junk(self.line_junk);
|
||||||
match opcode.tag.as_ref() {
|
let mut res = Vec::new();
|
||||||
"replace" => { gen = self.fancy_replace(first_sequence, opcode.first_start, opcode.first_end,
|
for opcode in matcher.get_opcodes() {
|
||||||
second_sequence, opcode.second_start, opcode.second_end) },
|
let mut gen = Vec::new();
|
||||||
"delete" => { gen = self.dump("-", first_sequence, opcode.first_start, opcode.first_end) },
|
match opcode.tag.as_ref() {
|
||||||
"insert" => { gen = self.dump("+", second_sequence, opcode.second_start, opcode.second_end) },
|
"replace" => {
|
||||||
"equal" => { gen = self.dump(" ", first_sequence, opcode.first_start, opcode.first_end) },
|
gen = self.fancy_replace(first_sequence,
|
||||||
_ => {}
|
opcode.first_start,
|
||||||
}
|
opcode.first_end,
|
||||||
for i in gen {
|
second_sequence,
|
||||||
res.push(i);
|
opcode.second_start,
|
||||||
}
|
opcode.second_end)
|
||||||
}
|
}
|
||||||
res
|
"delete" => {
|
||||||
}
|
gen = self.dump("-", first_sequence, opcode.first_start, opcode.first_end)
|
||||||
|
}
|
||||||
|
"insert" => {
|
||||||
|
gen = self.dump("+", second_sequence, opcode.second_start, opcode.second_end)
|
||||||
|
}
|
||||||
|
"equal" => {
|
||||||
|
gen = self.dump(" ", first_sequence, opcode.first_start, opcode.first_end)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
for i in gen {
|
||||||
|
res.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
fn dump<T: ?Sized + Sequence>(&self, tag: &str, sequence: &T, start: usize, end: usize) -> Vec<String> {
|
fn dump<T: ?Sized + Sequence>(&self,
|
||||||
let mut res = Vec::new();
|
tag: &str,
|
||||||
for i in start..end {
|
sequence: &T,
|
||||||
match sequence.at_index(i) {
|
start: usize,
|
||||||
Some(s) => res.push(format!("{} {}", tag, s)),
|
end: usize)
|
||||||
None => {},
|
-> Vec<String> {
|
||||||
}
|
let mut res = Vec::new();
|
||||||
}
|
for i in start..end {
|
||||||
res
|
match sequence.at_index(i) {
|
||||||
}
|
Some(s) => res.push(format!("{} {}", tag, s)),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
fn plain_replace<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
|
fn plain_replace<T: ?Sized + Sequence>(&self,
|
||||||
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
|
first_sequence: &T,
|
||||||
if !(first_start < first_end && second_start < second_end){
|
first_start: usize,
|
||||||
return Vec::new();
|
first_end: usize,
|
||||||
}
|
second_sequence: &T,
|
||||||
let mut first;
|
second_start: usize,
|
||||||
let second;
|
second_end: usize)
|
||||||
if second_end - second_start < first_end - first_start{
|
-> Vec<String> {
|
||||||
first = self.dump("+", second_sequence, second_start, second_end);
|
if !(first_start < first_end && second_start < second_end) {
|
||||||
second = self.dump("-", first_sequence, first_start, first_end);
|
return Vec::new();
|
||||||
} else {
|
}
|
||||||
first = self.dump("-", first_sequence, first_start, first_end);
|
let mut first;
|
||||||
second = self.dump("+", second_sequence, second_start, second_end);
|
let second;
|
||||||
}
|
if second_end - second_start < first_end - first_start {
|
||||||
for s in second{
|
first = self.dump("+", second_sequence, second_start, second_end);
|
||||||
first.push(s);
|
second = self.dump("-", first_sequence, first_start, first_end);
|
||||||
}
|
} else {
|
||||||
first
|
first = self.dump("-", first_sequence, first_start, first_end);
|
||||||
}
|
second = self.dump("+", second_sequence, second_start, second_end);
|
||||||
|
}
|
||||||
|
for s in second {
|
||||||
|
first.push(s);
|
||||||
|
}
|
||||||
|
first
|
||||||
|
}
|
||||||
|
|
||||||
fn fancy_replace<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
|
fn fancy_replace<T: ?Sized + Sequence>(&self,
|
||||||
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
|
first_sequence: &T,
|
||||||
let mut res = Vec::new();
|
first_start: usize,
|
||||||
let (mut best_ratio, cutoff) = (0.74, 0.75);
|
first_end: usize,
|
||||||
let (mut best_i, mut best_j) = (0, 0);
|
second_sequence: &T,
|
||||||
let (mut second_sequence_str, mut first_sequence_str);
|
second_start: usize,
|
||||||
let mut eqi: Option<usize> = None;
|
second_end: usize)
|
||||||
let mut eqj: Option<usize> = None;
|
-> Vec<String> {
|
||||||
for j in second_start..second_end{
|
let mut res = Vec::new();
|
||||||
second_sequence_str = second_sequence.at_index(j).unwrap();
|
let (mut best_ratio, cutoff) = (0.74, 0.75);
|
||||||
for i in first_start..first_end{
|
let (mut best_i, mut best_j) = (0, 0);
|
||||||
first_sequence_str = first_sequence.at_index(i).unwrap();
|
let (mut second_sequence_str, mut first_sequence_str);
|
||||||
if first_sequence_str == second_sequence_str{
|
let mut eqi: Option<usize> = None;
|
||||||
if eqi.is_none(){
|
let mut eqj: Option<usize> = None;
|
||||||
eqi = Some(i);
|
for j in second_start..second_end {
|
||||||
eqj = Some(j);
|
second_sequence_str = second_sequence.at_index(j).unwrap();
|
||||||
}
|
for i in first_start..first_end {
|
||||||
continue;
|
first_sequence_str = first_sequence.at_index(i).unwrap();
|
||||||
}
|
if first_sequence_str == second_sequence_str {
|
||||||
let mut cruncher = SequenceMatcher::new(first_sequence_str, second_sequence_str);
|
if eqi.is_none() {
|
||||||
cruncher.set_is_junk(self.char_junk);
|
eqi = Some(i);
|
||||||
if cruncher.ratio() > best_ratio{
|
eqj = Some(j);
|
||||||
best_ratio = cruncher.ratio();
|
}
|
||||||
best_i = i;
|
continue;
|
||||||
best_j = j;
|
}
|
||||||
}
|
let mut cruncher = SequenceMatcher::new(first_sequence_str, second_sequence_str);
|
||||||
}
|
cruncher.set_is_junk(self.char_junk);
|
||||||
}
|
if cruncher.ratio() > best_ratio {
|
||||||
if best_ratio < cutoff{
|
best_ratio = cruncher.ratio();
|
||||||
if eqi.is_none(){
|
best_i = i;
|
||||||
res.extend(self.plain_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end).iter().cloned());
|
best_j = j;
|
||||||
return res
|
}
|
||||||
}
|
}
|
||||||
best_i = eqi.unwrap();
|
}
|
||||||
best_j = eqj.unwrap();
|
if best_ratio < cutoff {
|
||||||
} else {
|
if eqi.is_none() {
|
||||||
eqi = None;
|
res.extend(self.plain_replace(first_sequence,
|
||||||
}
|
first_start,
|
||||||
res.extend(self.fancy_helper(first_sequence, first_start, best_i, second_sequence, second_start, best_j).iter().cloned());
|
first_end,
|
||||||
let (first_element, second_element) = (first_sequence.at_index(best_i).unwrap(), second_sequence.at_index(best_j).unwrap());
|
second_sequence,
|
||||||
if eqi.is_none(){
|
second_start,
|
||||||
let (mut first_tag, mut second_tag) = (String::new(), String::new());
|
second_end)
|
||||||
let mut cruncher = SequenceMatcher::new(first_element, second_element);
|
.iter()
|
||||||
cruncher.set_is_junk(self.char_junk);
|
.cloned());
|
||||||
for opcode in &cruncher.get_opcodes(){
|
return res;
|
||||||
let (first_length, second_length) = (opcode.first_end - opcode.first_start, opcode.second_end - opcode.second_start);
|
}
|
||||||
match opcode.tag.as_ref() {
|
best_i = eqi.unwrap();
|
||||||
"replace" => {
|
best_j = eqj.unwrap();
|
||||||
first_tag.push_str(&str_with_similar_chars('^', first_length));
|
} else {
|
||||||
second_tag.push_str(&str_with_similar_chars('^', second_length));
|
eqi = None;
|
||||||
},
|
}
|
||||||
"delete" => {
|
res.extend(self.fancy_helper(first_sequence,
|
||||||
first_tag.push_str(&str_with_similar_chars('-', first_length));
|
first_start,
|
||||||
},
|
best_i,
|
||||||
"insert" => {
|
second_sequence,
|
||||||
second_tag.push_str(&str_with_similar_chars('+', second_length));
|
second_start,
|
||||||
},
|
best_j)
|
||||||
"equal" => {
|
.iter()
|
||||||
first_tag.push_str(&str_with_similar_chars(' ', first_length));
|
.cloned());
|
||||||
second_tag.push_str(&str_with_similar_chars(' ', second_length));
|
let (first_element, second_element) = (first_sequence.at_index(best_i).unwrap(),
|
||||||
},
|
second_sequence.at_index(best_j).unwrap());
|
||||||
_ => {}
|
if eqi.is_none() {
|
||||||
}
|
let (mut first_tag, mut second_tag) = (String::new(), String::new());
|
||||||
}
|
let mut cruncher = SequenceMatcher::new(first_element, second_element);
|
||||||
res.extend(self.qformat(&first_element, &second_element, &first_tag, &second_tag).iter().cloned());
|
cruncher.set_is_junk(self.char_junk);
|
||||||
} else {
|
for opcode in &cruncher.get_opcodes() {
|
||||||
let mut s = String::from(" ");
|
let (first_length, second_length) = (opcode.first_end - opcode.first_start,
|
||||||
s.push_str(&first_element);
|
opcode.second_end - opcode.second_start);
|
||||||
res.push(s);
|
match opcode.tag.as_ref() {
|
||||||
}
|
"replace" => {
|
||||||
res.extend(self.fancy_helper(first_sequence, best_i + 1, first_end, second_sequence, best_j + 1, second_end).iter().cloned());
|
first_tag.push_str(&str_with_similar_chars('^', first_length));
|
||||||
res
|
second_tag.push_str(&str_with_similar_chars('^', second_length));
|
||||||
}
|
}
|
||||||
|
"delete" => {
|
||||||
|
first_tag.push_str(&str_with_similar_chars('-', first_length));
|
||||||
|
}
|
||||||
|
"insert" => {
|
||||||
|
second_tag.push_str(&str_with_similar_chars('+', second_length));
|
||||||
|
}
|
||||||
|
"equal" => {
|
||||||
|
first_tag.push_str(&str_with_similar_chars(' ', first_length));
|
||||||
|
second_tag.push_str(&str_with_similar_chars(' ', second_length));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.extend(self.qformat(&first_element, &second_element, &first_tag, &second_tag)
|
||||||
|
.iter()
|
||||||
|
.cloned());
|
||||||
|
} else {
|
||||||
|
let mut s = String::from(" ");
|
||||||
|
s.push_str(&first_element);
|
||||||
|
res.push(s);
|
||||||
|
}
|
||||||
|
res.extend(self.fancy_helper(first_sequence,
|
||||||
|
best_i + 1,
|
||||||
|
first_end,
|
||||||
|
second_sequence,
|
||||||
|
best_j + 1,
|
||||||
|
second_end)
|
||||||
|
.iter()
|
||||||
|
.cloned());
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
fn fancy_helper<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
|
fn fancy_helper<T: ?Sized + Sequence>(&self,
|
||||||
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
|
first_sequence: &T,
|
||||||
let mut res = Vec::new();
|
first_start: usize,
|
||||||
if first_start < first_end{
|
first_end: usize,
|
||||||
if second_start < second_end{
|
second_sequence: &T,
|
||||||
res = self.fancy_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end);
|
second_start: usize,
|
||||||
} else {
|
second_end: usize)
|
||||||
res = self.dump("-", first_sequence, first_start, first_end);
|
-> Vec<String> {
|
||||||
}
|
let mut res = Vec::new();
|
||||||
} else if second_start < second_end {
|
if first_start < first_end {
|
||||||
res = self.dump("+", second_sequence, second_start, second_end);
|
if second_start < second_end {
|
||||||
}
|
res = self.fancy_replace(first_sequence,
|
||||||
res
|
first_start,
|
||||||
}
|
first_end,
|
||||||
|
second_sequence,
|
||||||
|
second_start,
|
||||||
|
second_end);
|
||||||
|
} else {
|
||||||
|
res = self.dump("-", first_sequence, first_start, first_end);
|
||||||
|
}
|
||||||
|
} else if second_start < second_end {
|
||||||
|
res = self.dump("+", second_sequence, second_start, second_end);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
fn qformat(&self, first_line: &str, second_line: &str, first_tags: &str, second_tags: &str) -> Vec<String> {
|
fn qformat(&self,
|
||||||
let mut res = Vec::new();
|
first_line: &str,
|
||||||
let mut first_tags = first_tags;
|
second_line: &str,
|
||||||
let mut second_tags = second_tags;
|
first_tags: &str,
|
||||||
let mut common = cmp::min(count_leading(first_line, '\t'), count_leading(second_line, '\t'));
|
second_tags: &str)
|
||||||
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
|
-> Vec<String> {
|
||||||
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
|
let mut res = Vec::new();
|
||||||
first_tags = first_tags.split_at(common).1.trim_right();
|
let mut first_tags = first_tags;
|
||||||
second_tags = second_tags.split_at(common).1.trim_right();
|
let mut second_tags = second_tags;
|
||||||
let mut s = String::from(format!("- {}", first_line));
|
let mut common = cmp::min(count_leading(first_line, '\t'),
|
||||||
res.push(s);
|
count_leading(second_line, '\t'));
|
||||||
if first_tags != ""{
|
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
|
||||||
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), first_tags));
|
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
|
||||||
res.push(s);
|
first_tags = first_tags.split_at(common).1.trim_right();
|
||||||
}
|
second_tags = second_tags.split_at(common).1.trim_right();
|
||||||
s = String::from(format!("+ {}", second_line));
|
let mut s = String::from(format!("- {}", first_line));
|
||||||
res.push(s);
|
res.push(s);
|
||||||
if second_tags != "" {
|
if first_tags != "" {
|
||||||
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), second_tags));
|
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), first_tags));
|
||||||
res.push(s);
|
res.push(s);
|
||||||
}
|
}
|
||||||
res
|
s = String::from(format!("+ {}", second_line));
|
||||||
}
|
res.push(s);
|
||||||
|
if second_tags != "" {
|
||||||
pub fn restore(delta: &Vec<String>, which: usize) -> Vec<String> {
|
s = String::from(format!("? {}{}\n",
|
||||||
if !(which == 1 || which == 2) {
|
str_with_similar_chars('\t', common),
|
||||||
panic!("Second parameter must be 1 or 2");
|
second_tags));
|
||||||
}
|
res.push(s);
|
||||||
let mut res = Vec::new();
|
}
|
||||||
let tag;
|
res
|
||||||
if which == 1 {
|
}
|
||||||
tag = "- ".to_string();
|
|
||||||
} else {
|
|
||||||
tag = "+ ".to_string();
|
|
||||||
}
|
|
||||||
let prefixes = vec![tag, " ".to_string()];
|
|
||||||
for line in delta {
|
|
||||||
for prefix in &prefixes {
|
|
||||||
if line.starts_with(prefix) {
|
|
||||||
res.push( line.split_at(2).1.to_string() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
|
pub fn restore(delta: &Vec<String>, which: usize) -> Vec<String> {
|
||||||
|
if !(which == 1 || which == 2) {
|
||||||
|
panic!("Second parameter must be 1 or 2");
|
||||||
|
}
|
||||||
|
let mut res = Vec::new();
|
||||||
|
let tag;
|
||||||
|
if which == 1 {
|
||||||
|
tag = "- ".to_string();
|
||||||
|
} else {
|
||||||
|
tag = "+ ".to_string();
|
||||||
|
}
|
||||||
|
let prefixes = vec![tag, " ".to_string()];
|
||||||
|
for line in delta {
|
||||||
|
for prefix in &prefixes {
|
||||||
|
if line.starts_with(prefix) {
|
||||||
|
res.push(line.split_at(2).1.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fancy_replace() {
|
fn test_fancy_replace() {
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let result = differ.fancy_replace(&vec!["abcDefghiJkl\n"], 0, 1, &vec!["abcdefGhijkl\n"], 0, 1).join("");
|
let result = differ.fancy_replace(&vec!["abcDefghiJkl\n"], 0, 1, &vec!["abcdefGhijkl\n"], 0, 1)
|
||||||
assert_eq!(result, "- abcDefghiJkl\n? ^ ^ ^\n+ abcdefGhijkl\n? ^ ^ ^\n");
|
.join("");
|
||||||
|
assert_eq!(result,
|
||||||
|
"- abcDefghiJkl\n? ^ ^ ^\n+ abcdefGhijkl\n? ^ ^ ^\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_qformat() {
|
fn test_qformat() {
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let result = differ.qformat("\tabcDefghiJkl\n", "\tabcdefGhijkl\n", " ^ ^ ^ ", " ^ ^ ^ ");
|
let result = differ.qformat("\tabcDefghiJkl\n",
|
||||||
assert_eq!(result, vec!["- \tabcDefghiJkl\n", "? \t ^ ^ ^\n", "+ \tabcdefGhijkl\n", "? \t ^ ^ ^\n"]);
|
"\tabcdefGhijkl\n",
|
||||||
|
" ^ ^ ^ ",
|
||||||
|
" ^ ^ ^ ");
|
||||||
|
assert_eq!(result,
|
||||||
|
vec!["- \tabcDefghiJkl\n", "? \t ^ ^ ^\n", "+ \tabcdefGhijkl\n", "? \t ^ ^ ^\n"]);
|
||||||
}
|
}
|
||||||
+133
-113
@@ -8,122 +8,142 @@ use sequencematcher::{SequenceMatcher, Sequence};
|
|||||||
use utils::{format_range_unified, format_range_context};
|
use utils::{format_range_unified, format_range_context};
|
||||||
|
|
||||||
|
|
||||||
pub fn get_close_matches<'a>(word: &str, possibilities: Vec<&'a str>, n: usize, cutoff: f32) -> Vec<&'a str> {
|
pub fn get_close_matches<'a>(word: &str,
|
||||||
if !(0.0 <= cutoff && cutoff <= 1.0) {
|
possibilities: Vec<&'a str>,
|
||||||
panic!("Cutoff must be greater than 0.0 and lower than 1.0");
|
n: usize,
|
||||||
}
|
cutoff: f32)
|
||||||
let mut res: Vec<(f32, &str)> = Vec::new();
|
-> Vec<&'a str> {
|
||||||
let mut matcher = SequenceMatcher::new("", word);
|
if !(0.0 <= cutoff && cutoff <= 1.0) {
|
||||||
for i in &possibilities {
|
panic!("Cutoff must be greater than 0.0 and lower than 1.0");
|
||||||
matcher.set_first_seq(i);
|
}
|
||||||
let ratio = matcher.ratio();
|
let mut res: Vec<(f32, &str)> = Vec::new();
|
||||||
if ratio >= cutoff {
|
let mut matcher = SequenceMatcher::new("", word);
|
||||||
res.push((ratio, i));
|
for i in &possibilities {
|
||||||
}
|
matcher.set_first_seq(i);
|
||||||
}
|
let ratio = matcher.ratio();
|
||||||
res.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
|
if ratio >= cutoff {
|
||||||
res.truncate(n);
|
res.push((ratio, i));
|
||||||
res.iter().map(|x| x.1).collect()
|
}
|
||||||
|
}
|
||||||
|
res.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
|
||||||
|
res.truncate(n);
|
||||||
|
res.iter().map(|x| x.1).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unified_diff<T: Sequence>(first_sequence: &T, second_sequence: &T, from_file: &str, to_file: &str,
|
pub fn unified_diff<T: Sequence>(first_sequence: &T,
|
||||||
from_file_date: &str, to_file_date: &str, n: usize) -> Vec<String> {
|
second_sequence: &T,
|
||||||
let mut res = Vec::new();
|
from_file: &str,
|
||||||
let lineterm = '\n';
|
to_file: &str,
|
||||||
let mut started = false;
|
from_file_date: &str,
|
||||||
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
to_file_date: &str,
|
||||||
for group in &matcher.get_grouped_opcodes(n) {
|
n: usize)
|
||||||
if !started {
|
-> Vec<String> {
|
||||||
started = true;
|
let mut res = Vec::new();
|
||||||
let from_date = format!("\t{}", from_file_date);
|
let lineterm = '\n';
|
||||||
let to_date = format!("\t{}", to_file_date);
|
let mut started = false;
|
||||||
res.push( format!("--- {}{}{}", from_file, from_date, lineterm) );
|
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
||||||
res.push( format!("+++ {}{}{}", to_file, to_date, lineterm) );
|
for group in &matcher.get_grouped_opcodes(n) {
|
||||||
}
|
if !started {
|
||||||
let (first, last) = (group.first().unwrap(), group.last().unwrap());
|
started = true;
|
||||||
let file1_range = format_range_unified(first.first_start, last.first_end);
|
let from_date = format!("\t{}", from_file_date);
|
||||||
let file2_range = format_range_unified(first.second_start, last.second_end);
|
let to_date = format!("\t{}", to_file_date);
|
||||||
res.push( format!("@@ -{} +{} @@{}", file1_range, file2_range, lineterm) );
|
res.push(format!("--- {}{}{}", from_file, from_date, lineterm));
|
||||||
for code in group {
|
res.push(format!("+++ {}{}{}", to_file, to_date, lineterm));
|
||||||
if code.tag == "equal" {
|
}
|
||||||
for i in code.first_start..code.first_end {
|
let (first, last) = (group.first().unwrap(), group.last().unwrap());
|
||||||
res.push( format!(" {}", first_sequence.at_index(i).unwrap()) );
|
let file1_range = format_range_unified(first.first_start, last.first_end);
|
||||||
}
|
let file2_range = format_range_unified(first.second_start, last.second_end);
|
||||||
continue;
|
res.push(format!("@@ -{} +{} @@{}", file1_range, file2_range, lineterm));
|
||||||
}
|
for code in group {
|
||||||
if code.tag == "replace" || code.tag == "delete" {
|
if code.tag == "equal" {
|
||||||
for i in code.first_start..code.first_end {
|
for i in code.first_start..code.first_end {
|
||||||
res.push( format!("-{}", first_sequence.at_index(i).unwrap()) );
|
res.push(format!(" {}", first_sequence.at_index(i).unwrap()));
|
||||||
}
|
}
|
||||||
}
|
continue;
|
||||||
if code.tag == "replace" || code.tag == "insert" {
|
}
|
||||||
for i in code.second_start..code.second_end {
|
if code.tag == "replace" || code.tag == "delete" {
|
||||||
res.push( format!("+{}", second_sequence.at_index(i).unwrap()) );
|
for i in code.first_start..code.first_end {
|
||||||
}
|
res.push(format!("-{}", first_sequence.at_index(i).unwrap()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if code.tag == "replace" || code.tag == "insert" {
|
||||||
res
|
for i in code.second_start..code.second_end {
|
||||||
|
res.push(format!("+{}", second_sequence.at_index(i).unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn context_diff<T: Sequence>(first_sequence: &T, second_sequence: &T, from_file: &str, to_file: &str,
|
pub fn context_diff<T: Sequence>(first_sequence: &T,
|
||||||
from_file_date: &str, to_file_date: &str, n: usize) -> Vec<String> {
|
second_sequence: &T,
|
||||||
let mut res = Vec::new();
|
from_file: &str,
|
||||||
let lineterm = '\n';
|
to_file: &str,
|
||||||
let mut prefix: HashMap<String, String> = HashMap::new();
|
from_file_date: &str,
|
||||||
prefix.insert(String::from("insert"), String::from("+ "));
|
to_file_date: &str,
|
||||||
prefix.insert(String::from("delete"), String::from("- "));
|
n: usize)
|
||||||
prefix.insert(String::from("replace"), String::from("! "));
|
-> Vec<String> {
|
||||||
prefix.insert(String::from("equal"), String::from(" "));
|
let mut res = Vec::new();
|
||||||
let mut started = false;
|
let lineterm = '\n';
|
||||||
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
let mut prefix: HashMap<String, String> = HashMap::new();
|
||||||
for group in &matcher.get_grouped_opcodes(n) {
|
prefix.insert(String::from("insert"), String::from("+ "));
|
||||||
if !started {
|
prefix.insert(String::from("delete"), String::from("- "));
|
||||||
started = true;
|
prefix.insert(String::from("replace"), String::from("! "));
|
||||||
let from_date = format!("\t{}", from_file_date);
|
prefix.insert(String::from("equal"), String::from(" "));
|
||||||
let to_date = format!("\t{}", to_file_date);
|
let mut started = false;
|
||||||
res.push( format!("*** {}{}{}", from_file, from_date, lineterm) );
|
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
||||||
res.push( format!("--- {}{}{}", to_file, to_date, lineterm) );
|
for group in &matcher.get_grouped_opcodes(n) {
|
||||||
}
|
if !started {
|
||||||
let (first, last) = (group.first().unwrap(), group.last().unwrap());
|
started = true;
|
||||||
res.push(format!("***************{}", lineterm));
|
let from_date = format!("\t{}", from_file_date);
|
||||||
let file1_range = format_range_context(first.first_start, last.first_end);
|
let to_date = format!("\t{}", to_file_date);
|
||||||
res.push(format!("*** {} ****{}", file1_range, lineterm));
|
res.push(format!("*** {}{}{}", from_file, from_date, lineterm));
|
||||||
let mut any = false;
|
res.push(format!("--- {}{}{}", to_file, to_date, lineterm));
|
||||||
for opcode in group {
|
}
|
||||||
if opcode.tag == "replace" || opcode.tag == "delete" {
|
let (first, last) = (group.first().unwrap(), group.last().unwrap());
|
||||||
any = true;
|
res.push(format!("***************{}", lineterm));
|
||||||
break;
|
let file1_range = format_range_context(first.first_start, last.first_end);
|
||||||
}
|
res.push(format!("*** {} ****{}", file1_range, lineterm));
|
||||||
}
|
let mut any = false;
|
||||||
if any {
|
for opcode in group {
|
||||||
for opcode in group {
|
if opcode.tag == "replace" || opcode.tag == "delete" {
|
||||||
if opcode.tag != "insert" {
|
any = true;
|
||||||
for i in opcode.first_start..opcode.first_end {
|
break;
|
||||||
res.push( format!("{}{}", prefix.get(&opcode.tag).unwrap(), first_sequence.at_index(i).unwrap()) );
|
}
|
||||||
}
|
}
|
||||||
}
|
if any {
|
||||||
}
|
for opcode in group {
|
||||||
}
|
if opcode.tag != "insert" {
|
||||||
let file2_range = format_range_context(first.second_start, last.second_end);
|
for i in opcode.first_start..opcode.first_end {
|
||||||
res.push(format!("--- {} ----{}", file2_range, lineterm));
|
res.push(format!("{}{}",
|
||||||
any = false;
|
prefix.get(&opcode.tag).unwrap(),
|
||||||
for opcode in group {
|
first_sequence.at_index(i).unwrap()));
|
||||||
if opcode.tag == "replace" || opcode.tag == "insert" {
|
}
|
||||||
any = true;
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
let file2_range = format_range_context(first.second_start, last.second_end);
|
||||||
if any {
|
res.push(format!("--- {} ----{}", file2_range, lineterm));
|
||||||
for opcode in group {
|
any = false;
|
||||||
if opcode.tag != "delete" {
|
for opcode in group {
|
||||||
for i in opcode.second_start..opcode.second_end {
|
if opcode.tag == "replace" || opcode.tag == "insert" {
|
||||||
res.push( format!("{}{}", prefix.get(&opcode.tag).unwrap(), second_sequence.at_index(i).unwrap()) );
|
any = true;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if any {
|
||||||
}
|
for opcode in group {
|
||||||
res
|
if opcode.tag != "delete" {
|
||||||
|
for i in opcode.second_start..opcode.second_end {
|
||||||
|
res.push(format!("{}{}",
|
||||||
|
prefix.get(&opcode.tag).unwrap(),
|
||||||
|
second_sequence.at_index(i).unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
+313
-294
@@ -4,74 +4,77 @@ use utils::calculate_ratio;
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
pub struct Match{
|
pub struct Match {
|
||||||
pub first_start: usize,
|
pub first_start: usize,
|
||||||
pub second_start: usize,
|
pub second_start: usize,
|
||||||
pub size: usize
|
pub size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Match{
|
impl Match {
|
||||||
fn new(first_start: usize, second_start: usize, size: usize) -> Match {
|
fn new(first_start: usize, second_start: usize, size: usize) -> Match {
|
||||||
Match{
|
Match {
|
||||||
first_start: first_start,
|
first_start: first_start,
|
||||||
second_start: second_start,
|
second_start: second_start,
|
||||||
size: size
|
size: size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Opcode{
|
pub struct Opcode {
|
||||||
pub tag: String,
|
pub tag: String,
|
||||||
pub first_start: usize,
|
pub first_start: usize,
|
||||||
pub first_end: usize,
|
pub first_end: usize,
|
||||||
pub second_start: usize,
|
pub second_start: usize,
|
||||||
pub second_end: usize
|
pub second_end: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opcode{
|
impl Opcode {
|
||||||
fn new(tag: String, first_start: usize, first_end: usize, second_start: usize, second_end: usize) -> Opcode {
|
fn new(tag: String,
|
||||||
Opcode{
|
first_start: usize,
|
||||||
tag: tag,
|
first_end: usize,
|
||||||
first_start: first_start,
|
second_start: usize,
|
||||||
first_end: first_end,
|
second_end: usize)
|
||||||
second_start: second_start,
|
-> Opcode {
|
||||||
second_end: second_end
|
Opcode {
|
||||||
}
|
tag: tag,
|
||||||
}
|
first_start: first_start,
|
||||||
|
first_end: first_end,
|
||||||
|
second_start: second_start,
|
||||||
|
second_end: second_end,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Sequence: Debug {
|
pub trait Sequence: Debug {
|
||||||
fn len(&self) -> usize;
|
fn len(&self) -> usize;
|
||||||
fn at_index(&self, index: usize) -> Option<&str>;
|
fn at_index(&self, index: usize) -> Option<&str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sequence for str {
|
impl Sequence for str {
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.len()
|
self.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn at_index(&self, index: usize) -> Option<&str> {
|
fn at_index(&self, index: usize) -> Option<&str> {
|
||||||
if index > self.len(){
|
if index > self.len() {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
unsafe{
|
unsafe { Some(self.slice_unchecked(index, index + 1)) }
|
||||||
Some(self.slice_unchecked(index, index + 1))
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sequence for Vec<&'a str> {
|
impl<'a> Sequence for Vec<&'a str> {
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.len()
|
self.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn at_index(&self, index: usize) -> Option<&str> {
|
fn at_index(&self, index: usize) -> Option<&str> {
|
||||||
if index < self.len() {
|
if index < self.len() {
|
||||||
return Some(self[index])
|
return Some(self[index]);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sequence for String {
|
impl Sequence for String {
|
||||||
@@ -100,262 +103,278 @@ impl<'a> Sequence for Vec<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SequenceMatcher<'a, T: 'a + ?Sized + Sequence>{
|
pub struct SequenceMatcher<'a, T: 'a + ?Sized + Sequence> {
|
||||||
first_sequence: &'a T,
|
first_sequence: &'a T,
|
||||||
second_sequence: &'a T,
|
second_sequence: &'a T,
|
||||||
matching_blocks: Option<Vec<Match>>,
|
matching_blocks: Option<Vec<Match>>,
|
||||||
opcodes: Option<Vec<Opcode>>,
|
opcodes: Option<Vec<Opcode>>,
|
||||||
is_junk: Option<fn(&str) -> bool>,
|
is_junk: Option<fn(&str) -> bool>,
|
||||||
second_sequence_elements: HashMap<&'a str, Vec<usize>>,
|
second_sequence_elements: HashMap<&'a str, Vec<usize>>,
|
||||||
second_sequence_popular: Vec<&'a str>
|
second_sequence_popular: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
|
impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
||||||
pub fn new(first_sequence: &'a T, second_sequence: &'a T) -> SequenceMatcher<'a, T> {
|
pub fn new(first_sequence: &'a T, second_sequence: &'a T) -> SequenceMatcher<'a, T> {
|
||||||
let mut matcher =
|
let mut matcher = SequenceMatcher {
|
||||||
SequenceMatcher{
|
first_sequence: first_sequence,
|
||||||
first_sequence: first_sequence,
|
second_sequence: second_sequence,
|
||||||
second_sequence: second_sequence,
|
matching_blocks: None,
|
||||||
matching_blocks: None,
|
opcodes: None,
|
||||||
opcodes: None,
|
is_junk: None,
|
||||||
is_junk: None,
|
second_sequence_elements: HashMap::new(),
|
||||||
second_sequence_elements: HashMap::new(),
|
second_sequence_popular: Vec::new(),
|
||||||
second_sequence_popular: Vec::new()
|
};
|
||||||
};
|
matcher.set_seqs(first_sequence, second_sequence);
|
||||||
matcher.set_seqs(first_sequence, second_sequence);
|
matcher
|
||||||
matcher
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_is_junk(&mut self, is_junk: Option<fn(&str) -> bool>) {
|
pub fn set_is_junk(&mut self, is_junk: Option<fn(&str) -> bool>) {
|
||||||
self.is_junk = is_junk;
|
self.is_junk = is_junk;
|
||||||
self.set_second_seq(self.second_sequence);
|
self.set_second_seq(self.second_sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_seqs(&mut self, first_sequence: &'a T, second_sequence: &'a T) {
|
pub fn set_seqs(&mut self, first_sequence: &'a T, second_sequence: &'a T) {
|
||||||
self.set_first_seq(first_sequence);
|
self.set_first_seq(first_sequence);
|
||||||
self.set_second_seq(second_sequence);
|
self.set_second_seq(second_sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_first_seq(&mut self, sequence: &'a T) {
|
pub fn set_first_seq(&mut self, sequence: &'a T) {
|
||||||
self.first_sequence = sequence;
|
self.first_sequence = sequence;
|
||||||
self.matching_blocks = None;
|
self.matching_blocks = None;
|
||||||
self.opcodes = None;
|
self.opcodes = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_second_seq(&mut self, sequence: &'a T) {
|
pub fn set_second_seq(&mut self, sequence: &'a T) {
|
||||||
self.second_sequence = sequence;
|
self.second_sequence = sequence;
|
||||||
self.matching_blocks = None;
|
self.matching_blocks = None;
|
||||||
self.opcodes = None;
|
self.opcodes = None;
|
||||||
self.chain_second_seq();
|
self.chain_second_seq();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chain_second_seq(&mut self) {
|
fn chain_second_seq(&mut self) {
|
||||||
let second_sequence = self.second_sequence;
|
let second_sequence = self.second_sequence;
|
||||||
let mut second_sequence_elements = HashMap::new();
|
let mut second_sequence_elements = HashMap::new();
|
||||||
for i in 0..second_sequence.len() {
|
for i in 0..second_sequence.len() {
|
||||||
let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap()).or_insert(Vec::new());
|
let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap())
|
||||||
counter.push(i);
|
.or_insert(Vec::new());
|
||||||
}
|
counter.push(i);
|
||||||
if self.is_junk.is_some() {
|
}
|
||||||
let mut junk = Vec::new();
|
if self.is_junk.is_some() {
|
||||||
for element in second_sequence_elements.keys() {
|
let mut junk = Vec::new();
|
||||||
if (self.is_junk.unwrap())(element) {
|
for element in second_sequence_elements.keys() {
|
||||||
junk.push(element.clone());
|
if (self.is_junk.unwrap())(element) {
|
||||||
}
|
junk.push(element.clone());
|
||||||
}
|
}
|
||||||
for element in &junk {
|
}
|
||||||
second_sequence_elements.remove(element);
|
for element in &junk {
|
||||||
}
|
second_sequence_elements.remove(element);
|
||||||
}
|
}
|
||||||
let mut popular = Vec::new();
|
}
|
||||||
let len = second_sequence.len();
|
let mut popular = Vec::new();
|
||||||
if len >= 200 {
|
let len = second_sequence.len();
|
||||||
let test_len = (len as f32 / 100.0).floor() as usize + 1;
|
if len >= 200 {
|
||||||
for (element, indexes) in second_sequence_elements.iter() {
|
let test_len = (len as f32 / 100.0).floor() as usize + 1;
|
||||||
if indexes.len() > test_len {
|
for (element, indexes) in second_sequence_elements.iter() {
|
||||||
popular.push(element.clone());
|
if indexes.len() > test_len {
|
||||||
}
|
popular.push(element.clone());
|
||||||
}
|
}
|
||||||
for element in &popular {
|
}
|
||||||
second_sequence_elements.remove(element);
|
for element in &popular {
|
||||||
}
|
second_sequence_elements.remove(element);
|
||||||
}
|
}
|
||||||
self.second_sequence_elements = second_sequence_elements;
|
}
|
||||||
self.second_sequence_popular = popular;
|
self.second_sequence_elements = second_sequence_elements;
|
||||||
}
|
self.second_sequence_popular = popular;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find_longest_match(&self, first_start: usize, first_end: usize, second_start: usize, second_end: usize) -> Match {
|
pub fn find_longest_match(&self,
|
||||||
let first_sequence = &self.first_sequence;
|
first_start: usize,
|
||||||
let second_sequence = &self.second_sequence;
|
first_end: usize,
|
||||||
let second_sequence_elements = &self.second_sequence_elements;
|
second_start: usize,
|
||||||
let (mut best_i, mut best_j, mut best_size) = (first_start, second_start, 0);
|
second_end: usize)
|
||||||
let mut j2len: HashMap<usize, usize> = HashMap::new();
|
-> Match {
|
||||||
for i in first_start..first_end {
|
let first_sequence = &self.first_sequence;
|
||||||
let mut new_j2len: HashMap<usize, usize> = HashMap::new();
|
let second_sequence = &self.second_sequence;
|
||||||
match second_sequence_elements.get(first_sequence.at_index(i).unwrap()) {
|
let second_sequence_elements = &self.second_sequence_elements;
|
||||||
Some(indexes) => {
|
let (mut best_i, mut best_j, mut best_size) = (first_start, second_start, 0);
|
||||||
for j in indexes {
|
let mut j2len: HashMap<usize, usize> = HashMap::new();
|
||||||
let j = j.clone();
|
for i in first_start..first_end {
|
||||||
if j < second_start {
|
let mut new_j2len: HashMap<usize, usize> = HashMap::new();
|
||||||
continue;
|
match second_sequence_elements.get(first_sequence.at_index(i).unwrap()) {
|
||||||
};
|
Some(indexes) => {
|
||||||
if j >= second_end {
|
for j in indexes {
|
||||||
break;
|
let j = j.clone();
|
||||||
};
|
if j < second_start {
|
||||||
let mut size = 0;
|
continue;
|
||||||
if j > 0 {
|
};
|
||||||
match j2len.get(&(j-1)){
|
if j >= second_end {
|
||||||
Some(k) => {
|
break;
|
||||||
size = k.clone();
|
};
|
||||||
},
|
let mut size = 0;
|
||||||
None => {}
|
if j > 0 {
|
||||||
}
|
match j2len.get(&(j - 1)) {
|
||||||
}
|
Some(k) => {
|
||||||
size += 1;
|
size = k.clone();
|
||||||
new_j2len.insert(j, size);
|
}
|
||||||
if size > best_size {
|
None => {}
|
||||||
best_i = i + 1 - size;
|
}
|
||||||
best_j = j + 1 - size;
|
}
|
||||||
best_size = size;
|
size += 1;
|
||||||
}
|
new_j2len.insert(j, size);
|
||||||
}
|
if size > best_size {
|
||||||
},
|
best_i = i + 1 - size;
|
||||||
None => {},
|
best_j = j + 1 - size;
|
||||||
}
|
best_size = size;
|
||||||
j2len = new_j2len;
|
}
|
||||||
}
|
}
|
||||||
for _ in 0..2 {
|
}
|
||||||
while best_i > first_start && best_j > second_start &&
|
None => {}
|
||||||
first_sequence.at_index(best_i - 1) == second_sequence.at_index(best_j-1){
|
}
|
||||||
best_i = best_i - 1;
|
j2len = new_j2len;
|
||||||
best_j = best_j - 1;
|
}
|
||||||
best_size = best_size + 1;
|
for _ in 0..2 {
|
||||||
}
|
while best_i > first_start && best_j > second_start &&
|
||||||
while best_i + best_size < first_end && best_j + best_size < second_end &&
|
first_sequence.at_index(best_i - 1) == second_sequence.at_index(best_j - 1) {
|
||||||
first_sequence.at_index(best_i + best_size) == second_sequence.at_index(best_j + best_size) {
|
best_i = best_i - 1;
|
||||||
best_size += 1;
|
best_j = best_j - 1;
|
||||||
}
|
best_size = best_size + 1;
|
||||||
}
|
}
|
||||||
Match::new(best_i, best_j, best_size)
|
while best_i + best_size < first_end && best_j + best_size < second_end &&
|
||||||
}
|
first_sequence.at_index(best_i + best_size) ==
|
||||||
|
second_sequence.at_index(best_j + best_size) {
|
||||||
|
best_size += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Match::new(best_i, best_j, best_size)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_matching_blocks(&mut self) -> Vec<Match> {
|
pub fn get_matching_blocks(&mut self) -> Vec<Match> {
|
||||||
if self.matching_blocks.as_ref().is_some(){
|
if self.matching_blocks.as_ref().is_some() {
|
||||||
return self.matching_blocks.as_ref().unwrap().clone()
|
return self.matching_blocks.as_ref().unwrap().clone();
|
||||||
}
|
}
|
||||||
let (first_length, second_length) = (self.first_sequence.len(), self.second_sequence.len());
|
let (first_length, second_length) = (self.first_sequence.len(), self.second_sequence.len());
|
||||||
let mut matches = Vec::new();
|
let mut matches = Vec::new();
|
||||||
let mut queue = vec![(0, first_length, 0, second_length)];
|
let mut queue = vec![(0, first_length, 0, second_length)];
|
||||||
while !queue.is_empty(){
|
while !queue.is_empty() {
|
||||||
let (first_start, first_end, second_start, second_end) = queue.pop().unwrap();
|
let (first_start, first_end, second_start, second_end) = queue.pop().unwrap();
|
||||||
let m = self.find_longest_match(first_start, first_end, second_start, second_end);
|
let m = self.find_longest_match(first_start, first_end, second_start, second_end);
|
||||||
match m.size {
|
match m.size {
|
||||||
0 => {},
|
0 => {}
|
||||||
_ => {
|
_ => {
|
||||||
if first_start < m.first_start && second_start < m.second_start{
|
if first_start < m.first_start && second_start < m.second_start {
|
||||||
queue.push((first_start, m.first_start, second_start, m.second_start));
|
queue.push((first_start, m.first_start, second_start, m.second_start));
|
||||||
}
|
}
|
||||||
if m.first_start + m.size < first_end && m.second_start + m.size < second_end{
|
if m.first_start + m.size < first_end && m.second_start + m.size < second_end {
|
||||||
queue.push((m.first_start + m.size, first_end, m.second_start + m.size, second_end));
|
queue.push((m.first_start + m.size,
|
||||||
}
|
first_end,
|
||||||
matches.push(m);
|
m.second_start + m.size,
|
||||||
},
|
second_end));
|
||||||
}
|
}
|
||||||
}
|
matches.push(m);
|
||||||
matches.sort_by(|a, b| a.cmp(b));
|
}
|
||||||
let (mut first_start, mut second_start, mut size) = (0, 0, 0);
|
}
|
||||||
let mut non_adjacent = Vec::new();
|
}
|
||||||
for m in &matches{
|
matches.sort_by(|a, b| a.cmp(b));
|
||||||
if first_start + size == m.first_start && second_start + size == m.second_start{
|
let (mut first_start, mut second_start, mut size) = (0, 0, 0);
|
||||||
size += m.size
|
let mut non_adjacent = Vec::new();
|
||||||
}
|
for m in &matches {
|
||||||
else {
|
if first_start + size == m.first_start && second_start + size == m.second_start {
|
||||||
if size != 0{
|
size += m.size
|
||||||
non_adjacent.push(Match::new(first_start, second_start, size));
|
} else {
|
||||||
}
|
if size != 0 {
|
||||||
first_start = m.first_start;
|
non_adjacent.push(Match::new(first_start, second_start, size));
|
||||||
second_start = m.second_start;
|
}
|
||||||
size = m.size;
|
first_start = m.first_start;
|
||||||
}
|
second_start = m.second_start;
|
||||||
}
|
size = m.size;
|
||||||
if size != 0{
|
}
|
||||||
non_adjacent.push(Match::new(first_start, second_start, size));
|
}
|
||||||
}
|
if size != 0 {
|
||||||
non_adjacent.push(Match::new(first_length, second_length, 0));
|
non_adjacent.push(Match::new(first_start, second_start, size));
|
||||||
self.matching_blocks = Some(non_adjacent);
|
}
|
||||||
self.matching_blocks.as_ref().unwrap().clone()
|
non_adjacent.push(Match::new(first_length, second_length, 0));
|
||||||
}
|
self.matching_blocks = Some(non_adjacent);
|
||||||
|
self.matching_blocks.as_ref().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_opcodes(&mut self) -> Vec<Opcode>{
|
pub fn get_opcodes(&mut self) -> Vec<Opcode> {
|
||||||
if self.opcodes.as_ref().is_some(){
|
if self.opcodes.as_ref().is_some() {
|
||||||
return self.opcodes.as_ref().unwrap().clone()
|
return self.opcodes.as_ref().unwrap().clone();
|
||||||
}
|
}
|
||||||
let mut opcodes = Vec::new();
|
let mut opcodes = Vec::new();
|
||||||
let (mut i, mut j) = (0, 0);
|
let (mut i, mut j) = (0, 0);
|
||||||
for m in self.get_matching_blocks(){
|
for m in self.get_matching_blocks() {
|
||||||
let mut tag = String::new();
|
let mut tag = String::new();
|
||||||
if i < m.first_start && j < m.second_start{
|
if i < m.first_start && j < m.second_start {
|
||||||
tag = String::from("replace");
|
tag = String::from("replace");
|
||||||
}
|
} else if i < m.first_start {
|
||||||
else if i < m.first_start{
|
tag = String::from("delete");
|
||||||
tag = String::from("delete");
|
} else if j < m.second_start {
|
||||||
}
|
tag = String::from("insert");
|
||||||
else if j < m.second_start{
|
}
|
||||||
tag = String::from("insert");
|
if !tag.is_empty() {
|
||||||
}
|
opcodes.push(Opcode::new(tag, i, m.first_start, j, m.second_start));
|
||||||
if !tag.is_empty(){
|
}
|
||||||
opcodes.push( Opcode::new(tag, i, m.first_start, j, m.second_start) );
|
i = m.first_start + m.size;
|
||||||
}
|
j = m.second_start + m.size;
|
||||||
i = m.first_start + m.size;
|
if m.size != 0 {
|
||||||
j = m.second_start + m.size;
|
opcodes.push(
|
||||||
if m.size != 0{
|
Opcode::new(String::from("equal"), m.first_start, i, m.second_start, j)
|
||||||
opcodes.push( Opcode::new(String::from("equal"), m.first_start, i, m.second_start, j) );
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.opcodes = Some(opcodes);
|
self.opcodes = Some(opcodes);
|
||||||
return self.opcodes.as_ref().unwrap().clone()
|
return self.opcodes.as_ref().unwrap().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_grouped_opcodes(&mut self, n: usize) -> Vec<Vec<Opcode>> {
|
pub fn get_grouped_opcodes(&mut self, n: usize) -> Vec<Vec<Opcode>> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut codes = self.get_opcodes();
|
let mut codes = self.get_opcodes();
|
||||||
if codes.is_empty() {
|
if codes.is_empty() {
|
||||||
codes.push(Opcode::new("equal".to_string(), 0, 1, 0, 1));
|
codes.push(Opcode::new("equal".to_string(), 0, 1, 0, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if codes.first().unwrap().tag == "equal" {
|
if codes.first().unwrap().tag == "equal" {
|
||||||
let mut opcode = codes.first_mut().unwrap();
|
let mut opcode = codes.first_mut().unwrap();
|
||||||
opcode.first_start = max(opcode.first_start, opcode.first_end - n);
|
opcode.first_start = max(opcode.first_start, opcode.first_end - n);
|
||||||
opcode.second_start = max(opcode.second_start, opcode.second_end - n);
|
opcode.second_start = max(opcode.second_start, opcode.second_end - n);
|
||||||
}
|
}
|
||||||
if codes.last().unwrap().tag == "equal" {
|
if codes.last().unwrap().tag == "equal" {
|
||||||
let mut opcode = codes.last_mut().unwrap();
|
let mut opcode = codes.last_mut().unwrap();
|
||||||
opcode.first_end = min(opcode.first_start + n, opcode.first_end);
|
opcode.first_end = min(opcode.first_start + n, opcode.first_end);
|
||||||
opcode.second_end = min(opcode.second_start + n, opcode.second_end);
|
opcode.second_end = min(opcode.second_start + n, opcode.second_end);
|
||||||
}
|
}
|
||||||
let nn = n + n;
|
let nn = n + n;
|
||||||
let mut group = Vec::new();
|
let mut group = Vec::new();
|
||||||
for code in &codes {
|
for code in &codes {
|
||||||
let (mut first_start, mut second_start) = (code.first_start, code.second_start);
|
let (mut first_start, mut second_start) = (code.first_start, code.second_start);
|
||||||
if code.tag == "equal" && code.first_end-code.first_start > nn {
|
if code.tag == "equal" && code.first_end - code.first_start > nn {
|
||||||
group.push( Opcode::new(code.tag.clone(), code.first_start, min(code.first_end, code.first_start+n),
|
group.push(Opcode::new(code.tag.clone(),
|
||||||
code.second_start, min(code.second_end, code.second_start+n)) );
|
code.first_start,
|
||||||
res.push(group.clone());
|
min(code.first_end, code.first_start + n),
|
||||||
group.clear();
|
code.second_start,
|
||||||
first_start = max(first_start, code.first_end-n);
|
min(code.second_end, code.second_start + n)));
|
||||||
second_start = max(second_start, code.second_end - n);
|
res.push(group.clone());
|
||||||
}
|
group.clear();
|
||||||
group.push( Opcode::new(code.tag.clone(), first_start, code.first_end, second_start, code.second_end) );
|
first_start = max(first_start, code.first_end - n);
|
||||||
}
|
second_start = max(second_start, code.second_end - n);
|
||||||
if !group.is_empty() && !(group.len()==1 && group.first().unwrap().tag == "equal") {
|
}
|
||||||
res.push(group.clone());
|
group.push(Opcode::new(code.tag.clone(),
|
||||||
}
|
first_start,
|
||||||
res
|
code.first_end,
|
||||||
}
|
second_start,
|
||||||
|
code.second_end));
|
||||||
|
}
|
||||||
|
if !group.is_empty() && !(group.len() == 1 && group.first().unwrap().tag == "equal") {
|
||||||
|
res.push(group.clone());
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ratio(&mut self) -> f32{
|
pub fn ratio(&mut self) -> f32 {
|
||||||
let matches = self.get_matching_blocks().iter().fold(0, |res, &m| res + m.size);
|
let matches = self.get_matching_blocks().iter().fold(0, |res, &m| res + m.size);
|
||||||
calculate_ratio(matches, self.first_sequence.len() + self.second_sequence.len())
|
calculate_ratio(matches,
|
||||||
}
|
self.first_sequence.len() + self.second_sequence.len())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-35
@@ -1,51 +1,51 @@
|
|||||||
pub fn calculate_ratio(matches: usize, length: usize) -> f32{
|
pub fn calculate_ratio(matches: usize, length: usize) -> f32 {
|
||||||
if length != 0{
|
if length != 0 {
|
||||||
return 2.0 * matches as f32 / length as f32
|
return 2.0 * matches as f32 / length as f32;
|
||||||
}
|
}
|
||||||
return 1.0
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn str_with_similar_chars(c: char, length: usize) -> String {
|
pub fn str_with_similar_chars(c: char, length: usize) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
for _ in 0..length{
|
for _ in 0..length {
|
||||||
s.push_str(&c.to_string());
|
s.push_str(&c.to_string());
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn count_leading(line: &str, c: char) -> usize {
|
pub fn count_leading(line: &str, c: char) -> usize {
|
||||||
let (mut i, n) = (0, line.len());
|
let (mut i, n) = (0, line.len());
|
||||||
let line: Vec<char> = line.chars().collect();
|
let line: Vec<char> = line.chars().collect();
|
||||||
while (i < n) && line[i] == c {
|
while (i < n) && line[i] == c {
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
return i
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn format_range_unified(start:usize, end: usize) -> String {
|
pub fn format_range_unified(start: usize, end: usize) -> String {
|
||||||
let mut beginning = start + 1;
|
let mut beginning = start + 1;
|
||||||
let length = end - start;
|
let length = end - start;
|
||||||
if length == 1 {
|
if length == 1 {
|
||||||
return beginning.to_string()
|
return beginning.to_string();
|
||||||
}
|
}
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
beginning -= 1;
|
beginning -= 1;
|
||||||
}
|
}
|
||||||
format!("{},{}", beginning, length)
|
format!("{},{}", beginning, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn format_range_context(start: usize, end: usize) -> String {
|
pub fn format_range_context(start: usize, end: usize) -> String {
|
||||||
let mut beginning = start + 1;
|
let mut beginning = start + 1;
|
||||||
let length = end - start;
|
let length = end - start;
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
beginning -= 1
|
beginning -= 1
|
||||||
}
|
}
|
||||||
if length <= 1 {
|
if length <= 1 {
|
||||||
return beginning.to_string()
|
return beginning.to_string();
|
||||||
}
|
}
|
||||||
format!("{},{}", beginning, beginning + length - 1)
|
format!("{},{}", beginning, beginning + length - 1)
|
||||||
}
|
}
|
||||||
+120
-103
@@ -4,145 +4,162 @@ use difflib::sequencematcher::{SequenceMatcher, Match, Opcode};
|
|||||||
use difflib::differ::Differ;
|
use difflib::differ::Differ;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_longest_match()
|
fn test_longest_match() {
|
||||||
{
|
let matcher = SequenceMatcher::new(" abcd", "abcd abcd");
|
||||||
let matcher = SequenceMatcher::new(" abcd", "abcd abcd");
|
let m = matcher.find_longest_match(0, 5, 0, 9);
|
||||||
let m = matcher.find_longest_match(0, 5, 0, 9);
|
assert_eq!(m.first_start, 0);
|
||||||
assert_eq!(m.first_start, 0);
|
assert_eq!(m.second_start, 4);
|
||||||
assert_eq!(m.second_start, 4);
|
assert_eq!(m.size, 5);
|
||||||
assert_eq!(m.size, 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_all_matches()
|
fn test_all_matches() {
|
||||||
{
|
let mut matcher = SequenceMatcher::new("abxcd", "abcd");
|
||||||
let mut matcher = SequenceMatcher::new("abxcd", "abcd");
|
let result = matcher.get_matching_blocks();
|
||||||
let result = matcher.get_matching_blocks();
|
let mut expected_result = Vec::new();
|
||||||
let mut expected_result = Vec::new();
|
expected_result.push(Match {
|
||||||
expected_result.push(Match{
|
first_start: 0,
|
||||||
first_start: 0,
|
second_start: 0,
|
||||||
second_start: 0,
|
size: 2,
|
||||||
size: 2
|
});
|
||||||
});
|
expected_result.push(Match {
|
||||||
expected_result.push(Match{
|
first_start: 3,
|
||||||
first_start: 3,
|
second_start: 2,
|
||||||
second_start: 2,
|
size: 2,
|
||||||
size: 2
|
});
|
||||||
});
|
expected_result.push(Match {
|
||||||
expected_result.push(Match{
|
first_start: 5,
|
||||||
first_start: 5,
|
second_start: 4,
|
||||||
second_start: 4,
|
size: 0,
|
||||||
size: 0
|
});
|
||||||
});
|
assert_eq!(result, expected_result);
|
||||||
assert_eq!(result, expected_result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_opcodes() {
|
fn test_get_opcodes() {
|
||||||
let mut matcher = SequenceMatcher::new("qabxcd", "abycdf");
|
let mut matcher = SequenceMatcher::new("qabxcd", "abycdf");
|
||||||
let result = matcher.get_opcodes();
|
let result = matcher.get_opcodes();
|
||||||
let mut expected_result = Vec::new();
|
let mut expected_result = Vec::new();
|
||||||
expected_result.push(Opcode{
|
expected_result.push(Opcode {
|
||||||
tag: "delete".to_string(),
|
tag: "delete".to_string(),
|
||||||
first_start: 0,
|
first_start: 0,
|
||||||
first_end: 1,
|
first_end: 1,
|
||||||
second_start: 0,
|
second_start: 0,
|
||||||
second_end: 0
|
second_end: 0,
|
||||||
});
|
});
|
||||||
expected_result.push(Opcode{
|
expected_result.push(Opcode {
|
||||||
tag: "equal".to_string(),
|
tag: "equal".to_string(),
|
||||||
first_start: 1,
|
first_start: 1,
|
||||||
first_end: 3,
|
first_end: 3,
|
||||||
second_start: 0,
|
second_start: 0,
|
||||||
second_end: 2
|
second_end: 2,
|
||||||
});
|
});
|
||||||
expected_result.push(Opcode{
|
expected_result.push(Opcode {
|
||||||
tag: "replace".to_string(),
|
tag: "replace".to_string(),
|
||||||
first_start: 3,
|
first_start: 3,
|
||||||
first_end: 4,
|
first_end: 4,
|
||||||
second_start: 2,
|
second_start: 2,
|
||||||
second_end: 3
|
second_end: 3,
|
||||||
});
|
});
|
||||||
expected_result.push(Opcode{
|
expected_result.push(Opcode {
|
||||||
tag: "equal".to_string(),
|
tag: "equal".to_string(),
|
||||||
first_start: 4,
|
first_start: 4,
|
||||||
first_end: 6,
|
first_end: 6,
|
||||||
second_start: 3,
|
second_start: 3,
|
||||||
second_end: 5
|
second_end: 5,
|
||||||
});
|
});
|
||||||
expected_result.push(Opcode{
|
expected_result.push(Opcode {
|
||||||
tag: "insert".to_string(),
|
tag: "insert".to_string(),
|
||||||
first_start: 6,
|
first_start: 6,
|
||||||
first_end: 6,
|
first_end: 6,
|
||||||
second_start: 5,
|
second_start: 5,
|
||||||
second_end: 6
|
second_end: 6,
|
||||||
});
|
});
|
||||||
assert_eq!(result, expected_result);
|
assert_eq!(result, expected_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ratio() {
|
fn test_ratio() {
|
||||||
let mut matcher = SequenceMatcher::new("abcd", "bcde");
|
let mut matcher = SequenceMatcher::new("abcd", "bcde");
|
||||||
assert_eq!(matcher.ratio(), 0.75);
|
assert_eq!(matcher.ratio(), 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_close_matches() {
|
fn test_get_close_matches() {
|
||||||
let words = vec!["ape", "apple", "peach", "puppy"];
|
let words = vec!["ape", "apple", "peach", "puppy"];
|
||||||
let result = difflib::get_close_matches("appel", words, 3, 0.6);
|
let result = difflib::get_close_matches("appel", words, 3, 0.6);
|
||||||
assert_eq!(result, vec!["apple", "ape"]);
|
assert_eq!(result, vec!["apple", "ape"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_differ_compare() {
|
fn test_differ_compare() {
|
||||||
let first_text = vec!["one\n", "two\n", "three\n"];
|
let first_text = vec!["one\n", "two\n", "three\n"];
|
||||||
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let result = differ.compare(&first_text, &second_text).join("");
|
let result = differ.compare(&first_text, &second_text).join("");
|
||||||
assert_eq!(result, "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
|
assert_eq!(result,
|
||||||
|
"- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_junk_char(ch: &str) -> bool {
|
fn is_junk_char(ch: &str) -> bool {
|
||||||
if ch == " " || ch == "\t" {
|
if ch == " " || ch == "\t" {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_differ_compare_with_func() {
|
fn test_differ_compare_with_func() {
|
||||||
let first_text = vec!["one\n", "two\n", "three\n"];
|
let first_text = vec!["one\n", "two\n", "three\n"];
|
||||||
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
||||||
let mut differ = Differ::new();
|
let mut differ = Differ::new();
|
||||||
differ.char_junk = Some(is_junk_char);
|
differ.char_junk = Some(is_junk_char);
|
||||||
let result = differ.compare(&first_text, &second_text).join("");
|
let result = differ.compare(&first_text, &second_text).join("");
|
||||||
assert_eq!(result, "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
|
assert_eq!(result,
|
||||||
|
"- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_differ_restore() {
|
fn test_differ_restore() {
|
||||||
let first_text = vec!["one\n", " two\n", "three\n"];
|
let first_text = vec!["one\n", " two\n", "three\n"];
|
||||||
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let diff = differ.compare(&first_text, &second_text);
|
let diff = differ.compare(&first_text, &second_text);
|
||||||
assert_eq!(first_text, Differ::restore(&diff, 1));
|
assert_eq!(first_text, Differ::restore(&diff, 1));
|
||||||
assert_eq!(second_text, Differ::restore(&diff, 2));
|
assert_eq!(second_text, Differ::restore(&diff, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unified_diff() {
|
fn test_unified_diff() {
|
||||||
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
||||||
let result = difflib::unified_diff(&first_text, &second_text, "Original", "Current",
|
let result = difflib::unified_diff(&first_text,
|
||||||
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3).join("");
|
&second_text,
|
||||||
assert_eq!(result, "--- Original\t2005-01-26 23:30:50\n+++ Current\t2010-04-02 10:20:52\n@@ -1,4 +1,4 @@\n+zero one-two-three+tree four");
|
"Original",
|
||||||
|
"Current",
|
||||||
|
"2005-01-26 23:30:50",
|
||||||
|
"2010-04-02 10:20:52",
|
||||||
|
3)
|
||||||
|
.join("");
|
||||||
|
assert_eq!(result,
|
||||||
|
"--- Original\t2005-01-26 23:30:50\n+++ Current\t2010-04-02 10:20:52\n@@ -1,4 \
|
||||||
|
+1,4 @@\n+zero one-two-three+tree four");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_context_diff() {
|
fn test_context_diff() {
|
||||||
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
||||||
let result = difflib::context_diff(&first_text, &second_text, "Original", "Current",
|
let result = difflib::context_diff(&first_text,
|
||||||
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3).join("");
|
&second_text,
|
||||||
assert_eq!(result, "*** Original\t2005-01-26 23:30:50\n--- Current\t2010-04-02 10:20:52\n***************\n*** 1,4 ****\n one! two! three four--- 1,4 ----\n+ zero one! tree four");
|
"Original",
|
||||||
|
"Current",
|
||||||
|
"2005-01-26 23:30:50",
|
||||||
|
"2010-04-02 10:20:52",
|
||||||
|
3)
|
||||||
|
.join("");
|
||||||
|
assert_eq!(result,
|
||||||
|
"*** Original\t2005-01-26 23:30:50\n--- Current\t2010-04-02 \
|
||||||
|
10:20:52\n***************\n*** 1,4 ****\n one! two! three four--- 1,4 ----\n+ \
|
||||||
|
zero one! tree four");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user