const_str/__ctfe/
concat.rs1#![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#[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#[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}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_concat() {
153 const PROMPT: &str = "The answer is";
154 const ANSWER: usize = 42;
155 const MESSAGE: &str = concat!(PROMPT, " ", ANSWER);
156 assert_eq!(MESSAGE, "The answer is 42");
157
158 const S1: &str = concat!("hello", " ", "world");
159 assert_eq!(S1, "hello world");
160
161 const S2: &str = concat!("a", "b", "c");
162 assert_eq!(S2, "abc");
163
164 const S3: &str = concat!("single");
165 assert_eq!(S3, "single");
166
167 const S4: &str = concat!("");
168 assert_eq!(S4, "");
169
170 const S5: &str = concat!(true, " ", false);
171 assert_eq!(S5, "true false");
172
173 const S6: &str = concat!('a', 'b', 'c');
174 assert_eq!(S6, "abc");
175 }
176
177 #[test]
178 fn test_concat_runtime() {
179 let strs = &["hello", "world"];
181 let concat = Concat(strs);
182 assert_eq!(concat.output_len(), 10);
183
184 let buf: StrBuf<10> = concat.const_eval();
185 assert_eq!(buf.as_str(), "helloworld");
186
187 let empty: &[&str] = &[];
188 let concat_empty = Concat(empty);
189 assert_eq!(concat_empty.output_len(), 0);
190
191 let single = &["test"];
192 let concat_single = Concat(single);
193 assert_eq!(concat_single.output_len(), 4);
194 }
195
196 #[test]
197 fn test_join() {
198 const WORDS: &[&str] = &["hello", "world"];
199 const MESSAGE1: &str = join!(WORDS, " ");
200 assert_eq!(MESSAGE1, "hello world");
201
202 const NUMS: &[&str] = &["1", "2", "3"];
203 const MESSAGE2: &str = join!(NUMS, ", ");
204 assert_eq!(MESSAGE2, "1, 2, 3");
205
206 const EMPTY: &[&str] = &[];
207 const MESSAGE3: &str = join!(EMPTY, ", ");
208 assert_eq!(MESSAGE3, "");
209
210 const SINGLE: &[&str] = &["alone"];
211 const MESSAGE4: &str = join!(SINGLE, ", ");
212 assert_eq!(MESSAGE4, "alone");
213
214 const MULTI: &[&str] = &["a", "b", "c", "d"];
215 const MESSAGE5: &str = join!(MULTI, "-");
216 assert_eq!(MESSAGE5, "a-b-c-d");
217 }
218
219 #[test]
220 fn test_join_runtime() {
221 let strs = &["hello", "world"];
223 let join = Join(strs, " ");
224 assert_eq!(join.output_len(), 11);
225
226 let buf: StrBuf<11> = join.const_eval();
227 assert_eq!(buf.as_str(), "hello world");
228
229 let empty: &[&str] = &[];
230 let join_empty = Join(empty, ", ");
231 assert_eq!(join_empty.output_len(), 0);
232
233 let single = &["test"];
234 let join_single = Join(single, ", ");
235 assert_eq!(join_single.output_len(), 4);
236
237 let multi = &["a", "b", "c"];
238 let join_multi = Join(multi, "-");
239 assert_eq!(join_multi.output_len(), 5); }
241}