1use std::{borrow::Cow, marker::PhantomData};
7
8use self::private::SealedMaybeConstStr;
9
10pub trait ConstStr {
12 const VAL: &'static str;
14}
15
16pub struct EmptyConstStr;
18
19impl ConstStr for EmptyConstStr {
20 const VAL: &'static str = "";
21}
22
23impl<T: ConstStr> MaybeConstStr for T {
24 const MAYBE_VAL: &'static str = Self::VAL;
25 const LEN: usize = const { Self::VAL.len() };
26 const HAVE_VAL: bool = true;
27 fn extend(into: &mut String) {
28 into.push_str(Self::VAL);
29 }
30}
31
32impl<T: ConstStr> SealedMaybeConstStr for T {}
33
34pub trait MaybeConstStr: SealedMaybeConstStr {
53 #[doc(hidden)]
54 const MAYBE_VAL: &'static str;
55 #[doc(hidden)]
56 const LEN: usize = 0;
57 #[doc(hidden)]
58 const HAVE_VAL: bool;
59 fn extend(into: &mut String);
61}
62
63mod private {
64 pub trait SealedMaybeConstStr {}
65}
66
67pub struct Concatenated<S, T>(S, T);
69
70const fn concatenate_strings<const N: usize>(x: &[u8], y: &[u8]) -> [u8; N] {
71 let mut buf = [0; N];
72 if N != x.len() + y.len() {
73 return buf;
74 }
75 let mut i = 0;
76 while i < x.len() {
77 buf[i] = x[i];
78 i += 1;
79 }
80 let mut i = 0;
81 while i < y.len() {
82 buf[x.len() + i] = y[i];
83 i += 1;
84 }
85 buf
86}
87
88struct ConcatenatedLen<X: MaybeConstStr, Y: MaybeConstStr, const N: usize>(
89 X,
90 Y,
91 PhantomData<[u8; N]>,
92);
93impl<S: MaybeConstStr, T: MaybeConstStr, const N: usize> MaybeConstStr
94 for ConcatenatedLen<S, T, N>
95{
96 const MAYBE_VAL: &str = const {
97 let buf =
98 const { &concatenate_strings::<N>(S::MAYBE_VAL.as_bytes(), T::MAYBE_VAL.as_bytes()) };
99 match std::str::from_utf8(buf) {
100 Ok(res) => res,
101 Err(_) => panic!(),
102 }
103 };
104 const HAVE_VAL: bool = S::HAVE_VAL && T::HAVE_VAL;
105 const LEN: usize = S::LEN + T::LEN;
106 fn extend(into: &mut String) {
107 S::extend(into);
108 T::extend(into);
109 }
110}
111impl<S: MaybeConstStr, T: MaybeConstStr, const N: usize> SealedMaybeConstStr
112 for ConcatenatedLen<S, T, N>
113{
114}
115
116impl<S: MaybeConstStr, T: MaybeConstStr> MaybeConstStr for Concatenated<S, T> {
117 const HAVE_VAL: bool = S::HAVE_VAL && T::HAVE_VAL && (S::LEN + T::LEN) <= 100;
121 const LEN: usize = S::LEN + T::LEN;
122 fn extend(into: &mut String) {
123 S::extend(into);
124 T::extend(into);
125 }
126 const MAYBE_VAL: &str = match S::MAYBE_VAL.len() + T::MAYBE_VAL.len() {
128 0 => ConcatenatedLen::<S, T, 0>::MAYBE_VAL,
129 1 => ConcatenatedLen::<S, T, 1>::MAYBE_VAL,
130 2 => ConcatenatedLen::<S, T, 2>::MAYBE_VAL,
131 3 => ConcatenatedLen::<S, T, 3>::MAYBE_VAL,
132 4 => ConcatenatedLen::<S, T, 4>::MAYBE_VAL,
133 5 => ConcatenatedLen::<S, T, 5>::MAYBE_VAL,
134 6 => ConcatenatedLen::<S, T, 6>::MAYBE_VAL,
135 7 => ConcatenatedLen::<S, T, 7>::MAYBE_VAL,
136 8 => ConcatenatedLen::<S, T, 8>::MAYBE_VAL,
137 9 => ConcatenatedLen::<S, T, 9>::MAYBE_VAL,
138 10 => ConcatenatedLen::<S, T, 10>::MAYBE_VAL,
139 11 => ConcatenatedLen::<S, T, 11>::MAYBE_VAL,
140 12 => ConcatenatedLen::<S, T, 12>::MAYBE_VAL,
141 13 => ConcatenatedLen::<S, T, 13>::MAYBE_VAL,
142 14 => ConcatenatedLen::<S, T, 14>::MAYBE_VAL,
143 15 => ConcatenatedLen::<S, T, 15>::MAYBE_VAL,
144 16 => ConcatenatedLen::<S, T, 16>::MAYBE_VAL,
145 17 => ConcatenatedLen::<S, T, 17>::MAYBE_VAL,
146 18 => ConcatenatedLen::<S, T, 18>::MAYBE_VAL,
147 19 => ConcatenatedLen::<S, T, 19>::MAYBE_VAL,
148 20 => ConcatenatedLen::<S, T, 20>::MAYBE_VAL,
149 21 => ConcatenatedLen::<S, T, 21>::MAYBE_VAL,
150 22 => ConcatenatedLen::<S, T, 22>::MAYBE_VAL,
151 23 => ConcatenatedLen::<S, T, 23>::MAYBE_VAL,
152 24 => ConcatenatedLen::<S, T, 24>::MAYBE_VAL,
153 25 => ConcatenatedLen::<S, T, 25>::MAYBE_VAL,
154 26 => ConcatenatedLen::<S, T, 26>::MAYBE_VAL,
155 27 => ConcatenatedLen::<S, T, 27>::MAYBE_VAL,
156 28 => ConcatenatedLen::<S, T, 28>::MAYBE_VAL,
157 29 => ConcatenatedLen::<S, T, 29>::MAYBE_VAL,
158 30 => ConcatenatedLen::<S, T, 30>::MAYBE_VAL,
159 31 => ConcatenatedLen::<S, T, 31>::MAYBE_VAL,
160 32 => ConcatenatedLen::<S, T, 32>::MAYBE_VAL,
161 33 => ConcatenatedLen::<S, T, 33>::MAYBE_VAL,
162 34 => ConcatenatedLen::<S, T, 34>::MAYBE_VAL,
163 35 => ConcatenatedLen::<S, T, 35>::MAYBE_VAL,
164 36 => ConcatenatedLen::<S, T, 36>::MAYBE_VAL,
165 37 => ConcatenatedLen::<S, T, 37>::MAYBE_VAL,
166 38 => ConcatenatedLen::<S, T, 38>::MAYBE_VAL,
167 39 => ConcatenatedLen::<S, T, 39>::MAYBE_VAL,
168 40 => ConcatenatedLen::<S, T, 40>::MAYBE_VAL,
169 41 => ConcatenatedLen::<S, T, 41>::MAYBE_VAL,
170 42 => ConcatenatedLen::<S, T, 42>::MAYBE_VAL,
171 43 => ConcatenatedLen::<S, T, 43>::MAYBE_VAL,
172 44 => ConcatenatedLen::<S, T, 44>::MAYBE_VAL,
173 45 => ConcatenatedLen::<S, T, 45>::MAYBE_VAL,
174 46 => ConcatenatedLen::<S, T, 46>::MAYBE_VAL,
175 47 => ConcatenatedLen::<S, T, 47>::MAYBE_VAL,
176 48 => ConcatenatedLen::<S, T, 48>::MAYBE_VAL,
177 49 => ConcatenatedLen::<S, T, 49>::MAYBE_VAL,
178 50 => ConcatenatedLen::<S, T, 50>::MAYBE_VAL,
179 51 => ConcatenatedLen::<S, T, 51>::MAYBE_VAL,
180 52 => ConcatenatedLen::<S, T, 52>::MAYBE_VAL,
181 53 => ConcatenatedLen::<S, T, 53>::MAYBE_VAL,
182 54 => ConcatenatedLen::<S, T, 54>::MAYBE_VAL,
183 55 => ConcatenatedLen::<S, T, 55>::MAYBE_VAL,
184 56 => ConcatenatedLen::<S, T, 56>::MAYBE_VAL,
185 57 => ConcatenatedLen::<S, T, 57>::MAYBE_VAL,
186 58 => ConcatenatedLen::<S, T, 58>::MAYBE_VAL,
187 59 => ConcatenatedLen::<S, T, 59>::MAYBE_VAL,
188 60 => ConcatenatedLen::<S, T, 60>::MAYBE_VAL,
189 61 => ConcatenatedLen::<S, T, 61>::MAYBE_VAL,
190 62 => ConcatenatedLen::<S, T, 62>::MAYBE_VAL,
191 63 => ConcatenatedLen::<S, T, 63>::MAYBE_VAL,
192 64 => ConcatenatedLen::<S, T, 64>::MAYBE_VAL,
193 65 => ConcatenatedLen::<S, T, 65>::MAYBE_VAL,
194 66 => ConcatenatedLen::<S, T, 66>::MAYBE_VAL,
195 67 => ConcatenatedLen::<S, T, 67>::MAYBE_VAL,
196 68 => ConcatenatedLen::<S, T, 68>::MAYBE_VAL,
197 69 => ConcatenatedLen::<S, T, 69>::MAYBE_VAL,
198 70 => ConcatenatedLen::<S, T, 70>::MAYBE_VAL,
199 71 => ConcatenatedLen::<S, T, 71>::MAYBE_VAL,
200 72 => ConcatenatedLen::<S, T, 72>::MAYBE_VAL,
201 73 => ConcatenatedLen::<S, T, 73>::MAYBE_VAL,
202 74 => ConcatenatedLen::<S, T, 74>::MAYBE_VAL,
203 75 => ConcatenatedLen::<S, T, 75>::MAYBE_VAL,
204 76 => ConcatenatedLen::<S, T, 76>::MAYBE_VAL,
205 77 => ConcatenatedLen::<S, T, 77>::MAYBE_VAL,
206 78 => ConcatenatedLen::<S, T, 78>::MAYBE_VAL,
207 79 => ConcatenatedLen::<S, T, 79>::MAYBE_VAL,
208 80 => ConcatenatedLen::<S, T, 80>::MAYBE_VAL,
209 81 => ConcatenatedLen::<S, T, 81>::MAYBE_VAL,
210 82 => ConcatenatedLen::<S, T, 82>::MAYBE_VAL,
211 83 => ConcatenatedLen::<S, T, 83>::MAYBE_VAL,
212 84 => ConcatenatedLen::<S, T, 84>::MAYBE_VAL,
213 85 => ConcatenatedLen::<S, T, 85>::MAYBE_VAL,
214 86 => ConcatenatedLen::<S, T, 86>::MAYBE_VAL,
215 87 => ConcatenatedLen::<S, T, 87>::MAYBE_VAL,
216 88 => ConcatenatedLen::<S, T, 88>::MAYBE_VAL,
217 89 => ConcatenatedLen::<S, T, 89>::MAYBE_VAL,
218 90 => ConcatenatedLen::<S, T, 90>::MAYBE_VAL,
219 91 => ConcatenatedLen::<S, T, 91>::MAYBE_VAL,
220 92 => ConcatenatedLen::<S, T, 92>::MAYBE_VAL,
221 93 => ConcatenatedLen::<S, T, 93>::MAYBE_VAL,
222 94 => ConcatenatedLen::<S, T, 94>::MAYBE_VAL,
223 95 => ConcatenatedLen::<S, T, 95>::MAYBE_VAL,
224 96 => ConcatenatedLen::<S, T, 96>::MAYBE_VAL,
225 97 => ConcatenatedLen::<S, T, 97>::MAYBE_VAL,
226 98 => ConcatenatedLen::<S, T, 98>::MAYBE_VAL,
227 99 => ConcatenatedLen::<S, T, 99>::MAYBE_VAL,
228 100 => ConcatenatedLen::<S, T, 100>::MAYBE_VAL,
229 _ => "",
230 };
231}
232impl<S: MaybeConstStr, T: MaybeConstStr> SealedMaybeConstStr for Concatenated<S, T> {}
233
234pub fn const_str_value<S: MaybeConstStr>() -> Cow<'static, str> {
238 if S::HAVE_VAL {
239 Cow::Borrowed(S::MAYBE_VAL)
240 } else {
241 let mut buf = String::with_capacity(S::LEN);
242 S::extend(&mut buf);
243 Cow::Owned(buf)
244 }
245}
246
247#[cfg(test)]
248mod test {
249 use std::borrow::Cow;
250
251 use crate::concat::{Concatenated, ConstStr, const_str_value};
252
253 struct ConstFoo;
254 impl ConstStr for ConstFoo {
255 const VAL: &str = "Foo_";
256 }
257
258 struct ConstBar;
259 impl ConstStr for ConstBar {
260 const VAL: &str = "Bar";
261 }
262
263 struct ConstMinus;
264 impl ConstStr for ConstMinus {
265 const VAL: &str = "-";
266 }
267
268 type MinusConcated<U, V> = Concatenated<U, Concatenated<ConstMinus, V>>;
269 type VL1 = MinusConcated<ConstFoo, ConstBar>;
270 type VL2 = MinusConcated<VL1, VL1>;
271 type VL3 = MinusConcated<VL2, VL2>;
272 type VL4 = MinusConcated<VL3, VL3>;
273 type VL5 = MinusConcated<VL4, VL4>;
274 type VL6 = MinusConcated<VL5, VL5>;
275
276 #[test]
277 fn main() {
278 assert_eq!(
279 const_str_value::<Concatenated<ConstFoo, ConstBar>>(),
280 "Foo_Bar"
281 );
282 let vl6 = const_str_value::<VL6>();
283 match vl6 {
284 Cow::Owned(a) => assert_eq!(
285 a,
286 "Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar"
287 ),
288 _ => panic!(),
289 };
290 let vl5 = const_str_value::<VL5>();
291 match vl5 {
292 Cow::Owned(a) => assert_eq!(
293 a,
294 "Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar"
295 ),
296 _ => panic!(),
297 };
298 let vl4 = const_str_value::<VL4>();
299 match vl4 {
300 Cow::Borrowed(a) => assert_eq!(
301 a,
302 "Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar-Foo_-Bar"
303 ),
304 _ => panic!(),
305 };
306 }
307}