query_range/range/query_range_iterator.rs
1use std::ops::Range;
2use std::cmp::min;
3use super::utility::{ get_range, Shift, shift_range, shift_range_in_content, is_within };
4
5/// Iterates all found query within given content.
6///
7/// ### Examples
8///
9/// **Iteration:**
10/// ```
11/// use query_range::QueryRangeItr;
12///
13/// let query = "needle";
14/// let content = "haystackneedlehaystackneedlehaystack";
15/// let mut occurrences = QueryRangeItr::new(query, content);
16/// while let Some(next) = occurrences.next() {
17/// println!("{}", &content[next]);
18/// }
19/// ```
20///
21/// **Collecting to an Array:**
22/// ```
23/// use query_range::QueryRangeItr;
24///
25/// let query = "needle";
26/// let content = "haystackneedlehaystackneedlehaystack";
27/// let mut occurrences = QueryRangeItr::new(query, content);
28/// let needles: Vec<String> = occurrences.map(|range| String::from(&content[range])).collect();
29/// ```
30/// **Collecting to Strings:**
31/// ```
32/// use query_range::QueryRangeItr;
33///
34/// let query = "needle";
35/// let content = "haystackneedlehaystackneedlehaystack";
36/// let mut occurrences = QueryRangeItr::new(query, content);
37/// let needles: Vec<String> = occurrences.collect_strings();
38/// ```
39///
40/// **Transforming the query:**
41/// ```
42/// use query_range::QueryRangeItr;
43///
44/// let query = "needle";
45/// let content = "haystackneedlehaystackneedlehaystack";
46/// let result = QueryRangeItr::transform_query(query, content, |it| it.to_uppercase());
47/// ```
48///
49/// **Reassembling the content:**
50/// ```
51/// use query_range::QueryRangeItr;
52/// use query_range::utility::to_title_case;
53///
54/// let query = "needle";
55/// let content = "haystackneedlehaystackneedlehaystack";
56/// let result = QueryRangeItr::transform_all(
57/// query,
58/// content,
59/// |it| it.to_uppercase(), // query transform
60/// |it| to_title_case(it), // non-query transform
61/// );
62/// ```
63pub struct QueryRangeItr<'a> {
64 inverted: bool,
65 query: &'a str,
66 current_content: &'a str,
67 full_content: &'a str,
68 removed_count: usize,
69}
70
71// ----------------------------------------------------------------------------------------------- /
72
73/// Basic implementation
74impl<'a> QueryRangeItr<'a> {
75
76 /// Private, common initializer method.
77 fn new_base(query: &'a str, content: &'a str, inverted: bool) -> QueryRangeItr<'a> {
78 Self {
79 inverted,
80 query,
81 current_content: content,
82 full_content: content,
83 removed_count: 0,
84 }
85 }
86
87 /// Creates a new iterator with given content or query which will iterate each *found* instance
88 /// of the query.
89 pub fn new(query: &'a str, content: &'a str) -> QueryRangeItr<'a> {
90 Self::new_base(query, content, false)
91 }
92
93 /// Creates a new iterator with given content or query which will iterate the content in
94 /// between each *found* instance of the query.
95 pub fn new_inverted(query: &'a str, content: &'a str) -> QueryRangeItr<'a> {
96 Self::new_base(query, content, true)
97 }
98
99 /// Collects all iterated ranges and builds an array of strings from the original content at those ranges
100 pub fn collect_strings(&mut self) -> Vec<String> {
101 let content = self.full_content;
102 self.map(|range| String::from(&content[range])).collect()
103 }
104
105 /// Gets the next range that matches the given query.
106 fn next_standard(&mut self) -> Option<Range<usize>> {
107 let current_content = self.current_content;
108 let possible_range = get_range(self.query, current_content);
109 if let Some(range) = possible_range {
110 if is_within(current_content, &range) {
111 let next_start = range.end;
112 let possible_range = shift_range_in_content(range, Shift::Up(self.removed_count), self.full_content);
113 if let Some(range) = possible_range {
114 let start_len = current_content.len();
115 self.current_content = ¤t_content[next_start..];
116 self.removed_count += start_len - self.current_content.len();
117 Some(range)
118 } else {
119 None
120 }
121 } else {
122 None
123 }
124 } else {
125 None
126 }
127 }
128
129 /// Gets the next range that doesn't match the given query.
130 fn next_inverted(&mut self) -> Option<Range<usize>> {
131 let current_content = self.current_content;
132 let start_index: usize = 0;
133 let len = current_content.len();
134 let range = get_range(self.query, current_content).unwrap_or(len..len);
135 let end_index = range.start;
136 let next_start: usize = min(range.end, len);
137 let possible_range = shift_range(start_index..end_index, Shift::Up(self.removed_count));
138 self.current_content = ¤t_content[next_start..];
139 let start_length = len;
140 self.removed_count += start_length - self.current_content.len();
141 if current_content.len() > 0 {
142 possible_range
143 } else {
144 None
145 }
146 }
147}
148
149// ----------------------------------------------------------------------------------------------- /
150
151/// Transform methods
152impl<'a> QueryRangeItr<'a> {
153
154 /// Reassembles content, but transforms the query content or the non-query content
155 ///
156 /// **Example:**
157 /// ```
158 /// use query_range::QueryRangeItr;
159 ///
160 /// let query = "needle";
161 /// let content = "haystackneedlehaystackneedlehaystack";
162 /// let result = QueryRangeItr::transform(query, content, |it| it.to_uppercase(), false);
163 /// assert_eq!(result, "haystackNEEDLEhaystackNEEDLEhaystack");
164 /// ```
165 ///
166 /// **Parameters:**
167 /// - `query` - The search query
168 /// - `content` - The content to look for the query in
169 /// - `transform` - A transform closure to run on all query content
170 /// - `invert` - If `true`, applies the transform to the non-query content
171 pub fn transform<T>(
172 query: &'a str,
173 content: &'a str,
174 transform: T,
175 invert: bool,
176 ) -> String where T: Fn(&str) -> String {
177 let selects = Self::new_base(query, content, invert);
178 let non_selects = Self::new_base(query, content, !invert);
179 let transform_each = |range: Range<usize>| {
180 let start = range.clone().start;
181 let original = &content[range];
182 let value = transform(original);
183 (value, start)
184 };
185 let transform_rest = |range: Range<usize>| {
186 let start = range.clone().start;
187 let original = &content[range];
188 let value = String::from(original);
189 (value, start)
190 };
191 let mut selected_subs: Vec<(String, usize)> = selects.map(|range| transform_each(range)).collect();
192 let mut non_selected_subs: Vec<(String, usize)> = non_selects.map(|range| transform_rest(range)).collect();
193 selected_subs.append(&mut non_selected_subs);
194 let mut merged = selected_subs;
195 merged.sort_unstable_by(|s1,s2| s1.1.cmp(&s2.1));
196 let strings: Vec<String> = merged.iter().map(|s| s.0.clone()).collect();
197 strings.join("")
198 }
199
200 /// Reassembles content, but transforms the query content
201 ///
202 /// **Example:**
203 /// ```
204 /// use query_range::QueryRangeItr;
205 ///
206 /// let query = "needle";
207 /// let content = "haystackneedlehaystackneedlehaystack";
208 /// let result = QueryRangeItr::transform_query(query, content, |it| it.to_uppercase());
209 /// assert_eq!(result, "haystackNEEDLEhaystackNEEDLEhaystack");
210 /// ```
211 ///
212 /// **Parameters:**
213 /// - `query` - The search query
214 /// - `content` - The content to look for the query in
215 /// - `transform` - A transform closure to run on all query content
216 pub fn transform_query<T>(
217 query: &'a str,
218 content: &'a str,
219 transform: T,
220 ) -> String where T: Fn(&str) -> String {
221 Self::transform(query, content, transform, false)
222 }
223
224 /// Reassembles content, but transforms the non-query content
225 ///
226 /// **Example:**
227 /// ```
228 /// use query_range::QueryRangeItr;
229 ///
230 /// let query = "needle";
231 /// let content = "haystackneedlehaystackneedlehaystack";
232 /// let result = QueryRangeItr::transform_other(query, content, |it| it.to_uppercase());
233 /// assert_eq!(result, "HAYSTACKneedleHAYSTACKneedleHAYSTACK");
234 /// ```
235 ///
236 /// **Parameters:**
237 /// - `query` - The search query
238 /// - `content` - The content to look for the query in
239 /// - `transform` - A transform closure to run on all non-query content
240 pub fn transform_other<T>(
241 query: &'a str,
242 content: &'a str,
243 transform: T,
244 ) -> String where T: Fn(&str) -> String {
245 Self::transform(query, content, transform, true)
246 }
247
248 /// Reassembles content, but transforms both the query content and the non-query content
249 ///
250 /// **Example:**
251 /// ```
252 /// use query_range::QueryRangeItr;
253 /// use query_range::utility::to_title_case;
254 ///
255 /// let query = "needle";
256 /// let content = "haystackneedlehaystackneedlehaystack";
257 /// let result = QueryRangeItr::transform_all(
258 /// query,
259 /// content,
260 /// |it| it.to_uppercase(),
261 /// |it| to_title_case(it),
262 /// );
263 /// assert_eq!(result, "HaystackNEEDLEHaystackNEEDLEHaystack");
264 /// ```
265 ///
266 /// **Parameters:**
267 /// - `query` - The search query
268 /// - `content` - The content to look for the query in
269 /// - `transform_query` - A transform closure to run on all query content
270 /// - `transform_non_query` - If `true`, applies the transform to the non-query content
271 pub fn transform_all<TQ, TNQ>(
272 query: &'a str,
273 content: &'a str,
274 transform_query: TQ,
275 transform_non_query: TNQ,
276 ) -> String
277 where
278 TQ: Fn(&str) -> String,
279 TNQ: Fn(&str) -> String,
280 {
281 let transformed_content = Self::transform(query, content, &transform_query, false);
282 let transformed_query = transform_query(query);
283 let transformed = QueryRangeItr::transform(&transformed_query, &transformed_content, transform_non_query, true);
284 transformed
285 }
286}
287
288// Iterator implementation ----------------------------------------------------------------------- /
289
290impl<'a> Iterator for QueryRangeItr<'a> {
291 type Item = Range<usize>;
292
293 /// Gets next range of the query in the content.
294 fn next(&mut self) -> Option<Self::Item> {
295 if self.inverted {
296 self.next_inverted()
297 } else {
298 self.next_standard()
299 }
300 }
301}
302
303// Tests ----------------------------------------------------------------------------------------- /
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use super::super::utility::to_title_case;
309
310 #[test]
311 fn can_iterate_iter() {
312 let query = "needle";
313 let content = "haystackneedlehaystackneedlehaystack";
314 let mut occurrences = QueryRangeItr::new(query, content);
315 while let Some(next) = occurrences.next() {
316 assert_eq!(String::from(&content[next]), "needle");
317 }
318 }
319
320 #[test]
321 fn can_map_iter() {
322 let query = "needle";
323 let content = "haystackneedlehaystackneedlehaystack";
324 let occurrences = QueryRangeItr::new(query, content);
325 let needles: Vec<String> = occurrences.map(|range| String::from(&content[range])).collect();
326 assert_eq!(needles.len(), 2);
327 needles.iter().for_each(|n| assert_eq!(n, "needle"));
328 }
329
330 #[test]
331 fn can_collect_strings() {
332 let query = "needle";
333 let content = "haystackneedlehaystackneedlehaystack";
334 let mut occurrences = QueryRangeItr::new(query, content);
335 let needles: Vec<String> = occurrences.collect_strings();
336 assert_eq!(needles.len(), 2);
337 needles.iter().for_each(|n| assert_eq!(n, "needle"));
338 }
339
340 #[test]
341 fn can_transform_query() {
342 let query = "needle";
343 let content = "haystackneedlehaystackneedlehaystack";
344 let result = QueryRangeItr::transform_query(query, content, |it| it.to_uppercase());
345 assert_eq!(result, "haystackNEEDLEhaystackNEEDLEhaystack");
346 }
347
348 #[test]
349 fn can_reassemble_string() {
350 let query = "needle";
351 let content = "haystackneedlehaystackneedlehaystack";
352 let result = QueryRangeItr::transform_all(
353 query,
354 content,
355 |it| it.to_uppercase(),
356 |it| to_title_case(it),
357 );
358 assert_eq!(result, "HaystackNEEDLEHaystackNEEDLEHaystack");
359 }
360}