const_format/macros/str_methods.rs
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]
67macro_rules! str_replace {
68 ($input:expr, $pattern:expr, $replace_with:expr $(,)*) => {
69 $crate::__str_const! {{
70 const ARGS_OSRCTFL4A: $crate::__str_methods::ReplaceInput =
71 $crate::__str_methods::ReplaceInputConv($input, $pattern, $replace_with).conv();
72
73 {
74 const OB: &[$crate::pmr::u8; ARGS_OSRCTFL4A.replace_length()] =
75 &ARGS_OSRCTFL4A.replace();
76
77 const OS: &$crate::pmr::str = unsafe { $crate::__priv_transmute_bytes_to_str!(OB) };
78
79 OS
80 }
81 }}
82 };
83}
84
85/// Creates a `&'static str` by repeating a `&'static str` constant `times` times
86///
87/// This is evaluated at compile-time.
88///
89/// # Example
90///
91/// ```rust
92/// use const_format::str_repeat;
93///
94/// {
95/// const OUT: &str = str_repeat!("hi ", 4);
96/// assert_eq!(OUT, "hi hi hi hi ")
97/// }
98/// {
99/// const IN: &str = "bye ";
100/// const REPEAT: usize = 5;
101/// const OUT: &str = str_repeat!(IN, REPEAT);
102/// assert_eq!(OUT, "bye bye bye bye bye ")
103/// }
104///
105/// ```
106///
107/// ### Failing
108///
109/// If this macro would produce too large a string,
110/// it causes a compile-time error.
111///
112/// ```compile_fail
113/// const_format::str_repeat!("hello", usize::MAX / 4);
114/// ```
115///
116#[cfg_attr(
117 feature = "__test",
118 doc = r##"
119```rust
120const_format::str_repeat!("hello", usize::MAX.wrapping_add(4));
121```
122"##
123)]
124#[macro_export]
125macro_rules! str_repeat {
126 ($string:expr, $times:expr $(,)*) => {
127 $crate::__str_const! {{
128 const P_OSRCTFL4A: &$crate::__str_methods::StrRepeatArgs =
129 &$crate::__str_methods::StrRepeatArgs($string, $times);
130
131 {
132 use $crate::__hidden_utils::PtrToRef;
133 use $crate::pmr::{str, transmute, u8};
134
135 const P: &$crate::__str_methods::StrRepeatArgs = P_OSRCTFL4A;
136
137 $crate::pmr::respan_to! {
138 ($string)
139 const _ASSERT_VALID_LEN: () = P.assert_valid();
140 }
141
142 const OUT_B: &[u8; P.out_len] = &unsafe {
143 let ptr = P.str.as_ptr() as *const [u8; P.str_len];
144 transmute::<[[u8; P.str_len]; P.repeat], [u8; P.out_len]>(
145 [*PtrToRef { ptr }.reff; P.repeat],
146 )
147 };
148 const OUT_S: &str = unsafe { $crate::__priv_transmute_bytes_to_str!(OUT_B) };
149 OUT_S
150 }
151 }}
152 };
153}
154
155/// Replaces a substring in a `&'static str` constant.
156/// Returns both the new resulting `&'static str`, and the replaced substring.
157///
158/// # Alternatives
159///
160/// For an alternative which only returns the string with the applied replacement,
161/// you can use [`str_splice_out`].
162///
163/// # Signature
164///
165/// This macro acts like a function of this signature:
166/// ```rust
167/// # trait SomeIndex {}
168/// fn str_splice(
169/// input: &'static str,
170/// range: impl SomeIndex,
171/// replace_with: &'static str,
172/// ) -> const_format::SplicedStr
173/// # {unimplemented!()}
174/// ```
175/// and is evaluated at compile-time.
176///
177/// ### `range` argument
178///
179/// The `range` parameter determines what part of `input` is replaced,
180/// and can be any of these types:
181///
182/// - `usize`: the starting index of a char, only includes that char.
183/// - `Range<usize>`
184/// - `RangeTo<usize>`
185/// - `RangeFrom<usize>`
186/// - `RangeInclusive<usize>`
187/// - `RangeToInclusive<usize>`
188/// - `RangeFull`
189///
190/// [`SplicedStr`] contains:
191/// - `output`: a `&'static str` with the substring at `range` in `input` replaced with
192/// `replace_with`.
193/// - `removed`: the substring at `range` in `input`.
194///
195/// # Example
196///
197/// ```rust
198/// use const_format::{str_splice, SplicedStr};
199///
200/// const OUT: SplicedStr = str_splice!("foo bar baz", 4..=6, "is");
201/// assert_eq!(OUT , SplicedStr{output: "foo is baz", removed: "bar"});
202///
203/// // You can pass `const`ants to this macro, not just literals
204/// {
205/// const IN: &str = "this is bad";
206/// const INDEX: std::ops::RangeFrom<usize> = 8..;
207/// const REPLACE_WITH: &str = "... fine";
208/// const OUT: SplicedStr = str_splice!(IN, INDEX, REPLACE_WITH);
209/// assert_eq!(OUT , SplicedStr{output: "this is ... fine", removed: "bad"});
210/// }
211/// {
212/// const OUT: SplicedStr = str_splice!("ABC豆-", 3, "DEFGH");
213/// assert_eq!(OUT , SplicedStr{output: "ABCDEFGH-", removed: "豆"});
214/// }
215/// ```
216///
217/// ### Invalid index
218///
219/// Invalid indices cause compilation errors.
220///
221/// ```compile_fail
222/// const_format::str_splice!("foo", 0..10, "");
223/// ```
224#[cfg_attr(
225 feature = "__test",
226 doc = r#"
227```rust
228const_format::str_splice!("foo", 0..3, "");
229```
230
231```compile_fail
232const_format::str_splice!("foo", 0..usize::MAX, "");
233```
234
235```rust
236assert_eq!(
237 const_format::str_splice!("効率的", 3..6, "A"),
238 const_format::SplicedStr{output: "効A的", removed: "率"} ,
239);
240```
241
242```compile_fail
243assert_eq!(
244 const_format::str_splice!("効率的", 1..6, "A"),
245 const_format::SplicedStr{output: "効A的", removed: "率"} ,
246);
247```
248
249```compile_fail
250assert_eq!(
251 const_format::str_splice!("効率的", 3..5, "A"),
252 const_format::SplicedStr{output: "効A的", removed: "率"} ,
253);
254```
255
256"#
257)]
258///
259///
260/// [`SplicedStr`]: ./struct.SplicedStr.html
261/// [`str_splice_out`]: ./macro.str_splice_out.html
262#[macro_export]
263macro_rules! str_splice {
264 ($string:expr, $index:expr, $insert:expr $(,)*) => {
265 $crate::__const! {
266 $crate::__str_methods::SplicedStr =>
267 $crate::__str_splice!($string, $index, $insert)
268 }
269 };
270}
271
272/// Alternative version of [`str_splice`] which only returns the string
273/// with the applied replacement.
274///
275/// # Example
276///
277/// ```rust
278/// use const_format::{str_splice_out, SplicedStr};
279///
280/// const OUT: &str = str_splice_out!("foo bar baz", 4..=6, "is");
281/// assert_eq!(OUT , "foo is baz");
282///
283/// // You can pass `const`ants to this macro, not just literals
284/// {
285/// const IN: &str = "this is bad";
286/// const INDEX: std::ops::RangeFrom<usize> = 8..;
287/// const REPLACE_WITH: &str = "... fine";
288/// const OUT: &str = str_splice_out!(IN, INDEX, REPLACE_WITH);
289/// assert_eq!(OUT , "this is ... fine");
290/// }
291/// {
292/// const OUT: &str = str_splice_out!("ABC豆-", 3, "DEFGH");
293/// assert_eq!(OUT , "ABCDEFGH-");
294/// }
295/// ```
296///
297/// [`str_splice`]: ./macro.str_splice.html
298#[macro_export]
299macro_rules! str_splice_out {
300 ($string:expr, $index:expr, $insert:expr $(,)*) => {
301 $crate::__str_const! {
302 $crate::__str_splice!($string, $index, $insert).output
303 }
304 };
305}
306
307#[doc(hidden)]
308#[macro_export]
309macro_rules! __str_splice {
310 ($string:expr, $index:expr, $insert:expr) => {{
311 const P_OSRCTFL4A: $crate::__str_methods::StrSpliceArgs =
312 $crate::__str_methods::StrSplceArgsConv($string, $index, $insert).conv();
313 {
314 use $crate::__hidden_utils::PtrToRef;
315 use $crate::__str_methods::{DecomposedString, SplicedStr, StrSpliceArgs};
316 use $crate::pmr::{str, u8};
317
318 const P: &StrSpliceArgs = &P_OSRCTFL4A;
319
320 type DecompIn =
321 DecomposedString<[u8; P.used_rstart], [u8; P.used_rlen], [u8; P.suffix_len]>;
322
323 type DecompOut =
324 DecomposedString<[u8; P.used_rstart], [u8; P.insert_len], [u8; P.suffix_len]>;
325
326 $crate::pmr::respan_to! {
327 ($string)
328 const _ASSERT_VALID_INDEX: () = P.index_validity.assert_valid();
329 }
330
331 const OUT_A: (&DecompOut, &str) = unsafe {
332 let input = PtrToRef {
333 ptr: P.str.as_ptr() as *const DecompIn,
334 }
335 .reff;
336 let insert = PtrToRef {
337 ptr: P.insert.as_ptr() as *const [u8; P.insert_len],
338 }
339 .reff;
340
341 (
342 &DecomposedString {
343 prefix: input.prefix,
344 middle: *insert,
345 suffix: input.suffix,
346 },
347 $crate::__priv_transmute_bytes_to_str!(&input.middle),
348 )
349 };
350
351 const OUT: SplicedStr = unsafe {
352 let output = OUT_A.0 as *const DecompOut as *const [u8; P.out_len];
353 SplicedStr {
354 output: $crate::__priv_transmute_raw_bytes_to_str!(output),
355 removed: OUT_A.1,
356 }
357 };
358
359 OUT
360 }
361 }};
362}
363
364/// Indexes a `&'static str` constant.
365///
366///
367/// # Signature
368///
369/// This macro acts like a function of this signature:
370/// ```rust
371/// # trait SomeIndex {}
372/// fn str_index(input: &'static str, range: impl SomeIndex) -> &'static str
373/// # {unimplemented!()}
374/// ```
375/// and is evaluated at compile-time.
376///
377/// This accepts
378/// [the same `range` arguments as `str_splice`](macro.str_splice.html#range-argument)
379///
380/// # Example
381///
382/// ```
383/// use const_format::str_index;
384///
385/// use std::ops::RangeFrom;
386///
387/// assert_eq!(str_index!("foo bar baz", ..7), "foo bar");
388/// assert_eq!(str_index!("foo bar baz", 4..7), "bar");
389/// assert_eq!(str_index!("foo bar baz", 4..), "bar baz");
390///
391/// {
392/// const IN: &str = "hello world";
393/// const INDEX: RangeFrom<usize> = 6..;
394/// // You can pass `const`ants to this macro, not just literals
395/// const OUT_0: &str = str_index!(IN, INDEX);
396/// assert_eq!(OUT_0, "world");
397/// }
398/// {
399/// const OUT: &str = str_index!("hello world", 4);
400/// assert_eq!(OUT, "o");
401/// }
402///
403/// ```
404///
405/// ### Invalid index
406///
407/// Invalid indices cause compilation errors.
408///
409/// ```compile_fail
410/// const_format::str_index!("foo", 0..10);
411/// ```
412#[cfg_attr(
413 feature = "__test",
414 doc = r#"
415```rust
416assert_eq!(const_format::str_index!("効率的", 3..6), "率");
417```
418
419```compile_fail
420assert_eq!(const_format::str_index!("効率的", 3..5), "率");
421```
422```compile_fail
423assert_eq!(const_format::str_index!("効率的", 4..6), "率");
424```
425"#
426)]
427///
428///
429#[macro_export]
430macro_rules! str_index {
431 ($string:expr, $index:expr $(,)*) => {
432 $crate::__str_const! {{
433 const P_OSRCTFL4A: $crate::__str_methods::StrIndexArgs =
434 $crate::__str_methods::StrIndexArgsConv($string, $index).conv();
435
436 {
437 $crate::pmr::respan_to! {
438 ($string)
439 const _ASSERT_VALID_INDEX: () =
440 P_OSRCTFL4A.index_validity.assert_valid();
441 }
442
443 use $crate::__hidden_utils::PtrToRef;
444 use $crate::__str_methods::DecomposedString;
445 type DecompIn = DecomposedString<
446 [u8; P_OSRCTFL4A.used_rstart],
447 [u8; P_OSRCTFL4A.used_rlen],
448 [u8; 0],
449 >;
450
451 const OUT: &'static str = unsafe {
452 let input = PtrToRef {
453 ptr: P_OSRCTFL4A.str.as_ptr() as *const DecompIn,
454 }
455 .reff;
456 $crate::__priv_transmute_raw_bytes_to_str!(&input.middle)
457 };
458
459 OUT
460 }
461 }}
462 };
463}
464
465/// Indexes a `&'static str` constant,
466/// returning `None` when the index is not on a character boundary.
467///
468///
469/// # Signature
470///
471/// This macro acts like a function of this signature:
472/// ```rust
473/// # trait SomeIndex {}
474/// fn str_get(input: &'static str, range: impl SomeIndex) -> Option<&'static str>
475/// # {unimplemented!()}
476/// ```
477/// and is evaluated at compile-time.
478///
479/// This accepts
480/// [the same `range` arguments as `str_splice`](macro.str_splice.html#range-argument)
481///
482/// # Example
483///
484/// ```
485/// use const_format::str_get;
486///
487/// use std::ops::RangeFrom;
488///
489/// assert_eq!(str_get!("foo 鉄 baz", ..7), Some("foo 鉄"));
490/// assert_eq!(str_get!("foo 鉄 baz", 4..7), Some("鉄"));
491/// assert_eq!(str_get!("foo 鉄 baz", 4..100), None);
492///
493///
494/// {
495/// const IN: &str = "hello 鉄";
496/// const INDEX: RangeFrom<usize> = 6..;
497/// // You can pass `const`ants to this macro, not just literals
498/// const OUT: Option<&str> = str_get!(IN, INDEX);
499/// assert_eq!(OUT, Some("鉄"));
500/// }
501/// {
502/// const OUT: Option<&str> = str_get!("hello 鉄", 4);
503/// assert_eq!(OUT, Some("o"));
504/// }
505/// {
506/// // End index not on a character boundary
507/// const OUT: Option<&str> = str_get!("hello 鉄", 0..7);
508/// assert_eq!(OUT, None);
509/// }
510/// {
511/// // Out of bounds indexing
512/// const OUT: Option<&str> = str_get!("hello 鉄", 0..1000 );
513/// assert_eq!(OUT, None);
514/// }
515///
516/// ```
517#[cfg_attr(
518 feature = "__test",
519 doc = r#"
520```rust
521assert_eq!(const_format::str_get!("効率的", 3..6), Some("率"));
522assert_eq!(const_format::str_get!("効率的", 3..5), None);
523assert_eq!(const_format::str_get!("効率的", 4..6), None);
524```
525"#
526)]
527///
528#[macro_export]
529macro_rules! str_get {
530 ($string:expr, $index:expr $(,)*) => {
531 $crate::__const! {
532 $crate::pmr::Option<&'static $crate::pmr::str> => {
533 const P_OSRCTFL4A: $crate::__str_methods::StrIndexArgs =
534 $crate::__str_methods::StrIndexArgsConv($string, $index).conv();
535
536 {
537 use $crate::__hidden_utils::PtrToRef;
538 use $crate::__str_methods::DecomposedString;
539 type DecompIn = DecomposedString<
540 [u8; P_OSRCTFL4A.used_rstart],
541 [u8; P_OSRCTFL4A.used_rlen],
542 [u8; 0],
543 >;
544
545 const OUT: $crate::pmr::Option<&'static $crate::pmr::str> = unsafe {
546 if P_OSRCTFL4A.index_validity.is_valid() {
547 let input = PtrToRef {
548 ptr: P_OSRCTFL4A.str.as_ptr() as *const DecompIn,
549 }
550 .reff;
551
552 $crate::pmr::Some($crate::__priv_transmute_raw_bytes_to_str!(&input.middle))
553 } else {
554 $crate::pmr::None
555 }
556 };
557
558 OUT
559 }
560 }}
561 };
562}
563
564/// Splits `$string` (a `&'static str` constant) with `$splitter`,
565/// returning an array of `&'static str`s.
566///
567/// # Alternatives
568///
569/// For an alternative macro which will be usable in slice patterns
570/// (once [`inline_const_pat`] is stabilized)
571/// you can use [`str_split_pat`].
572///
573/// # Signature
574///
575/// This macro acts like a function of this signature:
576/// ```rust
577/// # const LEN: usize = 0;
578/// # trait Splitter {}
579/// fn str_split(string: &'static str, splitter: impl Splitter) -> [&'static str; LEN]
580/// # { [] }
581/// ```
582/// and is evaluated at compile-time.
583///
584/// `impl Splitter` is any of these types:
585///
586/// - `&'static str`
587///
588/// - `char`
589///
590/// - `u8`: only ascii values (0 up to 127 inclusive) are allowed
591///
592/// The value of `LEN` depends on the `string` and `splitter` arguments.
593///
594///
595/// # Example
596///
597/// ```rust
598/// use const_format::str_split;
599///
600/// assert_eq!(str_split!("this is nice", ' '), ["this", "is", "nice"]);
601///
602/// assert_eq!(str_split!("Hello, world!", ", "), ["Hello", "world!"]);
603///
604/// // A `""` splitter outputs all chars individually (`str::split` does the same)
605/// assert_eq!(str_split!("🧡BAR🧠", ""), ["", "🧡", "B", "A", "R", "🧠", ""]);
606///
607/// // Splitting the string with an ascii byte
608/// assert_eq!(str_split!("dash-separated-string", b'-'), ["dash", "separated", "string"]);
609///
610/// {
611/// const STR: &str = "foo bar baz";
612/// const SPLITTER: &str = " ";
613///
614/// // both arguments to the `str_aplit` macro can be non-literal constants
615/// const SPLIT: [&str; 3] = str_split!(STR, SPLITTER);
616///
617/// assert_eq!(SPLIT, ["foo", "bar", "baz"]);
618/// }
619///
620/// ```
621///
622/// [`inline_const_pat`]: https://doc.rust-lang.org/1.83.0/unstable-book/language-features/inline-const-pat.html
623/// [`str_split_pat`]: crate::str_split_pat
624#[macro_export]
625#[cfg(feature = "rust_1_64")]
626#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "rust_1_64")))]
627macro_rules! str_split {
628 ($string:expr, $splitter:expr $(,)?) => {{
629 const ARGS_OSRCTFL4A: $crate::__str_methods::SplitInput =
630 $crate::__str_methods::SplitInputConv($string, $splitter).conv();
631
632 {
633 const OB: [&$crate::pmr::str; ARGS_OSRCTFL4A.length()] = ARGS_OSRCTFL4A.split_it();
634 OB
635 }
636 }};
637}
638
639/// Version of [`str_split`] which evaluates to a `&'static [&'static str]`.
640///
641/// # Example
642///
643/// Using the currently unstable [`inline_const_pat`] feature to use this macro in patterns:
644///
645/// ```rust
646/// # /*
647/// #![feature(inline_const_pat)]
648/// # */
649///
650/// use const_format::str_split_pat;
651///
652/// assert!(is_it(&["foo", "bar", "baz"]));
653/// assert!(!is_it(&["foo", "bar"]));
654///
655/// fn is_it(slice: &[&str]) -> bool {
656/// const SEP: char = ' ';
657/// # /*
658/// matches!(slice, str_split_pat!("foo bar baz", SEP))
659/// # */
660/// # slice == str_split_pat!("foo bar baz", SEP)
661/// }
662/// ```
663///
664/// [`inline_const_pat`]: https://doc.rust-lang.org/1.83.0/unstable-book/language-features/inline-const-pat.html
665#[macro_export]
666#[cfg(feature = "rust_1_64")]
667#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "rust_1_64")))]
668macro_rules! str_split_pat {
669 ($string:expr, $splitter:expr $(,)?) => {
670 $crate::__const! {&'static [&'static $crate::pmr::str] => {
671 const ARGS_OSRCTFL4A: $crate::__str_methods::SplitInput =
672 $crate::__str_methods::SplitInputConv($string, $splitter).conv();
673
674 {
675 const OB: [&$crate::pmr::str; ARGS_OSRCTFL4A.length()] = ARGS_OSRCTFL4A.split_it();
676 &OB
677 }
678 }}
679 };
680}