1 pub mod differ;
2 pub mod sequencematcher;
3 mod utils;
4 
5 use sequencematcher::{Sequence, SequenceMatcher};
6 use std::collections::HashMap;
7 use std::fmt::Display;
8 use utils::{format_range_context, format_range_unified};
9 
get_close_matches<'a>( word: &str, possibilities: Vec<&'a str>, n: usize, cutoff: f32, ) -> Vec<&'a str>10 pub fn get_close_matches<'a>(
11     word: &str,
12     possibilities: Vec<&'a str>,
13     n: usize,
14     cutoff: f32,
15 ) -> Vec<&'a str> {
16     if !(0.0 <= cutoff && cutoff <= 1.0) {
17         panic!("Cutoff must be greater than 0.0 and lower than 1.0");
18     }
19     let mut res: Vec<(f32, &str)> = Vec::new();
20     let mut matcher = SequenceMatcher::new("", word);
21     for i in &possibilities {
22         matcher.set_first_seq(i);
23         let ratio = matcher.ratio();
24         if ratio >= cutoff {
25             res.push((ratio, i));
26         }
27     }
28     res.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
29     res.truncate(n);
30     res.iter().map(|x| x.1).collect()
31 }
32 
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>33 pub fn unified_diff<T: Sequence + Display>(
34     first_sequence: &[T],
35     second_sequence: &[T],
36     from_file: &str,
37     to_file: &str,
38     from_file_date: &str,
39     to_file_date: &str,
40     n: usize,
41 ) -> Vec<String> {
42     let mut res = Vec::new();
43     let lineterm = '\n';
44     let mut started = false;
45     let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
46     for group in &matcher.get_grouped_opcodes(n) {
47         if !started {
48             started = true;
49             let from_date = format!("\t{}", from_file_date);
50             let to_date = format!("\t{}", to_file_date);
51             res.push(format!("--- {}{}{}", from_file, from_date, lineterm));
52             res.push(format!("+++ {}{}{}", to_file, to_date, lineterm));
53         }
54         let (first, last) = (group.first().unwrap(), group.last().unwrap());
55         let file1_range = format_range_unified(first.first_start, last.first_end);
56         let file2_range = format_range_unified(first.second_start, last.second_end);
57         res.push(format!(
58             "@@ -{} +{} @@{}",
59             file1_range, file2_range, lineterm
60         ));
61         for code in group {
62             if code.tag == "equal" {
63                 for item in first_sequence
64                     .iter()
65                     .take(code.first_end)
66                     .skip(code.first_start)
67                 {
68                     res.push(format!(" {}", item));
69                 }
70                 continue;
71             }
72             if code.tag == "replace" || code.tag == "delete" {
73                 for item in first_sequence
74                     .iter()
75                     .take(code.first_end)
76                     .skip(code.first_start)
77                 {
78                     res.push(format!("-{}", item));
79                 }
80             }
81             if code.tag == "replace" || code.tag == "insert" {
82                 for item in second_sequence
83                     .iter()
84                     .take(code.second_end)
85                     .skip(code.second_start)
86                 {
87                     res.push(format!("+{}", item));
88                 }
89             }
90         }
91     }
92     res
93 }
94 
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>95 pub fn context_diff<T: Sequence + Display>(
96     first_sequence: &[T],
97     second_sequence: &[T],
98     from_file: &str,
99     to_file: &str,
100     from_file_date: &str,
101     to_file_date: &str,
102     n: usize,
103 ) -> Vec<String> {
104     let mut res = Vec::new();
105     let lineterm = '\n';
106     let mut prefix: HashMap<String, String> = HashMap::new();
107     prefix.insert(String::from("insert"), String::from("+ "));
108     prefix.insert(String::from("delete"), String::from("- "));
109     prefix.insert(String::from("replace"), String::from("! "));
110     prefix.insert(String::from("equal"), String::from("  "));
111     let mut started = false;
112     let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
113     for group in &matcher.get_grouped_opcodes(n) {
114         if !started {
115             started = true;
116             let from_date = format!("\t{}", from_file_date);
117             let to_date = format!("\t{}", to_file_date);
118             res.push(format!("*** {}{}{}", from_file, from_date, lineterm));
119             res.push(format!("--- {}{}{}", to_file, to_date, lineterm));
120         }
121         let (first, last) = (group.first().unwrap(), group.last().unwrap());
122         res.push(format!("***************{}", lineterm));
123         let file1_range = format_range_context(first.first_start, last.first_end);
124         res.push(format!("*** {} ****{}", file1_range, lineterm));
125         let mut any = false;
126         for opcode in group {
127             if opcode.tag == "replace" || opcode.tag == "delete" {
128                 any = true;
129                 break;
130             }
131         }
132         if any {
133             for opcode in group {
134                 if opcode.tag != "insert" {
135                     for item in first_sequence
136                         .iter()
137                         .take(opcode.first_end)
138                         .skip(opcode.first_start)
139                     {
140                         res.push(format!("{}{}", &prefix[&opcode.tag], item));
141                     }
142                 }
143             }
144         }
145         let file2_range = format_range_context(first.second_start, last.second_end);
146         res.push(format!("--- {} ----{}", file2_range, lineterm));
147         any = false;
148         for opcode in group {
149             if opcode.tag == "replace" || opcode.tag == "insert" {
150                 any = true;
151                 break;
152             }
153         }
154         if any {
155             for opcode in group {
156                 if opcode.tag != "delete" {
157                     for item in second_sequence
158                         .iter()
159                         .take(opcode.second_end)
160                         .skip(opcode.second_start)
161                     {
162                         res.push(format!("{}{}", prefix[&opcode.tag], item));
163                     }
164                 }
165             }
166         }
167     }
168     res
169 }
170