difflib/
lib.rs

1pub mod differ;
2pub mod sequencematcher;
3mod utils;
4
5use sequencematcher::{Sequence, SequenceMatcher};
6use std::collections::HashMap;
7use std::fmt::Display;
8use utils::{format_range_context, format_range_unified};
9
10pub 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
33pub 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
95pub 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}