str_distance/
utils.rs

1/// Return the shorter str as first index
2#[inline]
3pub(crate) fn order_by_len_asc<'a>(s1: &'a str, s2: &'a str) -> (&'a str, &'a str) {
4    if s1.len() <= s2.len() {
5        (s1, s2)
6    } else {
7        (s2, s1)
8    }
9}
10
11#[inline]
12pub(crate) fn count_eq<S, T>(mut s1_iter: S, mut s2_iter: T) -> usize
13where
14    S: Iterator,
15    T: Iterator,
16    <S as Iterator>::Item: PartialEq<<T as Iterator>::Item>,
17{
18    let mut match_ctn = 0;
19    loop {
20        let c1 = match s1_iter.next() {
21            None => {
22                // s2 ends with completely with s1
23                break;
24            }
25            Some(val) => val,
26        };
27
28        let c2 = match s2_iter.next() {
29            None => {
30                // s1 ends completely with s2
31                break;
32            }
33            Some(val) => val,
34        };
35        if c1 == c2 {
36            match_ctn += 1;
37        } else {
38            break;
39        }
40    }
41    match_ctn
42}
43
44pub(crate) struct DelimDistinct<S, T>
45where
46    S: Iterator + Clone,
47    T: Iterator + Clone,
48    <S as Iterator>::Item: PartialEq<<T as Iterator>::Item>,
49{
50    /// The amount of items both iter share at their beginning.
51    pub prefix_len: usize,
52    /// Iterator over the distinct items of s1
53    pub distinct_s1: S,
54    /// The amount of distinct items left in iter 1
55    pub s1_len: usize,
56    /// Iterator over the distinct items of s2
57    pub distinct_s2: T,
58    /// The amount of distinct items left in iter 2
59    pub s2_len: usize,
60    /// The amount of items both iters share at their end.
61    pub suffix_len: usize,
62}
63
64#[allow(unused)]
65impl<S, T> DelimDistinct<S, T>
66where
67    S: Iterator + Clone,
68    T: Iterator + Clone,
69    <S as Iterator>::Item: PartialEq<<T as Iterator>::Item>,
70{
71    /// Amount of chars both str share at their tails.
72    #[inline]
73    pub fn common(&self) -> usize {
74        self.prefix_len + self.suffix_len
75    }
76
77    /// The amount of distinct chars for each str
78    #[inline]
79    pub fn remaining(&self) -> (usize, usize) {
80        (self.s1_len, self.s2_len)
81    }
82
83    /// Whether both str are identical.
84    #[inline]
85    pub fn is_eq(&self) -> bool {
86        self.remaining() == (0, 0)
87    }
88
89    #[inline]
90    pub fn remaining_s2(&self) -> usize {
91        self.s2_len
92    }
93
94    #[inline]
95    pub fn remaining_s1(&self) -> usize {
96        self.s1_len
97    }
98
99    /// Return the len of common prefix and suffix items, and the distinct left
100    /// elements in between.
101    #[inline]
102    pub(crate) fn new_skip_take(
103        a: S,
104        b: T,
105    ) -> DelimDistinct<std::iter::Skip<std::iter::Take<S>>, std::iter::Skip<std::iter::Take<T>>>
106    {
107        // collecting is a little tedious here, but we can't rely on the iters also
108        // being DoubleEnded
109        let a_vec: Vec<_> = a.clone().collect();
110        let b_vec: Vec<_> = b.clone().collect();
111
112        let a_len = a_vec.len();
113        let b_len = b_vec.len();
114
115        let suffix_len = count_eq(a_vec.into_iter().rev(), b_vec.into_iter().rev());
116
117        let a_iter = a.take(a_len - suffix_len);
118        let b_iter = b.take(b_len - suffix_len);
119
120        let prefix_len = count_eq(a_iter.clone(), b_iter.clone());
121
122        let common_len = prefix_len + suffix_len;
123        DelimDistinct {
124            suffix_len,
125            prefix_len,
126            s1_len: a_len - common_len,
127            s2_len: b_len - common_len,
128            distinct_s1: a_iter.skip(prefix_len),
129            distinct_s2: b_iter.skip(prefix_len),
130        }
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn delim_different() {
140        let s1 = "kitten";
141        let s2 = "sitting";
142
143        let delim = DelimDistinct::new_skip_take(s1.chars(), s2.chars());
144        assert_eq!(delim.prefix_len, 0);
145        assert_eq!(delim.suffix_len, 0);
146        assert_eq!(delim.s1_len, 6);
147        assert_eq!(delim.s2_len, 7);
148    }
149
150    #[test]
151    fn delim_eq() {
152        let s1 = "kitten";
153        let s2 = "kitten";
154
155        let delim = DelimDistinct::new_skip_take(s1.chars(), s2.chars());
156        assert_eq!(delim.common(), 6);
157        assert_eq!(delim.remaining(), (0, 0));
158        assert!(delim.is_eq());
159    }
160
161    #[test]
162    fn delim_eq_suffix() {
163        let s1 = "cute kitten";
164        let s2 = "kitten";
165
166        let delim = DelimDistinct::new_skip_take(s1.chars(), s2.chars());
167        assert_eq!(delim.common(), 6);
168        assert_eq!(delim.remaining(), (5, 0));
169        assert_eq!(delim.distinct_s1.collect::<String>(), String::from("cute "));
170
171        let s1 = "k cute kitten";
172        let s2 = "kitten";
173        let delim = DelimDistinct::new_skip_take(s1.chars(), s2.chars());
174        assert_eq!(delim.common(), 6);
175        assert_eq!(delim.remaining(), (7, 0));
176        assert_eq!(
177            delim.distinct_s1.collect::<String>(),
178            String::from("k cute ")
179        );
180    }
181
182    #[test]
183    fn delim_eq_prefix() {
184        let s1 = "hungry kitten";
185        let s2 = "hungry hippo";
186
187        let delim = DelimDistinct::new_skip_take(s1.chars(), s2.chars());
188        assert_eq!(delim.common(), 7);
189        assert_eq!(delim.remaining(), (6, 5));
190        assert_eq!(
191            delim.distinct_s1.collect::<String>(),
192            String::from("kitten")
193        );
194        assert_eq!(delim.distinct_s2.collect::<String>(), String::from("hippo"));
195    }
196
197    #[test]
198    fn delim_eq_prefix_suffix() {
199        let s1 = "hungry kitten is hungry";
200        let s2 = "hungry hippo is hungry";
201
202        let delim = DelimDistinct::new_skip_take(s1.chars(), s2.chars());
203        assert_eq!(delim.common(), 17);
204        assert_eq!(delim.remaining(), (6, 5));
205        assert_eq!(
206            delim.distinct_s1.collect::<String>(),
207            String::from("kitten")
208        );
209        assert_eq!(delim.distinct_s2.collect::<String>(), String::from("hippo"));
210    }
211}