ext_string/
lib.rs

1//! ExtString is an attempt to bring string functions from other programming languages to the Rust std String struct
2extern crate unicode_segmentation;
3
4use unicode_segmentation::UnicodeSegmentation;
5/// The trait that adds functionality to the String struct.
6pub trait ExtString {
7    /// Reverses order of characters
8    fn reverse(&self) -> String;
9
10    /// Pads the left side of a string by repeating the same character until 'pad_len' is reached.
11    /// If pad_len is shorter or equal to the character length, a simple cloned string will be returned.
12    fn pad_left(&self, pad_len: usize, c: char) -> String;
13    /// Pads the right side of a string by repeating the same character until 'pad_len' is reached.
14    /// If pad_len is shorter or equal to the character length, a simple cloned string will be returned.
15    fn pad_right(&self, pad_len: usize, c: char) -> String;
16    /// Pads the left side of a string by repeating the same string slice until 'pad_len' is reached.
17    /// If pad_len is shorter or equal to the character length, a simple cloned string will be returned.
18    fn pad_left_str(&self, pad_len: usize, s: &str) -> String;
19    /// Pads the right side of a string by repeating the same string slice until 'pad_len' is reached.
20    /// If pad_len is shorter or equal to the character length, a simple cloned string will be returned.
21    fn pad_right_str(&self, pad_len: usize, s: &str) -> String;
22    /// Checks that all characters in a string are numeric characters.
23    fn is_numeric(&self) -> bool;
24    /// Checks that all characters in a string are alphabetic characters.
25    fn is_alphabetic(&self) -> bool;
26    /// Checks that all characters in a string are either numeric or alphabetic.
27    fn is_alphanumeric(&self) -> bool;
28    /// Swaps upper case characters to lower case and vice versa.
29    fn swap_case(&self) -> String;
30}
31
32impl ExtString for String {
33    /// Reverses order of characters
34    fn reverse(&self) -> String {
35        let mut g: Vec<&str> =
36            UnicodeSegmentation::graphemes(self.as_str(), true).collect::<Vec<&str>>();
37        g.reverse();
38        g.join("")
39    }
40
41    /// Pads the left side of a string by repeating the same character until 'pad_len' is reached.
42    /// If pad_len is shorter or equal to the character length, a simple cloned string will be returned.
43    fn pad_left(&self, pad_len: usize, c: char) -> String {
44        let count = self.chars().count();
45        if pad_len <= count {
46            return self.clone();
47        }
48        let repeat = pad_len - count;
49        let mut pad = String::new();
50        for _ in 0..repeat {
51            pad.push(c);
52        }
53        pad.push_str(self);
54        pad
55    }
56
57    /// Pads the right side of a string by repeating the same character until 'pad_len' is reached.
58    /// If pad_len is shorter or equal to the character length, a simple cloned string will be returned.
59    fn pad_right(&self, pad_len: usize, c: char) -> String {
60        let count = self.chars().count();
61        if pad_len <= count {
62            return self.clone();
63        }
64        let repeat = pad_len - count;
65        let mut pad = String::new();
66        pad.push_str(self);
67        for _ in 0..repeat {
68            pad.push(c);
69        }
70        pad
71    }
72
73    fn pad_left_str(&self, pad_len: usize, s: &str) -> String {
74        let count = self.chars().count();
75        if pad_len <= count || s.is_empty() {
76            return self.clone();
77        }
78
79        let repeat = pad_len - count;
80        let repeat_len = s.chars().count();
81        let mut pad = String::new();
82        for index in 0..repeat {
83            pad.push(s.chars().nth(index % repeat_len).unwrap());
84        }
85        pad.push_str(self);
86        pad
87    }
88
89    fn pad_right_str(&self, pad_len: usize, s: &str) -> String {
90        let count = self.chars().count();
91        if pad_len <= count || s.is_empty() {
92            return self.clone();
93        }
94
95        let repeat = pad_len - count;
96        let repeat_len = s.chars().count();
97        let mut pad = String::new();
98        pad.push_str(self);
99        for index in 0..repeat {
100            pad.push(s.chars().nth(index % repeat_len).unwrap());
101        }
102        pad
103    }
104
105    /// Checks that all characters in a string are numeric characters.
106    fn is_numeric(&self) -> bool {
107        let f = |c: char| c.is_numeric();        
108        (!self.is_empty()) && self.chars().all(f)
109    }
110
111    /// Checks that all characters in a string are alphabetic characters.
112    fn is_alphabetic(&self) -> bool {
113        let f = |c: char| c.is_alphabetic();
114        (!self.is_empty()) && self.chars().all(f)
115    }
116
117    // Checks that all characters in a string are either numeric or alphabetic.
118    fn is_alphanumeric(&self) -> bool {
119        let f = |c: char| c.is_alphanumeric();
120        (!self.is_empty()) && self.chars().all(f)
121    }
122
123    fn swap_case(&self) -> String {
124        let mut s = String::with_capacity(self.capacity());
125        for c in self.chars() {
126            if c.is_lowercase() {
127                s.push_str(c.to_uppercase().collect::<String>().as_str());
128            } else if c.is_uppercase() {
129                s.push_str(c.to_lowercase().collect::<String>().as_str());
130            } else {
131                s.push(c);
132            }
133        }
134        s
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::ExtString;
141
142    #[test]
143    fn test_reverse() {
144        let original = String::from("123456789");
145        assert_eq!(original.reverse(), "987654321");
146        let chinese = String::from("汉字漢字");
147        assert_eq!(chinese.reverse(), "字漢字汉");
148        let mangled = String::from("גבאabc1汉字漢字");
149        assert_eq!(mangled.reverse(), "字漢字汉1cbaאבג");
150        let weird = String::from("नमस्ते्");
151        assert_eq!(weird.reverse(), "ते्स्मन");
152    }
153
154    #[test]
155    fn test_pad_left() {
156        let s = "12345";
157        let space = ' ';
158        assert_eq!("12345", String::from(s).pad_left(3, space));
159        assert_eq!("     12345", String::from(s).pad_left(10, space));
160    }
161
162    #[test]
163    fn test_pad_right() {
164        let s = "12345";
165        let space = ' ';
166        assert_eq!("12345", String::from(s).pad_right(3, space));
167        assert_eq!("12345     ", String::from(s).pad_right(10, space));
168    }
169
170    #[test]
171    fn test_pad_left_str() {
172        let s = "12345";
173        let padding = "qwerty";
174        assert_eq!("qwerty12345", String::from(s).pad_left_str(11, padding));
175        assert_eq!("qwertyqwe12345", String::from(s).pad_left_str(14, padding));
176    }
177
178    #[test]
179    fn test_pad_right_str() {
180        let s = "12345";
181        let padding = "qwerty";
182        assert_eq!("12345qwerty", String::from(s).pad_right_str(11, padding));
183        assert_eq!("12345qwertyqwe", String::from(s).pad_right_str(14, padding));
184    }
185
186    #[test]
187    fn test_is_numeric() {
188        assert!(String::from("123456").is_numeric());
189        assert!(String::from("000100").is_numeric());
190        assert!(!String::from("123v56").is_numeric());
191        assert!(!String::from("-123v56").is_numeric());
192    }
193
194    #[test]
195    fn test_is_alphabetic() {
196        assert!(String::from("abcאבג").is_alphabetic());
197        assert!(String::from("literal").is_alphabetic());
198        assert!(!String::from("v1234").is_alphabetic());
199        assert!(!String::from("6v7777").is_alphabetic());
200    }
201
202    #[test]
203    fn test_is_alphanumeric() {
204        assert!(String::from("ab123cאבג").is_alphanumeric());
205        assert!(String::from("5yu32bliteral").is_alphanumeric());
206        assert!(!String::from("!@567").is_alphanumeric());
207        assert!(!String::from("<(*^*)>").is_alphanumeric());
208    }
209
210    #[test]
211    fn test_is_swap_case() {
212        let s1 = String::from("One Two Three");
213        assert_eq!("oNE tWO tHREE", s1.swap_case());
214
215        let s2 = String::from("Y SO SERIOUS???");
216        assert_eq!("y so serious???", s2.swap_case());
217
218        let s3 = String::from("משהו בעברית");
219        assert_eq!("משהו בעברית", s3.swap_case());
220    }
221}