Partly implement differ struce, add Opcode struct
This commit is contained in:
+141
@@ -1,4 +1,5 @@
|
||||
use sequencematcher::SequenceMatcher;
|
||||
use utils::{slice_str, str_with_similar_chars};
|
||||
|
||||
|
||||
pub struct Differ {
|
||||
@@ -13,4 +14,144 @@ impl Differ{
|
||||
charjunk: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare(&self, first_sequence: &str, second_sequence: &str) -> Vec<Vec<String>> {
|
||||
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
||||
let mut res = Vec::new();
|
||||
for opcode in matcher.get_opcodes(){
|
||||
match opcode.tag.as_ref() {
|
||||
"replace" => {},
|
||||
"delete" => { res.push( self.dump("-", first_sequence, opcode.first_start, opcode.first_end) ) },
|
||||
"insert" => { res.push( self.dump("+", second_sequence, opcode.second_start, opcode.second_end) ) },
|
||||
"equal" => { res.push( self.dump(" ", first_sequence, opcode.first_start, opcode.first_end) ) },
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn dump(&self, tag: &str, string: &str, start: usize, end: usize) -> Vec<String> {
|
||||
let mut res = Vec::new();
|
||||
match slice_str(string, start, end) {
|
||||
Some(s) => {
|
||||
for c in s.chars(){
|
||||
res.push(format!("{} {}", tag, c));
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn plain_replace(&self, first_sequence: &str, first_start: usize, first_end: usize,
|
||||
second_sequence: &str, second_start: usize, second_end: usize) -> Vec<Vec<String>> {
|
||||
if !(first_start < first_end && second_start < second_end){
|
||||
return Vec::new();
|
||||
}
|
||||
let mut first = Vec::new();
|
||||
let mut second = Vec::new();
|
||||
if second_end - second_start < first_end - first_start{
|
||||
first = self.dump("+", second_sequence, second_start, second_end);
|
||||
second = self.dump("-", first_sequence, first_start, first_end);
|
||||
} else {
|
||||
first = self.dump("-", first_sequence, first_start, first_end);
|
||||
second = self.dump("+", second_sequence, second_start, second_end);
|
||||
}
|
||||
vec![first, second]
|
||||
}
|
||||
|
||||
fn fancy_replace(&self, first_sequence: &str, first_start: usize, first_end: usize,
|
||||
second_sequence: &str, second_start: usize, second_end: usize) -> Vec<Vec<String>> {
|
||||
let mut res = Vec::new();
|
||||
let first_sequence_vec: Vec<char> = first_sequence.chars().collect();
|
||||
let second_sequence_vec: Vec<char> = second_sequence.chars().collect();
|
||||
let (mut best_ratio, cutoff) = (0.74, 0.75);
|
||||
let (mut best_i, mut best_j) = (0, 0);
|
||||
let (mut second_sequence_char, mut first_sequence_char) = (String::new(), String::new());
|
||||
let mut cruncher = SequenceMatcher::new("", "");
|
||||
//cruncher.charjunk = self.charjunk;
|
||||
// eqi, eqj = None, None # 1st indices of equal lines (if any)
|
||||
let mut eqi: Option<usize> = None;
|
||||
let mut eqj: Option<usize> = None;
|
||||
for j in second_start..second_end{
|
||||
cruncher.set_first_seq("");
|
||||
cruncher.set_second_seq("");
|
||||
second_sequence_char = second_sequence_vec[j].to_string();
|
||||
//cruncher.set_second_seq(&second_sequence_char);
|
||||
for i in first_start..first_end{
|
||||
first_sequence_char = first_sequence_vec[i].to_string();
|
||||
if first_sequence_char == second_sequence_char{
|
||||
if eqi.is_none(){
|
||||
eqi = Some(i);
|
||||
eqj = Some(j);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//cruncher.set_first_seq(&first_sequence_char);
|
||||
if cruncher.ratio() > best_ratio{
|
||||
best_ratio = cruncher.ratio();
|
||||
best_i = i;
|
||||
best_j = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if best_ratio < cutoff{
|
||||
if eqi.is_none(){
|
||||
res.extend(self.plain_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end).iter().cloned());
|
||||
}
|
||||
best_i = eqi.unwrap();
|
||||
best_j = eqj.unwrap();
|
||||
best_ratio = 1.0;
|
||||
} else {
|
||||
eqi = None;
|
||||
}
|
||||
res.extend(vec![self.fancy_helper(first_sequence, first_start, best_i, second_sequence, second_start, best_j)].iter().cloned());
|
||||
let (first_elt, second_elt) = (first_sequence_vec[best_i].to_string(), second_sequence_vec[best_j].to_string());
|
||||
if eqi.is_none(){
|
||||
let (mut first_tag, mut second_tag) = (String::new(), String::new());
|
||||
//cruncher.set_seqs(&first_elt, &second_elt);
|
||||
for opcode in &cruncher.get_opcodes(){
|
||||
let (first_length, second_length) = (opcode.first_end - opcode.first_start, opcode.second_end - opcode.second_start);
|
||||
match opcode.tag.as_ref() {
|
||||
"replace" => {
|
||||
first_tag.push_str(&str_with_similar_chars('^', first_length));
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
let mut s = String::from(" ");
|
||||
s.push_str(&first_elt);
|
||||
res.extend(vec![vec![s]].iter().cloned());
|
||||
}
|
||||
res.extend(vec![self.fancy_helper(first_sequence, best_i + 1, first_end, second_sequence, best_j + 1, second_end)].iter().cloned());
|
||||
res
|
||||
}
|
||||
|
||||
fn fancy_helper(&self, first_sequence: &str, first_start: usize, first_end: usize,
|
||||
second_sequence: &str, second_start: usize, second_end: usize) -> Vec<String> {
|
||||
let mut res = Vec::new();
|
||||
if first_start < first_end{
|
||||
if second_start < second_end{
|
||||
res = self.fancy_replace(first_sequence, 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
|
||||
}
|
||||
}
|
||||
+50
-6
@@ -38,24 +38,68 @@ impl Match{
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Opcode{
|
||||
pub tag: String,
|
||||
pub first_start: usize,
|
||||
pub first_end: usize,
|
||||
pub second_start: usize,
|
||||
pub second_end: usize
|
||||
}
|
||||
|
||||
impl Opcode{
|
||||
fn new(tag: String, first_start: usize, first_end: usize, second_start: usize, second_end: usize) -> Opcode {
|
||||
Opcode{
|
||||
tag: tag,
|
||||
first_start: first_start,
|
||||
first_end: first_end,
|
||||
second_start: second_start,
|
||||
second_end: second_end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct SequenceMatcher<'a>{
|
||||
first_sequence: &'a str,
|
||||
second_sequence: &'a str,
|
||||
matching_blocks: Option<Vec<Match>>,
|
||||
opcodes: Option<Vec<(String, usize, usize, usize, usize)>>
|
||||
opcodes: Option<Vec<Opcode>>,
|
||||
autojunk: bool
|
||||
}
|
||||
|
||||
|
||||
impl<'a> SequenceMatcher<'a>{
|
||||
pub fn new(first_sequence: &'a str, second_sequence: &'a str) -> SequenceMatcher<'a> {
|
||||
SequenceMatcher{
|
||||
first_sequence: first_sequence,
|
||||
second_sequence: second_sequence,
|
||||
matching_blocks: None,
|
||||
opcodes: None
|
||||
opcodes: None,
|
||||
autojunk: true,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_seqs(&mut self, first_sequence: &'a str, second_sequence: &'a str) {
|
||||
self.set_first_seq(first_sequence);
|
||||
self.set_second_seq(second_sequence);
|
||||
}
|
||||
|
||||
pub fn set_first_seq(&mut self, sequence: &'a str) {
|
||||
self.first_sequence = sequence;
|
||||
self.matching_blocks = None;
|
||||
self.opcodes = None;
|
||||
}
|
||||
|
||||
pub fn set_second_seq(&mut self, sequence: &'a str) {
|
||||
self.second_sequence = sequence;
|
||||
self.matching_blocks = None;
|
||||
self.opcodes = None;
|
||||
//self.fullbcount = None
|
||||
//self.__chain_b()
|
||||
}
|
||||
|
||||
pub fn find_longest_match(&self, first_start: usize, first_end: usize, second_start: usize, second_end: usize) -> Option<Match> {
|
||||
let first_sequence: Vec<char> = slice_str(self.first_sequence, first_start, first_end).unwrap_or("").chars().collect();
|
||||
let second_sequence: Vec<char> = slice_str(self.second_sequence, second_start, second_end).unwrap_or("").chars().collect();
|
||||
@@ -133,7 +177,7 @@ impl<'a> SequenceMatcher<'a>{
|
||||
self.matching_blocks.as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_opcodes(&mut self) -> Vec<(String, usize, usize, usize, usize)>{
|
||||
pub fn get_opcodes(&mut self) -> Vec<Opcode>{
|
||||
if self.opcodes.as_ref().is_some(){
|
||||
return self.opcodes.as_ref().unwrap().clone()
|
||||
}
|
||||
@@ -151,12 +195,12 @@ impl<'a> SequenceMatcher<'a>{
|
||||
tag = String::from("insert");
|
||||
}
|
||||
if !tag.is_empty(){
|
||||
opcodes.push( (tag, i, m.first_start, j, m.second_start) );
|
||||
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;
|
||||
if m.size != 0{
|
||||
opcodes.push( (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);
|
||||
@@ -167,4 +211,4 @@ impl<'a> SequenceMatcher<'a>{
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -17,4 +17,13 @@ pub fn calculate_ratio(matches: usize, length: usize) -> f32{
|
||||
return 2.0 * matches as f32 / length as f32
|
||||
}
|
||||
return 1.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn str_with_similar_chars(c: char, length: usize) -> String {
|
||||
let mut s = String::new();
|
||||
for _ in 0..length{
|
||||
s.push_str(&c.to_string());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user