From a99af75280cdd31f21818f5d6e8416db56b294bb Mon Sep 17 00:00:00 2001 From: Dima Kudosh Date: Sun, 28 Feb 2016 21:35:03 +0300 Subject: [PATCH] Add ability to specify user-defined functions for matcher --- src/differ.rs | 15 +++++++------- src/lib.rs | 2 +- src/sequencematcher.rs | 44 +++++++++++++++++++++++------------------- src/utils.rs | 1 - tests/test.rs | 19 +++++++++++++++++- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/differ.rs b/src/differ.rs index 7c34d37..8143098 100644 --- a/src/differ.rs +++ b/src/differ.rs @@ -4,20 +4,21 @@ use std::cmp; pub struct Differ { - linejunk: Option, - charjunk: Option + pub line_junk: Option bool>, + pub char_junk: Option bool> } impl Differ{ pub fn new() -> Differ { Differ{ - linejunk: None, - charjunk: None + line_junk: None, + char_junk: None } } pub fn compare(&self, first_sequence: &T, second_sequence: &T) -> Vec { let mut matcher = SequenceMatcher::new(first_sequence, second_sequence); + matcher.set_is_junk(self.line_junk); let mut res = Vec::new(); for opcode in matcher.get_opcodes(){ let mut gen = Vec::new(); @@ -87,7 +88,7 @@ impl Differ{ continue; } let mut cruncher = SequenceMatcher::new(first_sequence_str, second_sequence_str); - //cruncher.charjunk = self.charjunk; + cruncher.set_is_junk(self.char_junk); if cruncher.ratio() > best_ratio{ best_ratio = cruncher.ratio(); best_i = i; @@ -97,7 +98,7 @@ impl Differ{ } 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()); + res.extend(self.plain_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end).iter().cloned()); return res } best_i = eqi.unwrap(); @@ -111,7 +112,7 @@ impl Differ{ if eqi.is_none(){ let (mut first_tag, mut second_tag) = (String::new(), String::new()); let mut cruncher = SequenceMatcher::new(first_element, second_element); - //cruncher.charjunk = self.charjunk; + cruncher.set_is_junk(self.char_junk); 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() { diff --git a/src/lib.rs b/src/lib.rs index 15bb5c2..85544ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ 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> { if !(0.0 <= cutoff && cutoff <= 1.0) { - //error + panic!("Cutoff must be greater than 0.0 and lower than 1.0"); } let mut res: Vec<(f32, &str)> = Vec::new(); let mut matcher = SequenceMatcher::new("", word); diff --git a/src/sequencematcher.rs b/src/sequencematcher.rs index 22ef27f..b466eb4 100644 --- a/src/sequencematcher.rs +++ b/src/sequencematcher.rs @@ -44,7 +44,6 @@ impl Opcode{ pub trait Sequence: Debug { fn len(&self) -> usize; fn at_index(&self, index: usize) -> Option<&str>; - fn slice(&self, start: usize, end: usize) -> Option<&str>; } impl Sequence for str { @@ -52,20 +51,13 @@ impl Sequence for str { self.len() } - fn slice(&self, start: usize, end: usize) -> Option<&str> { - if start >= end{ - return None - } - if start > self.len() || end > self.len(){ - return None - } - unsafe{ - Some(self.slice_unchecked(start, end)) - } - } - fn at_index(&self, index: usize) -> Option<&str> { - self.slice(index, index + 1) + if index > self.len(){ + return None + } + unsafe{ + Some(self.slice_unchecked(index, index + 1)) + } } } @@ -74,10 +66,6 @@ impl<'a> Sequence for Vec<&'a str> { self.len() } - fn slice(&self, start: usize, end: usize) -> Option<&str> { - None - } - fn at_index(&self, index: usize) -> Option<&str> { if index < self.len() && index >= 0 { return Some(self[index]) @@ -91,7 +79,7 @@ pub struct SequenceMatcher<'a, T: 'a + ?Sized + Sequence>{ second_sequence: &'a T, matching_blocks: Option>, opcodes: Option>, - autojunk: bool, + is_junk: Option bool>, second_sequence_elements: HashMap<&'a str, Vec>, second_sequence_popular: Vec<&'a str> } @@ -105,7 +93,7 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{ second_sequence: second_sequence, matching_blocks: None, opcodes: None, - autojunk: true, + is_junk: None, second_sequence_elements: HashMap::new(), second_sequence_popular: Vec::new() }; @@ -113,6 +101,11 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{ matcher } + pub fn set_is_junk(&mut self, is_junk: Option bool>) { + self.is_junk = is_junk; + self.set_second_seq(self.second_sequence); + } + pub fn set_seqs(&mut self, first_sequence: &'a T, second_sequence: &'a T) { self.set_first_seq(first_sequence); self.set_second_seq(second_sequence); @@ -138,6 +131,17 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{ let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap()).or_insert(Vec::new()); counter.push(i); } + if self.is_junk.is_some() { + let mut junk = Vec::new(); + for element in second_sequence_elements.keys() { + if (self.is_junk.unwrap())(element) { + junk.push(element.clone()); + } + } + for element in &junk { + second_sequence_elements.remove(element); + } + } let mut popular = Vec::new(); let len = second_sequence.len(); if len >= 200 { diff --git a/src/utils.rs b/src/utils.rs index da3f1e3..535aee3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -27,7 +27,6 @@ pub fn count_leading(line: &str, c: char) -> usize { pub fn format_range_unified(start:usize, end: usize) -> String { let mut beginning = start + 1; - println!("{:?} {}", start, end); let length = end - start; if length == 1 { return beginning.to_string() diff --git a/tests/test.rs b/tests/test.rs index 1b330bd..fb70e69 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -102,9 +102,26 @@ fn test_differ_compare() { 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 { + if ch == " " || ch == "\t" { + return true + } + false +} + +#[test] +fn test_differ_compare_with_func() { + let first_text = vec!["one\n", "two\n", "three\n"]; + let second_text = vec!["ore\n", "tree\n", "emu\n"]; + let mut differ = Differ::new(); + differ.char_junk = Some(is_junk_char); + 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"); +} + #[test] 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 differ = Differ::new(); let diff = differ.compare(&first_text, &second_text);