1use alloc::{borrow::Cow, string::String};
2
3mod sealed {
4 pub trait Sealed {}
5
6 impl Sealed for char {}
7 impl Sealed for &str {}
8 impl Sealed for &&str {}
9 impl Sealed for &[char] {}
10
11 impl<const N: usize> Sealed for [char; N] {}
12 impl<const N: usize> Sealed for &[char; N] {}
13
14 impl<F> Sealed for F where F: FnMut(char) -> bool {}
15}
16
17#[inline]
18fn char_eq_str(c: char, s: &str) -> bool {
19 let mut buffer = [0; 4];
20
21 c.encode_utf8(&mut buffer) == s
22}
23
24fn replace_chars<'a, F>(s: &'a str, mut from: F, to: &str, mut count: usize) -> Cow<'a, str>
25where
26 F: FnMut(char) -> bool, {
27 if count == 0 {
28 return Cow::Borrowed(s);
29 }
30
31 let mut new_s: Option<String> = None;
32 let mut start = 0;
33
34 for (p, c) in s.char_indices() {
35 if count == 0 {
36 break;
37 }
38
39 if from(c) {
40 let next = p + c.len_utf8();
41
42 if char_eq_str(c, to) {
43 count -= 1;
44
45 continue;
46 }
47
48 if let Some(new_s) = new_s.as_mut() {
49 new_s.push_str(&s[start..p]);
50 new_s.push_str(to);
51 } else {
52 let mut owned = String::with_capacity(s.len());
53
54 owned.push_str(&s[..p]);
55 owned.push_str(to);
56
57 new_s = Some(owned);
58 }
59
60 start = next;
61 count -= 1;
62 }
63 }
64
65 match new_s {
66 Some(mut new_s) => {
67 new_s.push_str(&s[start..]);
68
69 Cow::Owned(new_s)
70 },
71 None => Cow::Borrowed(s),
72 }
73}
74
75#[inline]
76fn replace_str<'a>(s: &'a str, from: &str, to: &str) -> Cow<'a, str> {
77 if from == to || !s.contains(from) {
78 Cow::Borrowed(s)
79 } else {
80 Cow::Owned(s.replace(from, to))
81 }
82}
83
84#[inline]
85fn replacen_str<'a>(s: &'a str, from: &str, to: &str, count: usize) -> Cow<'a, str> {
86 if count == 0 || from == to || !s.contains(from) {
87 Cow::Borrowed(s)
88 } else {
89 Cow::Owned(s.replacen(from, to, count))
90 }
91}
92
93pub trait Pattern: sealed::Sealed {
97 #[doc(hidden)]
99 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str>;
100
101 #[doc(hidden)]
103 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str>;
104}
105
106impl Pattern for char {
107 #[inline]
108 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str> {
109 replace_chars(s, |c| c == self, to, usize::MAX)
110 }
111
112 #[inline]
113 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str> {
114 replace_chars(s, |c| c == self, to, count)
115 }
116}
117
118impl Pattern for &str {
119 #[inline]
120 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str> {
121 replace_str(s, self, to)
122 }
123
124 #[inline]
125 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str> {
126 replacen_str(s, self, to, count)
127 }
128}
129
130impl Pattern for &&str {
131 #[inline]
132 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str> {
133 (*self).replace_from(s, to)
134 }
135
136 #[inline]
137 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str> {
138 (*self).replacen_from(s, to, count)
139 }
140}
141
142impl Pattern for &[char] {
143 #[inline]
144 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str> {
145 replace_chars(s, |c| self.iter().any(|from| c.eq(from)), to, usize::MAX)
146 }
147
148 #[inline]
149 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str> {
150 replace_chars(s, |c| self.iter().any(|from| c.eq(from)), to, count)
151 }
152}
153
154impl<const N: usize> Pattern for [char; N] {
155 #[inline]
156 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str> {
157 replace_chars(s, |c| self.iter().any(|from| c.eq(from)), to, usize::MAX)
158 }
159
160 #[inline]
161 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str> {
162 replace_chars(s, |c| self.iter().any(|from| c.eq(from)), to, count)
163 }
164}
165
166impl<const N: usize> Pattern for &[char; N] {
167 #[inline]
168 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str> {
169 replace_chars(s, |c| self.iter().any(|from| c.eq(from)), to, usize::MAX)
170 }
171
172 #[inline]
173 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str> {
174 replace_chars(s, |c| self.iter().any(|from| c.eq(from)), to, count)
175 }
176}
177
178impl<F> Pattern for F
179where
180 F: FnMut(char) -> bool,
181{
182 #[inline]
183 fn replace_from<'a>(self, s: &'a str, to: &str) -> Cow<'a, str> {
184 replace_chars(s, self, to, usize::MAX)
185 }
186
187 #[inline]
188 fn replacen_from<'a>(self, s: &'a str, to: &str, count: usize) -> Cow<'a, str> {
189 replace_chars(s, self, to, count)
190 }
191}