1extern crate unicode_segmentation;
3
4use unicode_segmentation::UnicodeSegmentation;
5pub trait ExtString {
7 fn reverse(&self) -> String;
9
10 fn pad_left(&self, pad_len: usize, c: char) -> String;
13 fn pad_right(&self, pad_len: usize, c: char) -> String;
16 fn pad_left_str(&self, pad_len: usize, s: &str) -> String;
19 fn pad_right_str(&self, pad_len: usize, s: &str) -> String;
22 fn is_numeric(&self) -> bool;
24 fn is_alphabetic(&self) -> bool;
26 fn is_alphanumeric(&self) -> bool;
28 fn swap_case(&self) -> String;
30}
31
32impl ExtString for String {
33 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 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 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 fn is_numeric(&self) -> bool {
107 let f = |c: char| c.is_numeric();
108 (!self.is_empty()) && self.chars().all(f)
109 }
110
111 fn is_alphabetic(&self) -> bool {
113 let f = |c: char| c.is_alphabetic();
114 (!self.is_empty()) && self.chars().all(f)
115 }
116
117 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}