rreplace/
lib.rs

1use std::collections::HashMap;
2use std::collections::LinkedList;
3
4/// run takes a string argument to search and a Hashmap of replacements.
5/// The Key of the hashmap is the sequence to match, the Value is the sequence to replace.
6pub fn run<'a>(from: &str, seqs_hash: HashMap<&'a str, &'a str>) -> String {
7    let mut all_matches: LinkedList<Matcher> = LinkedList::new();
8    let seqs = hash_to_vec_ordered_longest_shortest(seqs_hash);
9    let mut to_str = String::from("");
10    let mut memory = StringCache::new();
11
12    for (c_index, c) in from.char_indices() {
13        // Build new matches
14        if let Some(matches) = build_matches(c_index, c, &seqs) {
15            for matcher in matches {
16                all_matches.push_back(matcher);
17            }
18        };
19
20        // If matches exist
21        if !all_matches.is_empty() {
22            // Run and keep the Completed and running
23            for matcher in &mut all_matches {
24                matcher.run(c);
25            }
26            all_matches = all_matches
27                .into_iter()
28                .filter(|matcher| match matcher.status {
29                    Status::Failed => false,
30                    Status::Running => true,
31                    Status::Complete => true,
32                })
33                .collect();
34
35            if !all_matches.is_empty() {
36                memory.push(c);
37
38                println!("MEM PUSH: {}", c);
39            }
40
41            // For every matched item
42            loop {
43                // Return the first item in the list if it is complete
44                let front_op = if !all_matches.is_empty() {
45                    let front: &Matcher = all_matches.front().unwrap();
46                    match front.status {
47                        Status::Complete => Some(all_matches.pop_front().unwrap()),
48                        _ => None,
49                    }
50                } else {
51                    None
52                };
53                if let Some(front) = front_op {
54                    // Clear matchers that overlapped with the thang
55                    all_matches = all_matches
56                        .into_iter()
57                        .filter(|matcher| {
58                            if matcher.start_i >= front.get_clear_to_i() {
59                                return true;
60                            }
61                            false
62                        })
63                        .collect();
64
65                    if front.start_i > memory.get_index() {
66                        let str_offset = front.start_i - memory.get_index();
67                        let prev_str = memory.drain_chars(str_offset);
68                        println!("MEM DRAIN PUSH: {}", prev_str);
69                        to_str.push_str(&prev_str);
70                    }
71
72                    // Sync memory and update str with matcher replace
73                    memory.set_index(front.end_i + 1); // ?? is on the last char, not after it
74                    println!("MEM INDEX: {}", memory.start_i);
75                    memory.drain_chars(front.from_seq_count);
76                    to_str.push_str(front.to_seq);
77                    println!("REP PUSH: {}", front.to_seq);
78                } else {
79                    break;
80                }
81            }
82
83            // If empty replace rest of the string
84            if all_matches.is_empty() {
85                to_str.push_str(&memory.drain_all());
86                memory.set_index(c_index + 1);
87                println!("MEM INDEX: {}", memory.start_i);
88            }
89        } else {
90            memory.set_index(c_index + 1);
91            println!("MEM INDEX: {}", memory.start_i);
92            println!("MEM PUSH C: {}", c);
93            to_str.push(c);
94        }
95        println!("CHAR: {}, LOOP COUNT {}", c, c_index);
96    }
97
98    to_str
99}
100use std::ops::Add;
101
102struct StringCache {
103    s: String,
104    start_i: usize,
105}
106impl StringCache {
107    fn new() -> Self {
108        StringCache {
109            s: "".to_string(),
110            start_i: 0,
111        }
112    }
113
114    fn drain_chars(&mut self, count: usize) -> String {
115        println!("Current String: {}", self.s);
116        println!("Drain_Chars (COUNT): {}", count);
117
118        let mut ret_s: String = "".to_string();
119        let mut chars_rem: Vec<char> = self.s.clone().chars().collect();
120        let chars: Vec<char> = chars_rem.drain(..count).collect();
121
122        self.s.clear();
123        for c in chars_rem {
124            self.s.push(c);
125        }
126
127        for c in chars {
128            ret_s.push(c);
129        }
130        ret_s
131    }
132
133    fn drain_all(&mut self) -> String {
134        let ret_s = self.s.clone();
135        self.s.clear();
136        return ret_s;
137    }
138
139    fn push(&mut self, c: char) {
140        self.s.push(c);
141    }
142
143    fn clear(&mut self) {
144        self.s.clear();
145    }
146
147    fn inc_index(&mut self) {
148        self.start_i += 1;
149    }
150
151    fn inc_index_by(&mut self, num: usize) {
152        self.start_i += num;
153    }
154
155    fn set_index(&mut self, index: usize) {
156        self.start_i = index;
157    }
158
159    fn get_index(&self) -> usize {
160        return self.start_i;
161    }
162
163    fn count(&self) -> usize {
164        self.s.chars().count()
165    }
166}
167
168fn hash_to_vec_ordered_longest_shortest<'a>(
169    seqs_hash: HashMap<&'a str, &'a str>,
170) -> Vec<(&'a str, &'a str)> {
171    let mut seqs_vec: Vec<(&str, &str)> = vec![];
172    for key in seqs_hash.keys() {
173        seqs_vec.push((key, seqs_hash.get(key).unwrap()));
174    }
175    seqs_vec.sort_by(|a, b| b.0.chars().count().cmp(&a.0.chars().count()));
176
177    seqs_vec
178}
179
180enum Status {
181    Complete,
182    Running,
183    Failed,
184}
185fn build_matches<'a>(
186    start_index: usize,
187    initial_char: char,
188    seqs: &'a Vec<(&'a str, &'a str)>,
189) -> Option<Vec<Matcher>> {
190    let mut matchers: Vec<Matcher> = vec![];
191    for (from, to) in seqs {
192        if let Some(c) = from.chars().next() {
193            if c == initial_char {
194                matchers.push(Matcher::new(from, to, start_index));
195            }
196        }
197    }
198
199    if matchers.is_empty() {
200        return None;
201    }
202    return Some(matchers);
203}
204
205struct Matcher<'a> {
206    status: Status,
207    from_seq: Vec<char>,
208    to_seq: &'a str,
209    from_seq_count: usize,
210    start_i: usize,
211    end_i: usize,
212    counter: usize,
213}
214impl<'a> Matcher<'a> {
215    fn new(from_seq: &'a str, to_seq: &'a str, start_i: usize) -> Self {
216        let from_seq_count = from_seq.chars().count();
217        let from_seq: Vec<char> = from_seq.chars().collect();
218        Self {
219            status: Status::Running,
220            from_seq: from_seq,
221            to_seq: &to_seq,
222            from_seq_count: from_seq_count,
223            start_i: start_i,
224            end_i: start_i + from_seq_count - 1,
225            counter: 0,
226        }
227    }
228
229    fn run(&mut self, c: char) -> Status {
230        match self.status {
231            Status::Running => self.next(c),
232            Status::Complete => Status::Complete,
233            Status::Failed => Status::Failed, // Failed is never returned from match. Added for compiler reasons.
234        }
235    }
236
237    // Matches the current char in the sequence with the char that is being read.
238    // If the the matched char is the last in the sequence it returns Status::Complete
239    // If the match fails it returns Status::Failed
240    fn next(&mut self, c: char) -> Status {
241        if self.from_seq[self.counter] == c {
242            self.counter += 1;
243
244            if self.counter == self.from_seq.len() {
245                self.status = Status::Complete;
246                return Status::Complete;
247            }
248            return Status::Running;
249        } else {
250            self.status = Status::Failed;
251            return Status::Failed;
252        }
253    }
254
255    pub fn get_clear_to_i(&self) -> usize {
256        return self.start_i + self.from_seq_count;
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use std::collections::HashMap;
263
264    use crate::*;
265    #[test]
266    fn drain_chars() {
267        let mut sc = StringCache::new();
268        sc.s = String::from("hippo");
269        println!("Before drain: {}", sc.s);
270        let ret_s = sc.drain_chars(5);
271        println!("After drain: {}", sc.s);
272
273        assert_eq!(ret_s, "hippo");
274        //assert_eq!("ppo".to_string(), sc.s);
275    }
276
277    #[test]
278    fn drain_all() {
279        let mut sc = StringCache::new();
280        sc.s = String::from("my word");
281        let ret_s = sc.drain_all();
282
283        assert_eq!(ret_s, "my word");
284        assert_eq!("".to_string(), sc.s);
285    }
286
287    #[test]
288    fn highest_complexity() {
289        let mut replacement_seqs: HashMap<&str, &str> = HashMap::new();
290        replacement_seqs.insert("a", "x");
291        replacement_seqs.insert("bbbb c dddd eX", "!NO REPLACE");
292        replacement_seqs.insert("c", "l");
293        replacement_seqs.insert("c dddd", "y");
294        replacement_seqs.insert("c dddd eeX", "!NO REPLACE");
295        replacement_seqs.insert("gg ", "z ");
296        replacement_seqs.insert("g", "r");
297        replacement_seqs.insert("hh", "END");
298        pr(&replacement_seqs);
299
300        let from_s = "a bbbb cc dddd eee f gggggg hh";
301        let expect_s = "x bbbb ly eee f rrrrz END";
302        let new_s = run(from_s, replacement_seqs);
303        println!("Orig: {}", from_s);
304        println!("New String: {:?}", new_s);
305
306        assert_eq!(expect_s, new_s);
307    }
308
309    #[test]
310    fn longest_collapse_three_shorters() {
311        let mut replacement_seqs: HashMap<&str, &str> = HashMap::new();
312        replacement_seqs.insert("wow is my fox", "mmmmmmmmm");
313        replacement_seqs.insert("wow", "aaaa");
314        replacement_seqs.insert("is", "bb");
315        replacement_seqs.insert("my", "cc");
316        pr(&replacement_seqs);
317
318        let from_s = "wow is my foo";
319        let expect_s = "aaaa bb cc foo";
320        let new_s = run(from_s, replacement_seqs);
321        println!("Orig: {}", from_s);
322        println!("New String: {:?}", new_s);
323
324        assert_eq!(expect_s, new_s);
325    }
326
327    #[test]
328    fn longest_collapse_two_shorters() {
329        let mut replacement_seqs: HashMap<&str, &str> = HashMap::new();
330        replacement_seqs.insert("wow is my fox", "mmmmmmmmm");
331        replacement_seqs.insert("is", "bb");
332        replacement_seqs.insert("my", "cc");
333        pr(&replacement_seqs);
334
335        let from_s = "wow is my foo";
336        let expect_s = "wow bb cc foo";
337        let new_s = run(from_s, replacement_seqs);
338        println!("Orig: {}", from_s);
339        println!("New String: {:?}", new_s);
340
341        assert_eq!(expect_s, new_s);
342    }
343
344    #[test]
345    fn longest_collapse_replace_shorter() {
346        let mut replacement_seqs: HashMap<&str, &str> = HashMap::new();
347        replacement_seqs.insert("present", "future");
348        replacement_seqs.insert("present txxxxx", "past");
349        pr(&replacement_seqs);
350
351        let from_s = "a present tense foo";
352        let expect_s = "a future tense foo";
353        let new_s = run(from_s, replacement_seqs);
354        println!("Orig: {}", from_s);
355        println!("New String: {:?}", new_s);
356
357        assert_eq!(expect_s, new_s);
358    }
359
360    #[test]
361    fn longest_replace() {
362        let mut replacement_seqs: HashMap<&str, &str> = HashMap::new();
363        replacement_seqs.insert("present", "future");
364        replacement_seqs.insert("present tense", "past");
365        pr(&replacement_seqs);
366
367        let from_s = "a present tense foo";
368        let expect_s = "a past foo";
369        let new_s = run(from_s, replacement_seqs);
370        println!("Orig: {}", from_s);
371        println!("New String: {:?}", new_s);
372
373        assert_eq!(expect_s, new_s);
374    }
375
376    #[test]
377    fn multiple_seperate_replace() {
378        let mut replacement_seqs: HashMap<&str, &str> = HashMap::new();
379        replacement_seqs.insert("a", "it was");
380        replacement_seqs.insert("present", "past");
381        pr(&replacement_seqs);
382
383        let from_s = "a present tense foo";
384        let expect_s = "it was past tense foo";
385        let new_s = run(from_s, replacement_seqs);
386        println!("Orig: {}", from_s);
387        println!("New String: {:?}", new_s);
388
389        assert_eq!(expect_s, new_s);
390    }
391
392    #[test]
393    fn single_replace() {
394        let mut replacement_seqs: HashMap<&str, &str> = HashMap::new();
395        replacement_seqs.insert("a", "the");
396        pr(&replacement_seqs);
397
398        let from_s = "a present tense foo";
399        let expect_s = "the present tense foo";
400        let new_s = run(from_s, replacement_seqs);
401        println!("Orig: {}", from_s);
402        println!("New String: {:?}", new_s);
403
404        assert_eq!(expect_s, new_s);
405    }
406
407    #[test]
408    fn hashmap_stays_in_order() {
409        let mut map: HashMap<&str, &str> = HashMap::new();
410        map.insert("ham", "cheese");
411        map.insert("foo", "boo");
412        assert!(map.get("ham").unwrap() == &"cheese");
413        assert!(map.keys().len() == 2);
414    }
415
416    fn pr(map: &HashMap<&str, &str>) {
417        println!("Replacements: {:?}", map);
418    }
419
420    // While interating through a string the match will build a string of all characters that have been matched.
421    #[test]
422    fn matcher_works() {
423        let from_match = "foo";
424        let to_match = "bar";
425        let mut sentance = "this is foo bar".chars();
426        let mut matched_str = String::from("");
427        let mut matcher: Option<Matcher> = Option::None;
428
429        let mut count = 0;
430        while let Some(c) = sentance.next() {
431            if c == 'f' {
432                matcher = Option::Some(Matcher::new(from_match, to_match, count));
433            }
434
435            let status = match matcher.as_mut() {
436                Some(m) => m.run(c),
437                None => Status::Failed,
438            };
439
440            match status {
441                Status::Running => matched_str = format!("{}{}", matched_str, c),
442                Status::Complete => {
443                    matched_str = format!("{}{}", matched_str, c);
444                    matcher = Option::None;
445                }
446                Status::Failed => matcher = Option::None,
447            }
448            println!("to match: {}", from_match);
449            println!("matched: {}", matched_str);
450
451            count += 1;
452        }
453
454        assert!(matched_str == from_match);
455    }
456
457    #[test]
458    pub fn hashmap_to_vector_sort_longest() {
459        let mut seqs_hash: HashMap<&str, &str> = HashMap::new();
460        seqs_hash.insert("a super duper long foo", "a super duper long bar");
461        seqs_hash.insert("fooest", "barest");
462        seqs_hash.insert("foo", "bar");
463        seqs_hash.insert("fooer", "barer");
464        seqs_hash.insert("a very long foo", "a very long foo");
465
466        let mut seqs_vec: Vec<(&str, &str)> = vec![];
467        for key in seqs_hash.keys() {
468            seqs_vec.push((key, seqs_hash.get(key).unwrap()));
469        }
470        seqs_vec.sort_by(|a, b| b.0.len().cmp(&a.0.len()));
471        println! {"{:?}", seqs_vec};
472
473        assert!(seqs_vec[0].0 == "a super duper long foo");
474        assert!(seqs_vec[1].0 == "a very long foo");
475        assert!(seqs_vec[2].0 == "fooest");
476        assert!(seqs_vec[3].0 == "fooer");
477        assert!(seqs_vec[4].0 == "foo");
478    }
479}