1 | /// Replaces all the instances of `$pattern` in `$input` |
2 | /// (a `&'static str` constant) with `$replace_with` (a `&'static str` constant). |
3 | /// |
4 | /// # Signature |
5 | /// |
6 | /// This macro acts like a function of this signature: |
7 | /// ```rust |
8 | /// # trait Pattern {} |
9 | /// fn str_replace( |
10 | /// input: &'static str, |
11 | /// pattern: impl Pattern, |
12 | /// replace_with: &'static str, |
13 | /// ) -> &'static str |
14 | /// # {"" } |
15 | /// ``` |
16 | /// and is evaluated at compile-time. |
17 | /// |
18 | /// Where `pattern` can be any of these types: |
19 | /// |
20 | /// - `&'static str` |
21 | /// |
22 | /// - `char` |
23 | /// |
24 | /// - `u8`: required to be ascii (`0` up to `127` inclusive). |
25 | /// |
26 | /// # Example |
27 | /// |
28 | /// |
29 | /// ```rust |
30 | /// use const_format::str_replace; |
31 | /// |
32 | /// // Passing a string pattern |
33 | /// assert_eq!( |
34 | /// str_replace!("The incredible shrinking man." , "i" , "eee" ), |
35 | /// "The eeencredeeeble shreeenkeeeng man." , |
36 | /// ); |
37 | /// |
38 | /// // Passing a char pattern |
39 | /// assert_eq!( |
40 | /// str_replace!("The incredible shrinking man." , ' ' , "---" ), |
41 | /// "The---incredible---shrinking---man." , |
42 | /// ); |
43 | /// |
44 | /// // Passing an ascii u8 pattern. |
45 | /// assert_eq!( |
46 | /// str_replace!("The incredible shrinking man." , b'i' , "eee" ), |
47 | /// "The eeencredeeeble shreeenkeeeng man." , |
48 | /// ); |
49 | /// |
50 | /// // Removing all instances of the pattern |
51 | /// assert_eq!( |
52 | /// str_replace!("remove haire" , "re" , "" ), |
53 | /// "move hai" , |
54 | /// ); |
55 | /// |
56 | /// // This shows that all the arguments can be `const`s, they don't have to be literals. |
57 | /// { |
58 | /// const IN: &str = "Foo Boo Patoo" ; |
59 | /// const REPLACING: &str = "oo" ; |
60 | /// const REPLACE_WITH: &str = "uh" ; |
61 | /// assert_eq!(str_replace!(IN, REPLACING, REPLACE_WITH), "Fuh Buh Patuh" ); |
62 | /// } |
63 | /// ``` |
64 | /// |
65 | /// [`str::replace`]: https://doc.rust-lang.org/std/primitive.str.html#method.replace |
66 | #[macro_export ] |
67 | macro_rules! str_replace { |
68 | ($input:expr, $pattern:expr, $replace_with:expr $(,)*) => {{ |
69 | const ARGS_OSRCTFL4A: $crate::__str_methods::ReplaceInput = |
70 | $crate::__str_methods::ReplaceInputConv($input, $pattern, $replace_with).conv(); |
71 | |
72 | { |
73 | const OB: &[$crate::pmr::u8; ARGS_OSRCTFL4A.replace_length()] = |
74 | &ARGS_OSRCTFL4A.replace(); |
75 | |
76 | const OS: &$crate::pmr::str = unsafe { $crate::__priv_transmute_bytes_to_str!(OB) }; |
77 | |
78 | OS |
79 | } |
80 | }}; |
81 | } |
82 | |
83 | /// Creates a `&'static str` by repeating a `&'static str` constant `times` times |
84 | /// |
85 | /// This is evaluated at compile-time. |
86 | /// |
87 | /// # Example |
88 | /// |
89 | /// ```rust |
90 | /// use const_format::str_repeat; |
91 | /// |
92 | /// { |
93 | /// const OUT: &str = str_repeat!("hi " , 4); |
94 | /// assert_eq!(OUT, "hi hi hi hi " ) |
95 | /// } |
96 | /// { |
97 | /// const IN: &str = "bye " ; |
98 | /// const REPEAT: usize = 5; |
99 | /// const OUT: &str = str_repeat!(IN, REPEAT); |
100 | /// assert_eq!(OUT, "bye bye bye bye bye " ) |
101 | /// } |
102 | /// |
103 | /// ``` |
104 | /// |
105 | /// ### Failing |
106 | /// |
107 | /// If this macro would produce too large a string, |
108 | /// it causes a compile-time error. |
109 | /// |
110 | /// ```compile_fail |
111 | /// const_format::str_repeat!("hello" , usize::MAX / 4); |
112 | /// ``` |
113 | /// |
114 | #[cfg_attr ( |
115 | feature = "__test" , |
116 | doc = r##" |
117 | ```rust |
118 | const_format::str_repeat!("hello", usize::MAX.wrapping_add(4)); |
119 | ``` |
120 | "## |
121 | )] |
122 | #[macro_export ] |
123 | macro_rules! str_repeat { |
124 | ($string:expr, $times:expr $(,)*) => {{ |
125 | const P_OSRCTFL4A: &$crate::__str_methods::StrRepeatArgs = |
126 | &$crate::__str_methods::StrRepeatArgs($string, $times); |
127 | |
128 | { |
129 | use $crate::__hidden_utils::PtrToRef; |
130 | use $crate::pmr::{str, transmute, u8}; |
131 | |
132 | const P: &$crate::__str_methods::StrRepeatArgs = P_OSRCTFL4A; |
133 | |
134 | $crate::pmr::respan_to! { |
135 | ($string) |
136 | const _ASSERT_VALID_LEN: () = P.assert_valid(); |
137 | } |
138 | |
139 | const OUT_B: &[u8; P.out_len] = &unsafe { |
140 | let ptr = P.str.as_ptr() as *const [u8; P.str_len]; |
141 | transmute::<[[u8; P.str_len]; P.repeat], [u8; P.out_len]>( |
142 | [*PtrToRef { ptr }.reff; P.repeat], |
143 | ) |
144 | }; |
145 | const OUT_S: &str = unsafe { $crate::__priv_transmute_bytes_to_str!(OUT_B) }; |
146 | OUT_S |
147 | } |
148 | }}; |
149 | } |
150 | |
151 | /// Replaces a substring in a `&'static str` constant. |
152 | /// Returns both the new resulting `&'static str`, and the replaced substring. |
153 | /// |
154 | /// # Signature |
155 | /// |
156 | /// This macro acts like a function of this signature: |
157 | /// ```rust |
158 | /// # trait SomeIndex {} |
159 | /// fn str_splice( |
160 | /// input: &'static str, |
161 | /// range: impl SomeIndex, |
162 | /// replace_with: &'static str, |
163 | /// ) -> const_format::SplicedStr |
164 | /// # {unimplemented!()} |
165 | /// ``` |
166 | /// and is evaluated at compile-time. |
167 | /// |
168 | /// ### `range` argument |
169 | /// |
170 | /// The `range` parameter determines what part of `input` is replaced, |
171 | /// and can be any of these types: |
172 | /// |
173 | /// - `usize`: the starting index of a char, only includes that char. |
174 | /// - `Range<usize>` |
175 | /// - `RangeTo<usize>` |
176 | /// - `RangeFrom<usize>` |
177 | /// - `RangeInclusive<usize>` |
178 | /// - `RangeToInclusive<usize>` |
179 | /// - `RangeFull` |
180 | /// |
181 | /// [`SplicedStr`] contains: |
182 | /// - `output`: a `&'static str` with the substring at `range` in `input` replaced with |
183 | /// `replace_with`. |
184 | /// - `removed`: the substring at `range` in `input`. |
185 | /// |
186 | /// # Example |
187 | /// |
188 | /// ```rust |
189 | /// use const_format::{str_splice, SplicedStr}; |
190 | /// |
191 | /// const OUT: SplicedStr = str_splice!("foo bar baz" , 4..=6, "is" ); |
192 | /// assert_eq!(OUT , SplicedStr{output: "foo is baz" , removed: "bar" }); |
193 | /// |
194 | /// // You can pass `const`ants to this macro, not just literals |
195 | /// { |
196 | /// const IN: &str = "this is bad" ; |
197 | /// const INDEX: std::ops::RangeFrom<usize> = 8..; |
198 | /// const REPLACE_WITH: &str = "... fine" ; |
199 | /// const OUT: SplicedStr = str_splice!(IN, INDEX, REPLACE_WITH); |
200 | /// assert_eq!(OUT , SplicedStr{output: "this is ... fine" , removed: "bad" }); |
201 | /// } |
202 | /// { |
203 | /// const OUT: SplicedStr = str_splice!("ABC豆-" , 3, "DEFGH" ); |
204 | /// assert_eq!(OUT , SplicedStr{output: "ABCDEFGH-" , removed: "豆" }); |
205 | /// } |
206 | /// ``` |
207 | /// |
208 | /// ### Invalid index |
209 | /// |
210 | /// Invalid indices cause compilation errors. |
211 | /// |
212 | /// ```compile_fail |
213 | /// const_format::str_splice!("foo" , 0..10, "" ); |
214 | /// ``` |
215 | #[cfg_attr ( |
216 | feature = "__test" , |
217 | doc = r#" |
218 | ```rust |
219 | const_format::str_splice!("foo", 0..3, ""); |
220 | ``` |
221 | |
222 | ```compile_fail |
223 | const_format::str_splice!("foo", 0..usize::MAX, ""); |
224 | ``` |
225 | |
226 | ```rust |
227 | assert_eq!( |
228 | const_format::str_splice!("効率的", 3..6, "A"), |
229 | const_format::SplicedStr{output: "効A的", removed: "率"} , |
230 | ); |
231 | ``` |
232 | |
233 | ```compile_fail |
234 | assert_eq!( |
235 | const_format::str_splice!("効率的", 1..6, "A"), |
236 | const_format::SplicedStr{output: "効A的", removed: "率"} , |
237 | ); |
238 | ``` |
239 | |
240 | ```compile_fail |
241 | assert_eq!( |
242 | const_format::str_splice!("効率的", 3..5, "A"), |
243 | const_format::SplicedStr{output: "効A的", removed: "率"} , |
244 | ); |
245 | ``` |
246 | |
247 | "# |
248 | )] |
249 | /// |
250 | /// |
251 | /// [`SplicedStr`]: ./struct.SplicedStr.html |
252 | #[macro_export ] |
253 | macro_rules! str_splice { |
254 | ($string:expr, $index:expr, $insert:expr $(,)*) => {{ |
255 | const P_OSRCTFL4A: $crate::__str_methods::StrSpliceArgs = |
256 | $crate::__str_methods::StrSplceArgsConv($string, $index, $insert).conv(); |
257 | { |
258 | use $crate::__hidden_utils::PtrToRef; |
259 | use $crate::__str_methods::{DecomposedString, SplicedStr, StrSpliceArgs}; |
260 | use $crate::pmr::{str, u8}; |
261 | |
262 | const P: &StrSpliceArgs = &P_OSRCTFL4A; |
263 | |
264 | type DecompIn = |
265 | DecomposedString<[u8; P.used_rstart], [u8; P.used_rlen], [u8; P.suffix_len]>; |
266 | |
267 | type DecompOut = |
268 | DecomposedString<[u8; P.used_rstart], [u8; P.insert_len], [u8; P.suffix_len]>; |
269 | |
270 | $crate::pmr::respan_to! { |
271 | ($string) |
272 | const _ASSERT_VALID_INDEX: () = P.index_validity.assert_valid(); |
273 | } |
274 | |
275 | const OUT_A: (&DecompOut, &str) = unsafe { |
276 | let input = PtrToRef { |
277 | ptr: P.str.as_ptr() as *const DecompIn, |
278 | } |
279 | .reff; |
280 | let insert = PtrToRef { |
281 | ptr: P.insert.as_ptr() as *const [u8; P.insert_len], |
282 | } |
283 | .reff; |
284 | |
285 | ( |
286 | &DecomposedString { |
287 | prefix: input.prefix, |
288 | middle: *insert, |
289 | suffix: input.suffix, |
290 | }, |
291 | $crate::__priv_transmute_bytes_to_str!(&input.middle), |
292 | ) |
293 | }; |
294 | |
295 | const OUT: SplicedStr = unsafe { |
296 | let output = OUT_A.0 as *const DecompOut as *const [u8; P.out_len]; |
297 | SplicedStr { |
298 | output: $crate::__priv_transmute_raw_bytes_to_str!(output), |
299 | removed: OUT_A.1, |
300 | } |
301 | }; |
302 | |
303 | OUT |
304 | } |
305 | }}; |
306 | } |
307 | |
308 | /// Indexes a `&'static str` constant. |
309 | /// |
310 | /// |
311 | /// # Signature |
312 | /// |
313 | /// This macro acts like a function of this signature: |
314 | /// ```rust |
315 | /// # trait SomeIndex {} |
316 | /// fn str_index(input: &'static str, range: impl SomeIndex) -> &'static str |
317 | /// # {unimplemented!()} |
318 | /// ``` |
319 | /// and is evaluated at compile-time. |
320 | /// |
321 | /// This accepts |
322 | /// [the same `range` arguments as `str_splice`](macro.str_splice.html#range-argument) |
323 | /// |
324 | /// # Example |
325 | /// |
326 | /// ``` |
327 | /// use const_format::str_index; |
328 | /// |
329 | /// use std::ops::RangeFrom; |
330 | /// |
331 | /// assert_eq!(str_index!("foo bar baz" , ..7), "foo bar" ); |
332 | /// assert_eq!(str_index!("foo bar baz" , 4..7), "bar" ); |
333 | /// assert_eq!(str_index!("foo bar baz" , 4..), "bar baz" ); |
334 | /// |
335 | /// { |
336 | /// const IN: &str = "hello world" ; |
337 | /// const INDEX: RangeFrom<usize> = 6..; |
338 | /// // You can pass `const`ants to this macro, not just literals |
339 | /// const OUT_0: &str = str_index!(IN, INDEX); |
340 | /// assert_eq!(OUT_0, "world" ); |
341 | /// } |
342 | /// { |
343 | /// const OUT: &str = str_index!("hello world" , 4); |
344 | /// assert_eq!(OUT, "o" ); |
345 | /// } |
346 | /// |
347 | /// ``` |
348 | /// |
349 | /// ### Invalid index |
350 | /// |
351 | /// Invalid indices cause compilation errors. |
352 | /// |
353 | /// ```compile_fail |
354 | /// const_format::str_index!("foo" , 0..10); |
355 | /// ``` |
356 | #[cfg_attr ( |
357 | feature = "__test" , |
358 | doc = r#" |
359 | ```rust |
360 | assert_eq!(const_format::str_index!("効率的", 3..6), "率"); |
361 | ``` |
362 | |
363 | ```compile_fail |
364 | assert_eq!(const_format::str_index!("効率的", 3..5), "率"); |
365 | ``` |
366 | ```compile_fail |
367 | assert_eq!(const_format::str_index!("効率的", 4..6), "率"); |
368 | ``` |
369 | "# |
370 | )] |
371 | /// |
372 | /// |
373 | #[macro_export ] |
374 | macro_rules! str_index { |
375 | ($string:expr, $index:expr $(,)*) => {{ |
376 | const P_OSRCTFL4A: $crate::__str_methods::StrIndexArgs = |
377 | $crate::__str_methods::StrIndexArgsConv($string, $index).conv(); |
378 | |
379 | { |
380 | $crate::pmr::respan_to! { |
381 | ($string) |
382 | const _ASSERT_VALID_INDEX: () = |
383 | P_OSRCTFL4A.index_validity.assert_valid(); |
384 | } |
385 | |
386 | use $crate::__hidden_utils::PtrToRef; |
387 | use $crate::__str_methods::DecomposedString; |
388 | type DecompIn = DecomposedString< |
389 | [u8; P_OSRCTFL4A.used_rstart], |
390 | [u8; P_OSRCTFL4A.used_rlen], |
391 | [u8; 0], |
392 | >; |
393 | |
394 | const OUT: &'static str = unsafe { |
395 | let input = PtrToRef { |
396 | ptr: P_OSRCTFL4A.str.as_ptr() as *const DecompIn, |
397 | } |
398 | .reff; |
399 | $crate::__priv_transmute_raw_bytes_to_str!(&input.middle) |
400 | }; |
401 | |
402 | OUT |
403 | } |
404 | }}; |
405 | } |
406 | |
407 | /// Indexes a `&'static str` constant, |
408 | /// returning `None` when the index is not on a character boundary. |
409 | /// |
410 | /// |
411 | /// # Signature |
412 | /// |
413 | /// This macro acts like a function of this signature: |
414 | /// ```rust |
415 | /// # trait SomeIndex {} |
416 | /// fn str_get(input: &'static str, range: impl SomeIndex) -> Option<&'static str> |
417 | /// # {unimplemented!()} |
418 | /// ``` |
419 | /// and is evaluated at compile-time. |
420 | /// |
421 | /// This accepts |
422 | /// [the same `range` arguments as `str_splice`](macro.str_splice.html#range-argument) |
423 | /// |
424 | /// # Example |
425 | /// |
426 | /// ``` |
427 | /// use const_format::str_get; |
428 | /// |
429 | /// use std::ops::RangeFrom; |
430 | /// |
431 | /// assert_eq!(str_get!("foo 鉄 baz" , ..7), Some("foo 鉄" )); |
432 | /// assert_eq!(str_get!("foo 鉄 baz" , 4..7), Some("鉄" )); |
433 | /// assert_eq!(str_get!("foo 鉄 baz" , 4..100), None); |
434 | /// |
435 | /// |
436 | /// { |
437 | /// const IN: &str = "hello 鉄" ; |
438 | /// const INDEX: RangeFrom<usize> = 6..; |
439 | /// // You can pass `const`ants to this macro, not just literals |
440 | /// const OUT: Option<&str> = str_get!(IN, INDEX); |
441 | /// assert_eq!(OUT, Some("鉄" )); |
442 | /// } |
443 | /// { |
444 | /// const OUT: Option<&str> = str_get!("hello 鉄" , 4); |
445 | /// assert_eq!(OUT, Some("o" )); |
446 | /// } |
447 | /// { |
448 | /// // End index not on a character boundary |
449 | /// const OUT: Option<&str> = str_get!("hello 鉄" , 0..7); |
450 | /// assert_eq!(OUT, None); |
451 | /// } |
452 | /// { |
453 | /// // Out of bounds indexing |
454 | /// const OUT: Option<&str> = str_get!("hello 鉄" , 0..1000 ); |
455 | /// assert_eq!(OUT, None); |
456 | /// } |
457 | /// |
458 | /// ``` |
459 | #[cfg_attr ( |
460 | feature = "__test" , |
461 | doc = r#" |
462 | ```rust |
463 | assert_eq!(const_format::str_get!("効率的", 3..6), Some("率")); |
464 | assert_eq!(const_format::str_get!("効率的", 3..5), None); |
465 | assert_eq!(const_format::str_get!("効率的", 4..6), None); |
466 | ``` |
467 | "# |
468 | )] |
469 | /// |
470 | #[macro_export ] |
471 | macro_rules! str_get { |
472 | ($string:expr, $index:expr $(,)*) => {{ |
473 | const P_OSRCTFL4A: $crate::__str_methods::StrIndexArgs = |
474 | $crate::__str_methods::StrIndexArgsConv($string, $index).conv(); |
475 | |
476 | { |
477 | use $crate::__hidden_utils::PtrToRef; |
478 | use $crate::__str_methods::DecomposedString; |
479 | type DecompIn = DecomposedString< |
480 | [u8; P_OSRCTFL4A.used_rstart], |
481 | [u8; P_OSRCTFL4A.used_rlen], |
482 | [u8; 0], |
483 | >; |
484 | |
485 | const OUT: $crate::pmr::Option<&'static $crate::pmr::str> = unsafe { |
486 | if P_OSRCTFL4A.index_validity.is_valid() { |
487 | let input = PtrToRef { |
488 | ptr: P_OSRCTFL4A.str.as_ptr() as *const DecompIn, |
489 | } |
490 | .reff; |
491 | |
492 | $crate::pmr::Some($crate::__priv_transmute_raw_bytes_to_str!(&input.middle)) |
493 | } else { |
494 | $crate::pmr::None |
495 | } |
496 | }; |
497 | |
498 | OUT |
499 | } |
500 | }}; |
501 | } |
502 | |
503 | /// Splits `$string` (a `&'static str` constant) with `$splitter`, |
504 | /// returning an array of `&'static str`s. |
505 | /// |
506 | /// # Signature |
507 | /// |
508 | /// This macro acts like a function of this signature: |
509 | /// ```rust |
510 | /// # const LEN: usize = 0; |
511 | /// # trait Splitter {} |
512 | /// fn str_split(string: &'static str, splitter: impl Splitter) -> [&'static str; LEN] |
513 | /// # { [] } |
514 | /// ``` |
515 | /// and is evaluated at compile-time. |
516 | /// |
517 | /// `impl Splitter` is any of these types: |
518 | /// |
519 | /// - `&'static str` |
520 | /// |
521 | /// - `char` |
522 | /// |
523 | /// - `u8`: only ascii values (0 up to 127 inclusive) are allowed |
524 | /// |
525 | /// The value of `LEN` depends on the `string` and `splitter` arguments. |
526 | /// |
527 | /// |
528 | /// # Example |
529 | /// |
530 | /// ```rust |
531 | /// use const_format::str_split; |
532 | /// |
533 | /// assert_eq!(str_split!("this is nice", ' '), ["this", "is", "nice"]); |
534 | /// |
535 | /// assert_eq!(str_split!("Hello, world!", ", "), ["Hello", "world!"]); |
536 | /// |
537 | /// // A `""` splitter outputs all chars individually (`str::split` does the same) |
538 | /// assert_eq!(str_split!("🧡BAR🧠", ""), ["", "🧡", "B", "A", "R", "🧠", ""]); |
539 | /// |
540 | /// // Splitting the string with an ascii byte |
541 | /// assert_eq!(str_split!("dash-separated-string", b'-'), ["dash", "separated", "string"]); |
542 | /// |
543 | /// { |
544 | /// const STR: &str = "foo bar baz"; |
545 | /// const SPLITTER: &str = " "; |
546 | /// |
547 | /// // both arguments to the `str_aplit` macro can be non-literal constants |
548 | /// const SPLIT: [&str; 3] = str_split!(STR, SPLITTER); |
549 | /// |
550 | /// assert_eq!(SPLIT, ["foo", "bar", "baz"]); |
551 | /// } |
552 | /// |
553 | /// ``` |
554 | #[macro_export ] |
555 | #[cfg (feature = "rust_1_64" )] |
556 | #[cfg_attr (feature = "__docsrs" , doc(cfg(feature = "rust_1_64" )))] |
557 | macro_rules! str_split { |
558 | ($string:expr, $splitter:expr $(,)?) => {{ |
559 | const ARGS_OSRCTFL4A: $crate::__str_methods::SplitInput = |
560 | $crate::__str_methods::SplitInputConv($string, $splitter).conv(); |
561 | |
562 | { |
563 | const OB: [&$crate::pmr::str; ARGS_OSRCTFL4A.length()] = ARGS_OSRCTFL4A.split_it(); |
564 | OB |
565 | } |
566 | }}; |
567 | } |
568 | |