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}