1use crate::utf8::CharEncodeUtf8;
2
3pub struct Contains<'a, P>(pub &'a str, pub P);
4
5impl Contains<'_, &str> {
6 pub const fn const_eval(&self) -> bool {
7 crate::str::contains(self.0, self.1)
8 }
9}
10
11impl Contains<'_, char> {
12 pub const fn const_eval(&self) -> bool {
13 let haystack = self.0;
14 let ch = CharEncodeUtf8::new(self.1);
15 let needle = ch.as_str();
16 crate::str::contains(haystack, needle)
17 }
18}
19
20#[macro_export]
42macro_rules! contains {
43 ($haystack: expr, $pattern: expr) => {{
44 $crate::__ctfe::Contains($haystack, $pattern).const_eval()
45 }};
46}
47
48pub struct StartsWith<'a, P>(pub &'a str, pub P);
49
50impl StartsWith<'_, &str> {
51 pub const fn const_eval(&self) -> bool {
52 crate::str::starts_with(self.0, self.1)
53 }
54}
55
56impl StartsWith<'_, char> {
57 pub const fn const_eval(&self) -> bool {
58 let haystack = self.0.as_bytes();
59 let ch = CharEncodeUtf8::new(self.1);
60 let needle = ch.as_bytes();
61 crate::bytes::starts_with(haystack, needle)
62 }
63}
64
65#[macro_export]
87macro_rules! starts_with {
88 ($haystack: expr, $pattern: expr) => {{
89 $crate::__ctfe::StartsWith($haystack, $pattern).const_eval()
90 }};
91}
92
93pub struct EndsWith<'a, P>(pub &'a str, pub P);
94
95impl EndsWith<'_, &str> {
96 pub const fn const_eval(&self) -> bool {
97 crate::str::ends_with(self.0, self.1)
98 }
99}
100
101impl EndsWith<'_, char> {
102 pub const fn const_eval(&self) -> bool {
103 let haystack = self.0.as_bytes();
104 let ch = CharEncodeUtf8::new(self.1);
105 let needle = ch.as_bytes();
106 crate::bytes::ends_with(haystack, needle)
107 }
108}
109
110#[macro_export]
132macro_rules! ends_with {
133 ($haystack: expr, $pattern: expr) => {{
134 $crate::__ctfe::EndsWith($haystack, $pattern).const_eval()
135 }};
136}
137
138pub struct StripPrefix<'a, P>(pub &'a str, pub P);
139
140impl<'a> StripPrefix<'a, &str> {
141 pub const fn const_eval(&self) -> Option<&'a str> {
142 crate::str::strip_prefix(self.0, self.1)
143 }
144}
145
146pub struct StripSuffix<'a, P>(pub &'a str, pub P);
147
148impl<'a> StripSuffix<'a, &str> {
149 pub const fn const_eval(&self) -> Option<&'a str> {
150 crate::str::strip_suffix(self.0, self.1)
151 }
152}
153
154#[macro_export]
171macro_rules! strip_prefix {
172 ($s: expr, $prefix: expr) => {{
173 $crate::__ctfe::StripPrefix($s, $prefix).const_eval()
174 }};
175}
176
177#[macro_export]
194macro_rules! strip_suffix {
195 ($s: expr, $suffix: expr) => {{
196 $crate::__ctfe::StripSuffix($s, $suffix).const_eval()
197 }};
198}
199
200#[cfg(test)]
201mod tests {
202 use crate::unwrap;
203
204 #[test]
205 fn test_contains() {
206 const BANANAS: &str = "bananas";
207 const A: bool = contains!(BANANAS, "nana");
208 const B: bool = contains!(BANANAS, "apples");
209 const C: bool = contains!(BANANAS, 'c');
210 const D: bool = contains!(BANANAS, 'a');
211
212 assert_eq!([A, B, C, D], [true, false, false, true]);
213
214 let f = contains!("hello", "");
215 assert!(f);
216 }
217
218 #[test]
219 fn test_starts_with() {
220 const BANANAS: &str = "bananas";
221 const A: bool = starts_with!(BANANAS, "bana");
222 const B: bool = starts_with!(BANANAS, "nana");
223 const C: bool = starts_with!(BANANAS, 'b');
224 const D: bool = starts_with!(BANANAS, 'n');
225
226 assert_eq!([A, B, C, D], [true, false, true, false]);
227
228 let f = starts_with!("hello", "");
229 assert!(f);
230 }
231
232 #[test]
233 fn test_ends_with() {
234 const BANANAS: &str = "bananas";
235 const A: bool = ends_with!(BANANAS, "anas");
236 const B: bool = ends_with!(BANANAS, "nana");
237 const C: bool = ends_with!(BANANAS, 's');
238 const D: bool = ends_with!(BANANAS, 'b');
239
240 assert_eq!([A, B, C, D], [true, false, true, false]);
241
242 let f = ends_with!("hello", "");
243 assert!(f);
244 }
245
246 #[test]
247 fn test_strip_prefix() {
248 const R1: Option<&str> = strip_prefix!("foo:bar", "foo:");
249 const R2: Option<&str> = strip_prefix!("foo:bar", "bar");
250 const R3: Option<&str> = strip_prefix!("foofoo", "foo");
251 const R4: Option<&str> = strip_prefix!("", "");
252
253 assert_eq!(R1, Some("bar"));
254 assert_eq!(R2, None);
255 assert_eq!(R3, Some("foo"));
256 assert_eq!(R4, Some(""));
257
258 const FOO_BAR: &str = "foo:bar";
259 const BAR: &str = unwrap!(strip_prefix!(FOO_BAR, "foo:"));
260 assert_eq!(BAR, "bar");
261 }
262
263 #[test]
264 fn test_strip_suffix() {
265 const R1: Option<&str> = strip_suffix!("bar:foo", ":foo");
266 const R2: Option<&str> = strip_suffix!("bar:foo", "bar");
267 const R3: Option<&str> = strip_suffix!("foofoo", "foo");
268 const R4: Option<&str> = strip_suffix!("", "");
269
270 assert_eq!(R1, Some("bar"));
271 assert_eq!(R2, None);
272 assert_eq!(R3, Some("foo"));
273 assert_eq!(R4, Some(""));
274
275 const FOO_BAR: &str = "foo:bar";
276 const FOO: &str = unwrap!(strip_suffix!(FOO_BAR, ":bar"));
277 assert_eq!(FOO, "foo");
278 }
279
280 #[test]
281 fn test_find_runtime() {
282 use super::*;
283
284 let contains1 = Contains("hello world", "world");
286 assert!(contains1.const_eval());
287
288 let contains2 = Contains("hello", "x");
289 assert!(!contains2.const_eval());
290
291 let starts1 = StartsWith("hello", "he");
293 assert!(starts1.const_eval());
294
295 let starts2 = StartsWith("hello", "lo");
296 assert!(!starts2.const_eval());
297
298 let ends1 = EndsWith("hello", "lo");
300 assert!(ends1.const_eval());
301
302 let ends2 = EndsWith("hello", "he");
303 assert!(!ends2.const_eval());
304
305 let strip_pre = StripPrefix("hello world", "hello ");
307 let result = strip_pre.const_eval();
308 assert_eq!(result, Some("world"));
309
310 let strip_pre_none = StripPrefix("hello", "world");
311 let result_none = strip_pre_none.const_eval();
312 assert_eq!(result_none, None);
313
314 let strip_suf = StripSuffix("hello world", " world");
316 let result_suf = strip_suf.const_eval();
317 assert_eq!(result_suf, Some("hello"));
318
319 let strip_suf_none = StripSuffix("hello", "world");
320 let result_suf_none = strip_suf_none.const_eval();
321 assert_eq!(result_suf_none, None);
322
323 let contains_char = Contains("hello", 'e');
325 assert!(contains_char.const_eval());
326
327 let contains_char_none = Contains("hello", 'x');
328 assert!(!contains_char_none.const_eval());
329
330 let starts_char = StartsWith("hello", 'h');
331 assert!(starts_char.const_eval());
332
333 let starts_char_false = StartsWith("hello", 'e');
334 assert!(!starts_char_false.const_eval());
335
336 let ends_char = EndsWith("hello", 'o');
337 assert!(ends_char.const_eval());
338
339 let ends_char_false = EndsWith("hello", 'h');
340 assert!(!ends_char_false.const_eval());
341 }
342}