string_simple/
lib.rs

1pub mod builder {
2    use std::ops::AddAssign;
3
4    /// # Description
5    /// A simple struct to wrap the process of building strings.
6    /// You can append anything to the `StringBuilder` as long as the type provided implements the `ToString` trait.
7    ///
8    /// # Examples
9    ///
10    /// Simple example:
11    /// ```
12    /// use string_simple::builder::StringBuilder;
13    ///
14    /// let mut new_builder = StringBuilder::new();
15    ///
16    /// new_builder.append("This string has ")
17    ///     .append(30)
18    ///     .append(" characters.");
19    ///
20    /// // result = "This string has 30 characters"
21    /// let result = new_builder.build();
22    /// ```
23    ///
24    /// Simple loop example:
25    /// ```
26    /// use string_simple::builder::StringBuilder;
27    ///
28    /// let mut new_builder = StringBuilder::new();
29    /// let mut counter = 0;
30    /// const LOOP_COUNT: u8 = 10;
31    /// while counter < LOOP_COUNT {
32    ///     if counter % 2 == 0 {
33    ///         new_builder.append("even");
34    ///     } else {
35    ///         new_builder.append("odd");
36    ///     }
37    ///     if counter + 1 != LOOP_COUNT {
38    ///         new_builder.append(" ");
39    ///     }
40    ///     counter += 1;
41    /// }
42    /// // result = "even odd even odd..."
43    /// let result = new_builder.build();
44    /// ```
45    pub struct StringBuilder {
46        full_string: String,
47        current_len: usize
48    }
49
50    impl StringBuilder {
51        pub fn new() -> Self {
52            StringBuilder {
53                full_string: String::with_capacity(0),
54                current_len: 0
55            }
56        }
57
58        pub fn append<T>(
59            &mut self,
60            t: T
61        ) -> &mut Self
62            where T: ToString
63        {
64            let str = t.to_string();
65            let mut len = self.current_len;
66            len.add_assign(AsRef::<str>::as_ref(&str).len());
67            let mut buf = String::with_capacity(len);
68            buf.push_str(self.full_string.as_ref());
69            buf.push_str(str.as_ref());
70            self.current_len = len;
71            self.full_string = buf;
72            self
73        }
74
75        pub fn build(&self) -> String {
76            self.full_string.clone()
77        }
78    }
79}
80
81
82pub mod modify {
83    use std::ops::AddAssign;
84
85    /// # Description
86    ///
87    /// Adds a new string onto the base string. The provided base string will be modified.
88    /// All arguments assume that all characters are UTF-8.
89    ///
90    /// # Arguments
91    ///
92    /// * `base` - The base string that will be modified.
93    /// * `append` - The new string that will be added onto the base.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use string_simple::modify::append;
99    /// let mut str1 = String::from("base string");
100    /// let str2 = String::from("!");
101    ///
102    /// // str1 will now be: "base string!"
103    /// append(&mut str1, &str2);
104    /// ```
105    pub fn append<S>(base: &mut String, append: &S)
106        where S: ToString
107    {
108        let str = append.to_string();
109        let mut len = base.len();
110        len.add_assign(AsRef::<str>::as_ref(&str).len());
111        let mut buf = String::with_capacity(len);
112        buf.push_str(base.as_ref());
113        buf.push_str(str.as_ref());
114        *base = buf;
115    }
116
117    /// # Description
118    ///
119    /// Replaces all occurrences of a substring in a base string with another given term. The base string provided will be modified.
120    /// All provided arguments are assumed to be valid UTF-8 chars.
121    ///
122    /// # Arguments
123    ///
124    /// * `base` -  The full base string. The base string will be modified by the function call.
125    /// * `find` - The substring we are going to replace in the `base` string.
126    /// * `replace` - The new string that replaces all occurrences of the `find` string.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// use string_simple::modify::replace;
132    ///
133    /// let mut base_string = String::from("This is my base string!");
134    /// let find_string = String::from("base");
135    /// let replace_string = String::from("modified");
136    ///
137    /// // The base string will be "This is my modified string!"
138    /// replace(&mut base_string, &find_string, &replace_string);
139    /// ```
140    ///
141    pub fn replace<S, R>(base: &mut String, find: &S, replace: &R)
142        where S: ToString, R: ToString
143    {
144        let t = base.to_string();
145        let base_str_bytes = t.as_bytes();
146        let t = find.to_string();
147        let sub_str_bytes = t.as_bytes();
148        let t = replace.to_string();
149        let repl_len = AsRef::<str>::as_ref(&t).len();
150        assert!(base_str_bytes.len() >= sub_str_bytes.len());
151
152        let mut replaced_string = String::with_capacity(0);
153        let mut replaced_len = 0usize;
154        let mut current_base_pos = 0usize;
155        while current_base_pos < base_str_bytes.len() {
156
157            let mut current_sub_pos = 0usize;
158            let mut current_base_test = current_base_pos;
159            'inner: while current_sub_pos < sub_str_bytes.len()
160                && current_base_test < base_str_bytes.len() {
161
162                if &base_str_bytes[current_base_test] == &sub_str_bytes[current_sub_pos] {
163
164                    if current_sub_pos == sub_str_bytes.len() -1 {
165                        let mut temp_len = replaced_len;
166                        temp_len.add_assign(repl_len);
167                        let mut temp_str = String::with_capacity(temp_len);
168                        temp_str.push_str(replaced_string.as_ref());
169                        temp_str.push_str(t.as_ref());
170                        replaced_len = temp_len;
171                        replaced_string = temp_str;
172                        current_base_pos = current_base_test;
173                        break 'inner;
174                    }
175
176                    current_sub_pos = current_sub_pos + 1;
177                    current_base_test = current_base_test + 1;
178                    continue 'inner;
179                }
180                let mut temp_len = replaced_len;
181                temp_len.add_assign(1);
182                let mut temp_string = String::with_capacity(temp_len);
183                temp_string.push_str(replaced_string.as_ref());
184                temp_string.push_str(unsafe {
185                    std::str::from_utf8_unchecked(&base_str_bytes[current_base_pos..current_base_pos +1])
186                });
187                replaced_len = temp_len;
188                replaced_string = temp_string;
189                break 'inner;
190            }
191
192            current_base_pos = current_base_pos + 1;
193        }
194
195        *base = replaced_string
196    }
197}
198
199
200pub mod compare {
201
202    /// # Description
203    ///
204    /// Finds all sub-string occurrences and ranges the sub-strings occur at.
205    /// All provided arguments are assumed to be valid UTF-8 chars.
206    ///
207    /// # Arguments
208    /// * `base` - The base string we are searching.
209    /// * `find` - The sub-string we are trying to find all occurrences of.
210    ///
211    /// # Output
212    /// * `Vec<(usize, usize)>` - A vector of tuples containing 2 usize numbers. The first number is the starting position of the occurrence, the second number is where the occurrence ends. The vector will be empty if no occurrences were found.
213    ///
214    /// # Examples
215    ///
216    /// ```
217    /// use string_simple::compare::find_all_exact;
218    ///
219    /// let base_string = String::from("This is my test string! test test!");
220    /// let find_string = String::from("test");
221    ///
222    /// // output in this case will look like this: [(11, 15), (24, 28), (29, 33)]
223    /// let result = find_all_exact(&base_string, &find_string);
224    /// ```
225    pub fn find_all_exact<B, S>(
226        base: &B,
227        find: &S
228    ) -> Vec<(usize, usize)>
229        where B: ToString, S: ToString
230    {
231        let t = base.to_string();
232        let base_string_bytes = t.as_bytes();
233        let t = find.to_string();
234        let find_string_bytes = t.as_bytes();
235        assert!(base_string_bytes.len() >= find_string_bytes.len());
236        let mut matches: Vec<(usize, usize)> = vec![];
237
238        let mut current_base_pos = 0usize;
239        while current_base_pos < base_string_bytes.len() {
240
241            let mut current_find_pos = 0usize;
242            let mut current_base_test = current_base_pos;
243            'inner: while current_find_pos < find_string_bytes.len()
244                && current_base_test < base_string_bytes.len() {
245
246                if &base_string_bytes[current_base_test] == &find_string_bytes[current_find_pos] {
247
248                    if current_find_pos == find_string_bytes.len() -1 {
249                        matches.push((current_base_pos, current_base_test + 1));
250                        break 'inner;
251                    }
252
253                    current_find_pos = current_find_pos + 1;
254                    current_base_test = current_base_test + 1;
255                    continue 'inner;
256                }
257                break 'inner;
258            }
259            current_base_pos = current_base_pos + 1;
260        }
261        matches
262    }
263
264    /// # Description
265    /// Find the first occurrence of a sub-string within a base string.
266    /// All arguments are assumed to be valid UTF-8 characters.
267    ///
268    /// # Arguments
269    /// * `base` - The provided base string we are searching.
270    /// * `find` - The substring we are trying to find.
271    ///
272    /// # Output
273    /// * `Option<(usize, usize)>` - Optional tuple containing the start and end positions in the `base`  where the first `find` can be found. Returns `None` if the `find` was not found.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use string_simple::compare::contains;
279    /// let base_string = String::from("This is my test string! test test!");
280    /// let find_string = String::from("test");
281    ///
282    /// // output in this case will look like this: Some((11, 15))
283    /// let result = contains(&base_string, &find_string);
284    /// ```
285    pub fn contains<B, S>(
286        base: &B,
287        find: &S
288    ) -> Option<(usize, usize)>
289        where B: ToString, S: ToString
290    {
291        let t = base.to_string();
292        let base_string_bytes = t.as_bytes();
293        let t = find.to_string();
294        let find_string_bytes = t.as_bytes();
295        assert!(base_string_bytes.len() >= find_string_bytes.len());
296
297        let mut current_base_pos = 0usize;
298        while current_base_pos < base_string_bytes.len() {
299
300            let mut current_find_pos = 0usize;
301            let mut base_pos_test = current_base_pos;
302            'inner: while current_find_pos < find_string_bytes.len()
303                && base_pos_test < base_string_bytes.len(){
304
305                if &base_string_bytes[base_pos_test] == &find_string_bytes[current_find_pos] {
306
307                    if current_find_pos == find_string_bytes.len() -1 {
308                        return Some((current_base_pos, base_pos_test + 1));
309                    }
310
311                    current_find_pos = current_find_pos + 1;
312                    base_pos_test = base_pos_test + 1;
313                    continue 'inner;
314                }
315                break 'inner;
316            }
317            current_base_pos = current_base_pos + 1;
318        }
319        return None;
320    }
321}
322
323
324#[cfg(test)]
325mod tests {
326    use std::fmt::Display;
327    use super::*;
328
329    #[test]
330    fn test_append1() {
331        let mut str1 = String::from("123");
332        let str2 = String::from("test");
333        modify::append(&mut str1, &str2);
334        assert_eq!(String::from("123test"), str1);
335    }
336
337    #[test]
338    fn test_append2() {
339        let mut str1 = String::from("123");
340        let str2 = String::from("test");
341        modify::append(&mut str1, &str2);
342        assert_eq!(String::from("123test"), str1);
343    }
344
345    #[test]
346    fn test_replace() {
347        let mut str1 = String::from("123123123test123123123test12");
348        let str2 = String::from("test");
349        let str3 = String::from("replaced");
350        modify::replace(&mut str1, &str2, &str3);
351        assert_eq!("123123123replaced123123123replaced12", str1);
352    }
353
354    #[test]
355    fn test_contains() {
356        let str1 = String::from("123123123test123123123");
357        let str2 = String::from("test");
358        let result = compare::contains(&str1, &str2);
359        assert_eq!(str2, &str1[result.unwrap().0..result.unwrap().1]);
360    }
361
362    #[test]
363    fn test_find_all_exact1() {
364        let str1 = String::from("123test113test444testtest");
365        let str2 = String::from("test");
366        let result = compare::find_all_exact(&str1, &str2);
367        let expected: Vec<(usize, usize)> = vec![(3,7), (10, 14), (17, 21), (21, 25)];
368        assert_eq!(expected, result);
369    }
370
371    #[test]
372    fn test_find_all_exact2() {
373        let str1 = String::from("bbbbbbbbbbbbbbbbbb");
374        let str2 = String::from("bbb");
375        let result = compare::find_all_exact(&str1, &str2);
376        let expected: Vec<(usize, usize)> = vec![(0, 3), (1, 4), (2, 5), (3, 6), (4, 7), (5, 8), (6, 9), (7, 10), (8, 11), (9, 12), (10, 13), (11, 14), (12, 15), (13, 16), (14, 17), (15, 18)];
377        assert_eq!(expected, result);
378    }
379
380    struct ToStringStruct {
381        a_string: String,
382        a_number: i32
383    }
384
385    impl Display for ToStringStruct {
386        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387            write!(f, r#"{{ "a_string": "{}", "a_number": "{}" }}"#, self.a_string, self.a_number)
388        }
389    }
390
391
392    #[test]
393    fn test_stringbuilder1() {
394        let mut string_builder = builder::StringBuilder::new();
395        string_builder
396            .append("this")
397            .append("is")
398            .append("a")
399            .append("test");
400
401        assert_eq!(string_builder.build(), "thisisatest".to_string());
402    }
403
404    #[test]
405    fn test_stringbuilder2() {
406        let mut string_builder = builder::StringBuilder::new();
407        string_builder.append("this")
408            .append("is")
409            .append("another")
410            .append("test");
411        assert_eq!(string_builder.build(), "thisisanothertest".to_string());
412
413        string_builder
414            .append("this")
415            .append("is")
416            .append("another")
417            .append("test");
418        assert_eq!(string_builder.build(), "thisisanothertestthisisanothertest".to_string());
419    }
420
421    #[test]
422    fn test_stringbuilder3() {
423        let mut string_builder = builder::StringBuilder::new();
424        let to_string_struct = ToStringStruct {
425            a_string: String::from("struct_string"),
426            a_number: 4321
427        };
428        string_builder
429            .append(1234)
430            .append('c')
431            .append("test")
432            .append(55usize)
433            .append(to_string_struct);
434
435        assert_eq!(string_builder.build(), "1234ctest55{ \"a_string\": \"struct_string\", \"a_number\": \"4321\" }".to_string());
436    }
437}