1#![allow(unsafe_code)]
2
3use crate::slice::advance;
4use crate::utf8::CharEncodeUtf8;
5
6use super::str::StrBuf;
7
8pub struct Replace<I, P, O>(pub I, pub P, pub O);
9
10impl Replace<&str, &str, &str> {
11 pub const fn output_len(&self) -> usize {
12 let Self(mut input, replace_from, replace_to) = *self;
13
14 if replace_from.is_empty() {
15 let input_chars = crate::utf8::str_count_chars(self.0);
16 input.len() + (input_chars + 1) * replace_to.len()
17 } else {
18 let mut ans = 0;
19 while let Some((pos, remain)) = crate::str::next_match(input, replace_from) {
20 ans += pos + replace_to.len();
21 input = remain;
22 }
23 ans += input.len();
24 ans
25 }
26 }
27
28 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
29 let Self(input, replace_from, replace_to) = *self;
30
31 let mut buf = [0; N];
32 let mut pos = 0;
33
34 macro_rules! push {
35 ($x: expr) => {{
36 buf[pos] = $x;
37 pos += 1;
38 }};
39 }
40
41 if replace_from.is_empty() {
42 let mut input = input.as_bytes();
43 let replace_to = replace_to.as_bytes();
44 loop {
45 let mut k = 0;
46 while k < replace_to.len() {
47 push!(replace_to[k]);
48 k += 1;
49 }
50
51 let count = match crate::utf8::next_char(input) {
52 Some((_, count)) => count,
53 None => break,
54 };
55
56 let mut i = 0;
57 while i < count {
58 push!(input[i]);
59 i += 1;
60 }
61
62 input = advance(input, count);
63 }
64 } else {
65 let mut input = input;
66 let replace_to = replace_to.as_bytes();
67
68 while let Some((pos, remain)) = crate::str::next_match(input, replace_from) {
69 let mut i = 0;
70 while i < pos {
71 push!(input.as_bytes()[i]);
72 i += 1;
73 }
74 let mut k = 0;
75 while k < replace_to.len() {
76 push!(replace_to[k]);
77 k += 1;
78 }
79 input = remain;
80 }
81
82 let input = input.as_bytes();
83 let mut i = 0;
84 while i < input.len() {
85 push!(input[i]);
86 i += 1;
87 }
88 }
89
90 assert!(pos == N);
91 unsafe { StrBuf::new_unchecked(buf) }
92 }
93}
94
95impl Replace<&str, char, &str> {
96 pub const fn output_len(&self) -> usize {
97 let ch = CharEncodeUtf8::new(self.1);
98 Replace(self.0, ch.as_str(), self.2).output_len()
99 }
100 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
101 let ch = CharEncodeUtf8::new(self.1);
102 Replace(self.0, ch.as_str(), self.2).const_eval()
103 }
104}
105
106#[macro_export]
124macro_rules! replace {
125 ($s: expr, $from: expr, $to: expr) => {{
126 const OUTPUT_LEN: usize = $crate::__ctfe::Replace($s, $from, $to).output_len();
127 const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
128 $crate::__ctfe::Replace($s, $from, $to).const_eval();
129 OUTPUT_BUF.as_str()
130 }};
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_replace() {
139 macro_rules! testcase {
140 ($input: expr, $from: expr, $to: expr) => {{
141 const OUTPUT_LEN: usize = Replace($input, $from, $to).output_len();
142 const OUTPUT_BUF: StrBuf<OUTPUT_LEN> = Replace($input, $from, $to).const_eval();
143 const OUTPUT: &str = OUTPUT_BUF.as_str();
144
145 let ans = $input.replace($from, $to);
146 assert_eq!(OUTPUT, &*ans, "ans = {:?}", ans);
147 assert_eq!(OUTPUT_LEN, ans.len());
148 }};
149 }
150
151 testcase!("", "", "");
152 testcase!("", "", "a");
153 testcase!("", "a", "");
154 testcase!("", "a", "b");
155 testcase!("a", "", "b");
156 testcase!("asd", "", "b");
157 testcase!("aba", "a", "c");
158 testcase!("this is old", "old", "new");
159 testcase!("我", "", "1");
160 testcase!("我", "", "我");
161 testcase!("我", "我", "");
162 testcase!("aaaa", "aa", "bb");
163 testcase!("run / v4", " ", "");
164 testcase!("token", " ", "");
165 testcase!("v4 / udp", " ", "");
166 testcase!("v4 / upnp", "p", "");
167
168 testcase!("", 'a', "");
169 testcase!("", 'a', "b");
170 testcase!("aba", 'a', "c");
171 testcase!("run / v4", ' ', "");
172 testcase!("token", ' ', "");
173 testcase!("v4 / udp", ' ', "");
174 testcase!("我", '我', "");
175 }
176
177 #[test]
178 fn test_replace_runtime() {
179 let replace1 = Replace("hello world", "world", "rust");
181 assert_eq!(replace1.output_len(), 10);
182 let buf1: StrBuf<10> = replace1.const_eval();
183 assert_eq!(buf1.as_str(), "hello rust");
184
185 let replace2 = Replace("aaa", "a", "bb");
186 assert_eq!(replace2.output_len(), 6);
187 let buf2: StrBuf<6> = replace2.const_eval();
188 assert_eq!(buf2.as_str(), "bbbbbb");
189
190 let replace3 = Replace("test", "x", "y");
191 assert_eq!(replace3.output_len(), 4);
192 let buf3: StrBuf<4> = replace3.const_eval();
193 assert_eq!(buf3.as_str(), "test");
194
195 let replace_empty = Replace("", "a", "b");
196 let len_empty = replace_empty.output_len();
197 assert_eq!(len_empty, 0);
198
199 let replace_char = Replace("hello", 'l', "L");
201 assert_eq!(replace_char.output_len(), 5);
202 let buf_char: StrBuf<5> = replace_char.const_eval();
203 assert_eq!(buf_char.as_str(), "heLLo");
204 }
205
206 #[test]
207 fn test_replace_empty_pattern() {
208 let r1 = Replace("", "", "");
213 assert_eq!(r1.output_len(), 0);
214 let buf1: StrBuf<0> = r1.const_eval();
215 assert_eq!(buf1.as_str(), "");
216
217 let r2 = Replace("", "", "x");
219 assert_eq!(r2.output_len(), 1);
220 let buf2: StrBuf<1> = r2.const_eval();
221 assert_eq!(buf2.as_str(), "x");
222
223 let r3 = Replace("a", "", "x");
225 assert_eq!(r3.output_len(), 3);
226 let buf3: StrBuf<3> = r3.const_eval();
227 assert_eq!(buf3.as_str(), "xax");
228
229 let r4 = Replace("ab", "", "x");
231 assert_eq!(r4.output_len(), 5);
232 let buf4: StrBuf<5> = r4.const_eval();
233 assert_eq!(buf4.as_str(), "xaxbx");
234
235 let r5 = Replace("我", "", "x");
237 assert_eq!(r5.output_len(), 5);
238 let buf5: StrBuf<5> = r5.const_eval();
239 assert_eq!(buf5.as_str(), "x我x");
240
241 let r6 = Replace("我好", "", "x");
243 assert_eq!(r6.output_len(), 9);
244 let buf6: StrBuf<9> = r6.const_eval();
245 assert_eq!(buf6.as_str(), "x我x好x");
246 }
247}