1use crate::{impl_display_with_writeable, LengthHint, Writeable};
6use core::fmt;
7
8#[derive(Debug)]
30#[allow(clippy::exhaustive_structs)] pub struct Replace<A, B, C> {
32 pub source: A,
34 pub needle: B,
36 pub replacement: C,
38}
39
40fn get_pi_bytes(needle: &str, matched_bytes: usize) -> usize {
48 let s = match needle.get(0..matched_bytes) {
49 Some(s) => s,
50 None => return 0,
51 };
52 for k in s
57 .char_indices()
58 .map(|(idx, _)| idx)
59 .rev()
60 .filter(|&idx| idx > 0)
61 {
62 if let Some(suffix) = s.as_bytes().get(s.len() - k..) {
64 if s.as_bytes().starts_with(suffix) {
65 return k;
66 }
67 }
68 }
69 0
70}
71
72struct ReplaceWriter<'a, W: ?Sized, C> {
77 sink: &'a mut W,
79 needle: &'a str,
81 replacement: &'a C,
83 remaining_needle: &'a str,
86}
87
88impl<'a, W, C> ReplaceWriter<'a, W, C>
89where
90 W: fmt::Write + ?Sized,
91 C: Writeable,
92{
93 fn new(sink: &'a mut W, needle: &'a str, replacement: &'a C) -> Self {
94 Self {
95 sink,
96 needle,
97 replacement,
98 remaining_needle: needle,
99 }
100 }
101
102 fn matched_len(&self) -> usize {
104 self.needle.len() - self.remaining_needle.len()
105 }
106
107 fn finalize(&mut self) -> fmt::Result {
109 let matched = self.matched_len();
110 if matched > 0 {
111 let slice = self.needle.get(0..matched).ok_or(fmt::Error)?;
112 self.sink.write_str(slice)?;
113 self.remaining_needle = self.needle;
114 }
115 Ok(())
116 }
117}
118
119impl<'a, W, C> fmt::Write for ReplaceWriter<'a, W, C>
120where
121 W: fmt::Write + ?Sized,
122 C: Writeable,
123{
124 fn write_str(&mut self, s: &str) -> fmt::Result {
125 for c in s.chars() {
126 self.write_char(c)?;
127 }
128 Ok(())
129 }
130
131 fn write_char(&mut self, c: char) -> fmt::Result {
132 if self.needle.is_empty() {
134 return self.sink.write_char(c);
135 }
136
137 let mut matched = self.matched_len();
138 while matched > 0 && !self.remaining_needle.starts_with(c) {
142 let old_j = matched;
143 matched = get_pi_bytes(self.needle, old_j);
144 let slice = self.needle.get(0..(old_j - matched)).ok_or(fmt::Error)?;
147 self.sink.write_str(slice)?;
148 self.remaining_needle = self.needle.get(matched..).ok_or(fmt::Error)?;
150 }
151
152 if self.remaining_needle.starts_with(c) {
154 self.remaining_needle = self
156 .remaining_needle
157 .get(c.len_utf8()..)
158 .ok_or(fmt::Error)?;
159 if self.remaining_needle.is_empty() {
160 self.replacement.write_to(self.sink)?;
162 self.remaining_needle = self.needle;
164 }
165 } else {
166 self.sink.write_char(c)?;
168 }
169 Ok(())
170 }
171}
172
173impl<A, C> Writeable for Replace<A, &str, C>
174where
175 A: Writeable,
176 C: Writeable,
177{
178 fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
181 let mut writer = ReplaceWriter::new(sink, self.needle, &self.replacement);
182 self.source.write_to(&mut writer)?;
183 writer.finalize()
184 }
185
186 fn writeable_length_hint(&self) -> LengthHint {
187 let source_hint = self.source.writeable_length_hint();
188 let needle_len = self.needle.len();
189 let replacement_hint = self.replacement.writeable_length_hint();
190
191 if let Some(r_upper) = replacement_hint.1 {
193 if replacement_hint.0 == r_upper && needle_len == r_upper {
194 return source_hint;
195 }
196 }
197
198 let mut lower = 0;
199 let mut upper = None;
200
201 if replacement_hint.0 >= needle_len {
204 lower = source_hint.0;
205 }
206
207 if let Some(r_upper) = replacement_hint.1 {
210 if r_upper <= needle_len {
211 upper = source_hint.1;
212 }
213 }
214
215 LengthHint(lower, upper)
216 }
217}
218
219impl_display_with_writeable!(Replace<A, &'a str, C>, #[cfg(feature = "alloc")], where 'a, A: Writeable, C: Writeable);
220
221#[test]
222fn test_replace() {
223 use crate::assert_writeable_eq;
224 use crate::concat::Concat;
225
226 let replace1 = Replace {
228 source: Concat("Hello", " 10 22 1101 33"),
229 needle: "10",
230 replacement: Concat("4", "4"),
231 };
232 assert_writeable_eq!(replace1, "Hello 44 22 1441 33");
233
234 let replace2 = Replace {
236 source: "Hello World",
237 needle: "",
238 replacement: "X",
239 };
240 assert_writeable_eq!(replace2, "Hello World");
241
242 let replace3 = Replace {
244 source: "Hello 10 World 10",
245 needle: "10",
246 replacement: "",
247 };
248 assert_writeable_eq!(replace3, "Hello World ");
249
250 let replace4 = Replace {
252 source: "Hello World",
253 needle: "10",
254 replacement: "X",
255 };
256 assert_writeable_eq!(replace4, "Hello World");
257
258 let replace5 = Replace {
260 source: "10 Hello World",
261 needle: "10",
262 replacement: "X",
263 };
264 assert_writeable_eq!(replace5, "X Hello World");
265
266 let replace6 = Replace {
268 source: "Hello World 10",
269 needle: "10",
270 replacement: "X",
271 };
272 assert_writeable_eq!(replace6, "Hello World X");
273
274 let replace7 = Replace {
276 source: "ababa",
277 needle: "aba",
278 replacement: "X",
279 };
280 assert_writeable_eq!(replace7, "Xba");
281
282 let replace8 = Replace {
284 source: "aab",
285 needle: "aac",
286 replacement: "X",
287 };
288 assert_writeable_eq!(replace8, "aab");
289
290 let replace9 = Replace {
292 source: "🚀 🛸 🚀🚀 🚁",
293 needle: "🚀",
294 replacement: "星",
295 };
296 assert_writeable_eq!(replace9, "星 🛸 星星 🚁");
297
298 let replace10 = Replace {
300 source: "🚀🚁",
301 needle: "🚀🛸",
302 replacement: "星",
303 };
304 assert_writeable_eq!(replace10, "🚀🚁");
305
306 let replace11 = Replace {
308 source: "🚀🚀🚁",
309 needle: "🚀🚀🛸",
310 replacement: "星",
311 };
312 assert_writeable_eq!(replace11, "🚀🚀🚁");
313
314 let replace12 = Replace {
316 source: "🚀🚀🚀🛸",
317 needle: "🚀🚀🛸",
318 replacement: "星",
319 };
320 assert_writeable_eq!(replace12, "🚀星");
321}