1pub use to_offset::*;
2
3pub trait SubstringReplace where Self:ToString {
6
7 fn substring<T: ToOffset>(&self, start: usize, end: T) -> &str;
11
12 fn substring_start(&self, end: i64) -> &str {
14 let end_index = if end < 0 { self.char_len().checked_sub(end.abs() as usize).unwrap_or(0) } else { end as usize };
15 self.substring(0, end_index)
16 }
17
18 fn substring_end(&self, start: i64) -> &str {
22 let max_index = self.char_len();
23 let start_index = if start < 0 { max_index.checked_sub(start.abs() as usize).unwrap_or(0) } else { start as usize };
24 self.substring(start_index, max_index)
25 }
26
27 #[deprecated(since = "0.2.1", note = "Use `substring` instead")]
31 fn substring_range(&self, start: usize, end: i64) -> &str {
32 self.substring(start, end)
33 }
34
35
36 fn substring_replace<T: ToOffset>(&self, replacement: &str, start: usize, end: T) -> String;
40
41 #[deprecated(since = "0.2.1", note = "Use `substring` instead")]
45 fn substring_replace_range(&self, replacement: &str, start: usize, end: i64) -> String {
46 self.substring_replace(replacement, start, end)
47 }
48
49
50 fn substring_replace_start(&self, replacement: &str, end: i64) -> String {
55 let end_index = if end < 0 { self.char_len().saturating_sub(end.abs() as usize) } else { end as usize };
56 self.substring_replace(replacement, 0, end_index)
57 }
58
59 fn substring_replace_end(&self, replacement: &str, start: i64) -> String {
64 let end = self.char_len();
65 let start_index = if start < 0 { end.saturating_sub(start.abs() as usize) } else { start as usize };
66 self.substring_replace(replacement, start_index, end)
67 }
68
69 fn substring_remove(&self, start: usize, end: usize) -> String {
74 self.substring_replace("", start, end)
75 }
76
77 fn substring_offset(&self, position: usize, length: i32) -> &str {
80 let (start, end) = position_and_offset_to_start_end(position, length);
81 self.substring(start, end)
82 }
83
84 fn substring_pull(&self, position: usize, length: i32) -> String {
90 let (start, end) = position_and_offset_to_start_end(position, length);
91 self.substring_replace("", start, end)
92 }
93
94 fn substring_insert(&self, replacement: &str, start: usize) -> String {
99 self.substring_replace(replacement, start, start)
100 }
101
102 fn to_start_byte_index(&self, start: usize) -> usize;
104
105 fn to_end_byte_index(&self, start: usize) -> usize;
107
108 fn char_len(&self) -> usize;
110
111 fn char_find(&self, pat: &str) -> Option<usize>;
113
114 fn char_rfind(&self, pat: &str) -> Option<usize>;
117
118 fn insert_adjacent(&self, insert: &str, pat: &str, before: bool, first: bool) -> String;
120
121 fn insert_before_first(&self, insert: &str, pat: &str) -> String {
123 self.insert_adjacent(insert, pat, true, true)
124 }
125
126 fn insert_before_last(&self, insert: &str, pat: &str) -> String {
128 self.insert_adjacent(insert, pat, true, false)
129 }
130
131 fn insert_after_first(&self, insert: &str, pat: &str) -> String {
133 self.insert_adjacent(insert, pat, false, true)
134 }
135
136 fn insert_after_last(&self, insert: &str, pat: &str) -> String {
138 self.insert_adjacent(insert, pat, false, false)
139 }
140
141 fn insert_between(&self, insert: &str, start_pat: &str, end_pat: &str) -> String {
143 if let Some(start_index) = self.char_find(start_pat) {
144 if let Some(end_index) = self.char_rfind(end_pat) {
145 return self.substring_replace(insert, start_index + 1, end_index);
146 }
147 }
148 self.to_string()
149 }
150
151 fn prepend(&self, insert: &str) -> String {
153 [insert.to_string(), self.to_string()].concat()
154 }
155
156 fn append(&self, insert: &str) -> String {
157 [self.to_string(), insert.to_string()].concat()
158 }
159
160}
161
162impl SubstringReplace for str {
163
164 fn substring<T: ToOffset>(&self, start: usize, end: T) -> &str {
167 let end_index = end.to_offset(self.char_len());
168 if end_index > start {
169 &self[self.to_start_byte_index(start)..self.to_end_byte_index(end_index)]
170 } else {
171 ""
172 }
173 }
174
175 fn substring_replace<T: ToOffset>(&self, replacement: &str, start: usize, end: T) -> String {
177 let end_index = end.to_offset(self.char_len());
178 [&self[0..self.to_start_byte_index(start)], replacement, &self[self.to_end_byte_index(end_index)..]].concat()
179 }
180
181 fn to_start_byte_index(&self, start: usize) -> usize {
184 char_index_to_byte_index(self, start, false)
185 }
186
187 fn to_end_byte_index(&self, end: usize) -> usize {
190 char_index_to_byte_index(self, end, true)
191 }
192
193 fn char_len(&self) -> usize {
196 self.char_indices().count()
197 }
198
199 fn char_find(&self, pat: &str) -> Option<usize>{
201 extract_char_index(self, pat, false)
202 }
203
204 fn char_rfind(&self, pat: &str) -> Option<usize>{
207 extract_char_index(self, pat, true)
208 }
209
210 fn insert_adjacent(&self, insert: &str, pat: &str, before: bool, first: bool) -> String {
212 if let Some(index) = extract_char_index(self, pat, !first) {
213 let rel_index = if before {
214 index
215 } else {
216 index + 1
217 };
218 self.substring_insert(insert, rel_index)
219 } else {
220 self.to_string()
221 }
222 }
223}
224
225fn char_index_to_byte_index(text: &str, char_index: usize, to_end: bool) -> usize {
229 let default_index = if to_end { text.len() } else { 0 };
230 text.char_indices().nth(char_index).map(|(i, _)| i).unwrap_or(default_index)
231}
232
233fn position_and_offset_to_start_end(position: usize, length: i32) -> (usize, usize) {
239 let reverse = length < 0;
240 let start = if reverse {
241 position.checked_sub(length.abs() as usize).unwrap_or(0)
242 } else {
243 position
244 };
245 let start_i32 = if start > i32::MAX as usize { i32::MAX } else { start as i32 };
246 let end_i32 = start_i32 + length.abs();
247 let end = if end_i32 < 0 {
248 0
249 } else {
250 end_i32 as usize
251 };
252 (start, end)
253}
254
255fn extract_char_index(text: &str, pat: &str, reverse: bool) -> Option<usize> {
257 let mut start_index: Option<usize> = None;
258 let pat_chars = pat.chars().collect::<Vec<_>>();
259 let pat_len = pat.char_len();
260 let text_chars = text.chars().collect::<Vec<_>>();
261 let num_text_chars = text_chars.len();
262 let range = 0..num_text_chars;
263 let mut next_pat_char_index = if reverse { pat_len - 1 } else { 0 };
264 let mut temp_pat_len = 0;
265 for tc_index in range {
266 let rel_index = if reverse { num_text_chars - 1 - tc_index } else { tc_index };
267 let tc = text_chars[rel_index];
268 if tc == pat_chars[next_pat_char_index] {
269 if !reverse && next_pat_char_index == 0 {
270 start_index = Some(rel_index);
271 }
272 if pat_len > 1 {
273 if reverse {
274 if next_pat_char_index > 0 {
275 next_pat_char_index -= 1;
276 }
277 } else {
278 next_pat_char_index += 1;
279 }
280 }
281 temp_pat_len += 1;
282 } else {
283 next_pat_char_index = if reverse { pat_len - 1 } else { 0 };
284 temp_pat_len = 0;
285 }
286 if temp_pat_len == pat_len {
287 if reverse {
288 start_index = Some(rel_index);
289 }
290 break;
291 }
292 }
293 start_index
294}