const_str/__ctfe/
concat.rs

1#![allow(unsafe_code)]
2
3use super::StrBuf;
4
5pub struct Concat<'a>(pub &'a [&'a str]);
6
7impl Concat<'_> {
8    pub const fn output_len(&self) -> usize {
9        let mut ans = 0;
10        let mut iter = self.0;
11        while let [x, xs @ ..] = iter {
12            ans += x.len();
13            iter = xs;
14        }
15        ans
16    }
17
18    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
19        let mut buf = [0; N];
20        let mut pos = 0;
21
22        let mut iter = self.0;
23        while let [x, xs @ ..] = iter {
24            let x = x.as_bytes();
25            let mut i = 0;
26            while i < x.len() {
27                buf[pos] = x[i];
28                pos += 1;
29                i += 1;
30            }
31            iter = xs;
32        }
33        assert!(pos == N);
34
35        unsafe { StrBuf::new_unchecked(buf) }
36    }
37}
38
39/// Concatenates values into a string slice.
40///
41/// The input type must be one of
42///
43/// + [`&str`]
44/// + [`char`]
45/// + [`bool`]
46/// + [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], [`usize`]
47/// + [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`]
48///
49/// This macro is [const-context only](./index.html#const-context-only).
50///
51/// # Examples
52///
53/// ```
54/// const PROMPT: &str = "The answer is";
55/// const ANSWER: usize = 42;
56/// const MESSAGE: &str = const_str::concat!(PROMPT, " ", ANSWER);
57///
58/// assert_eq!(MESSAGE, "The answer is 42");
59/// ```
60///
61#[macro_export]
62macro_rules! concat {
63    ($($x: expr),+ $(,)?) => {{
64        const STRS: &[&str] = &[$( $crate::to_str!($x) ),+];
65        const OUTPUT_LEN: usize = $crate::__ctfe::Concat(STRS).output_len();
66        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> = $crate::__ctfe::Concat(STRS).const_eval();
67        OUTPUT_BUF.as_str()
68    }}
69}
70
71pub struct Join<'a>(pub &'a [&'a str], pub &'a str);
72
73impl Join<'_> {
74    pub const fn output_len(&self) -> usize {
75        let mut ans = 0;
76        let mut i = 0;
77        while i < self.0.len() {
78            ans += self.0[i].len();
79            if i < self.0.len() - 1 {
80                ans += self.1.len();
81            }
82            i += 1;
83        }
84        ans
85    }
86
87    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
88        let mut buf = [0; N];
89        let mut pos = 0;
90
91        let mut i = 0;
92        while i < self.0.len() {
93            let x = self.0[i].as_bytes();
94            let mut j = 0;
95            while j < x.len() {
96                buf[pos] = x[j];
97                pos += 1;
98                j += 1;
99            }
100            if i < self.0.len() - 1 {
101                let sep = self.1.as_bytes();
102                let mut j = 0;
103                while j < sep.len() {
104                    buf[pos] = sep[j];
105                    pos += 1;
106                    j += 1;
107                }
108            }
109            i += 1;
110        }
111
112        unsafe { StrBuf::new_unchecked(buf) }
113    }
114}
115
116/// Concatenates string slices into a string slice, separated by a given separator.
117///
118/// This macro is [const-context only](./index.html#const-context-only).
119///
120/// # Examples
121///
122/// ```
123/// const WORDS: &[&str] = &["hello", "world"];
124/// const MESSAGE1: &str = const_str::join!(WORDS, " ");
125/// assert_eq!(MESSAGE1, "hello world");
126///
127/// const NUMS: &[&str] = &["1", "2", "3"];
128/// const MESSAGE2: &str = const_str::join!(NUMS, ", ");
129/// assert_eq!(MESSAGE2, "1, 2, 3");
130///
131/// const EMPTY: &[&str] = &[];
132/// const MESSAGE3: &str = const_str::join!(EMPTY, ", ");
133/// assert_eq!(MESSAGE3, "");
134/// ```
135#[macro_export]
136macro_rules! join {
137    ($strs: expr, $sep: expr) => {{
138        const STRS: &[&str] = $strs;
139        const SEP: &str = $sep;
140        const OUTPUT_LEN: usize = $crate::__ctfe::Join(STRS, SEP).output_len();
141        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
142            $crate::__ctfe::Join(STRS, SEP).const_eval();
143        OUTPUT_BUF.as_str()
144    }};
145}