pub mod differ;
pub mod sequencematcher;
mod utils;
use sequencematcher::{Sequence, SequenceMatcher};
use std::collections::HashMap;
use std::fmt::Display;
use utils::{format_range_context, format_range_unified};
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) {
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);
for i in &possibilities {
matcher.set_first_seq(i);
let ratio = matcher.ratio();
if ratio >= cutoff {
res.push((ratio, i));
}
}
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 + Display>(
first_sequence: &[T],
second_sequence: &[T],
from_file: &str,
to_file: &str,
from_file_date: &str,
to_file_date: &str,
n: usize,
) -> Vec<String> {
let mut res = Vec::new();
let lineterm = '\n';
let mut started = false;
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
for group in &matcher.get_grouped_opcodes(n) {
if !started {
started = true;
let from_date = format!("\t{}", from_file_date);
let to_date = format!("\t{}", to_file_date);
res.push(format!("--- {}{}{}", from_file, from_date, lineterm));
res.push(format!("+++ {}{}{}", to_file, to_date, lineterm));
}
let (first, last) = (group.first().unwrap(), group.last().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);
res.push(format!(
"@@ -{} +{} @@{}",
file1_range, file2_range, lineterm
));
for code in group {
if code.tag == "equal" {
for item in first_sequence
.iter()
.take(code.first_end)
.skip(code.first_start)
{
res.push(format!(" {}", item));
}
continue;
}
if code.tag == "replace" || code.tag == "delete" {
for item in first_sequence
.iter()
.take(code.first_end)
.skip(code.first_start)
{
res.push(format!("-{}", item));
}
}
if code.tag == "replace" || code.tag == "insert" {
for item in second_sequence
.iter()
.take(code.second_end)
.skip(code.second_start)
{
res.push(format!("+{}", item));
}
}
}
}
res
}
pub fn context_diff<T: Sequence + Display>(
first_sequence: &[T],
second_sequence: &[T],
from_file: &str,
to_file: &str,
from_file_date: &str,
to_file_date: &str,
n: usize,
) -> Vec<String> {
let mut res = Vec::new();
let lineterm = '\n';
let mut prefix: HashMap<String, String> = HashMap::new();
prefix.insert(String::from("insert"), String::from("+ "));
prefix.insert(String::from("delete"), String::from("- "));
prefix.insert(String::from("replace"), String::from("! "));
prefix.insert(String::from("equal"), String::from(" "));
let mut started = false;
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
for group in &matcher.get_grouped_opcodes(n) {
if !started {
started = true;
let from_date = format!("\t{}", from_file_date);
let to_date = format!("\t{}", to_file_date);
res.push(format!("*** {}{}{}", from_file, from_date, lineterm));
res.push(format!("--- {}{}{}", to_file, to_date, lineterm));
}
let (first, last) = (group.first().unwrap(), group.last().unwrap());
res.push(format!("***************{}", lineterm));
let file1_range = format_range_context(first.first_start, last.first_end);
res.push(format!("*** {} ****{}", file1_range, lineterm));
let mut any = false;
for opcode in group {
if opcode.tag == "replace" || opcode.tag == "delete" {
any = true;
break;
}
}
if any {
for opcode in group {
if opcode.tag != "insert" {
for item in first_sequence
.iter()
.take(opcode.first_end)
.skip(opcode.first_start)
{
res.push(format!("{}{}", &prefix[&opcode.tag], item));
}
}
}
}
let file2_range = format_range_context(first.second_start, last.second_end);
res.push(format!("--- {} ----{}", file2_range, lineterm));
any = false;
for opcode in group {
if opcode.tag == "replace" || opcode.tag == "insert" {
any = true;
break;
}
}
if any {
for opcode in group {
if opcode.tag != "delete" {
for item in second_sequence
.iter()
.take(opcode.second_end)
.skip(opcode.second_start)
{
res.push(format!("{}{}", prefix[&opcode.tag], item));
}
}
}
}
}
res
}