konst_kernel/string/
string_for_konst.rs

1#![allow(clippy::len_without_is_empty)]
2
3use crate::chr::{encode_utf8, Utf8Encoded};
4
5#[doc(hidden)]
6pub struct __NormalizeConcatArg<T: 'static>(pub &'static [T]);
7
8impl __NormalizeConcatArg<char> {
9    pub const fn conv(self) -> __StrConcatArg {
10        __StrConcatArg::Char(self.0)
11    }
12}
13
14impl __NormalizeConcatArg<&'static str> {
15    pub const fn conv(self) -> __StrConcatArg {
16        __StrConcatArg::Str(self.0)
17    }
18}
19
20#[doc(hidden)]
21#[derive(Copy, Clone)]
22pub enum __StrConcatArg {
23    Char(&'static [char]),
24    Str(&'static [&'static str]),
25}
26
27#[doc(hidden)]
28pub struct __MakeSepArg<T>(pub T);
29
30#[doc(hidden)]
31#[derive(Copy, Clone)]
32pub enum __SepArg {
33    Char(char),
34    Str(&'static str),
35}
36
37impl __SepArg {
38    const fn len(self) -> usize {
39        match self {
40            Self::Char(x) => x.len_utf8(),
41            Self::Str(x) => x.len(),
42        }
43    }
44}
45
46#[doc(hidden)]
47pub struct __ElemDispatch<T>(pub T);
48
49macro_rules! __ref_unref_impls {
50    ($($ref_token:tt $deref_token:tt)?) => {
51        impl __MakeSepArg<$($ref_token)? char> {
52            pub const fn conv(self) -> __SepArg {
53                __SepArg::Char($($deref_token)? self.0)
54            }
55        }
56
57        impl __MakeSepArg<$($ref_token)? &'static str> {
58            pub const fn conv(self) -> __SepArg {
59                __SepArg::Str(self.0)
60            }
61        }
62
63        impl __ElemDispatch<$($ref_token)? char> {
64            pub const fn as_bytesable(self) -> Utf8Encoded {
65                encode_utf8($($deref_token)? self.0)
66            }
67            pub const fn len(self) -> usize {
68                self.0.len_utf8()
69            }
70        }
71
72        impl __ElemDispatch<$($ref_token)? &'static str> {
73            pub const fn as_bytesable(self) -> &'static str {
74                self.0
75            }
76            pub const fn len(self) -> usize {
77                self.0.len()
78            }
79        }
80    };
81}
82
83__ref_unref_impls! {}
84__ref_unref_impls! {& *}
85
86#[doc(hidden)]
87#[macro_export]
88macro_rules! __with_str_concat_slices {
89    ($arg:expr, |$slices:ident| $with_slices:expr) => {
90        match $arg {
91            $crate::string::__StrConcatArg::Char($slices) => $with_slices,
92            $crate::string::__StrConcatArg::Str($slices) => $with_slices,
93        }
94    };
95}
96
97////////////////////////////////////////////////////////////////////////////////
98
99#[macro_export]
100macro_rules! string_concat {
101    ($(&)? []) => {
102        ""
103    };
104    ($slice:expr $(,)*) => {{
105        const __ARGS_81608BFNA5: $crate::string::__StrConcatArg =
106            $crate::string::__NormalizeConcatArg($slice).conv();
107        {
108            const LEN: $crate::__::usize = $crate::string::concat_sum_lengths(__ARGS_81608BFNA5);
109
110            const CONC: &$crate::string::ArrayStr<LEN> =
111                &$crate::string::concat_strs(__ARGS_81608BFNA5);
112
113            const STR: &$crate::__::str = CONC.as_str();
114
115            STR
116        }
117    }};
118}
119
120pub const fn concat_sum_lengths(arg: __StrConcatArg) -> usize {
121    let mut sum = 0usize;
122
123    __with_str_concat_slices! {arg, |slices| {
124        crate::for_range! {i in 0..slices.len() =>
125            sum += __ElemDispatch(slices[i]).len();
126        }
127    }}
128
129    sum
130}
131
132pub const fn concat_strs<const N: usize>(arg: __StrConcatArg) -> ArrayStr<N> {
133    let mut out = [0u8; N];
134    let mut out_i = 0usize;
135
136    __with_str_concat_slices! {arg, |slices| {
137        crate::for_range! {si in 0..slices.len() =>
138            let byteser = __ElemDispatch(slices[si]).as_bytesable();
139            let slice = byteser.as_bytes();
140            crate::for_range! {i in 0..slice.len() =>
141                out[out_i] = slice[i];
142                out_i += 1;
143            }
144        }
145    }}
146
147    ArrayStr(out)
148}
149
150////////////////////////////////////////////////////////////////////////////////
151
152#[macro_export]
153macro_rules! string_join {
154    ($sep:expr, $(&)? []) => {
155        ""
156    };
157    ($sep:expr, $slice:expr $(,)*) => {{
158        const __ARGS_81608BFNA5: $crate::string::StrJoinArgs = $crate::string::StrJoinArgs {
159            sep: $crate::string::__MakeSepArg($sep).conv(),
160            slice: $slice,
161        };
162
163        {
164            const LEN: $crate::__::usize = $crate::string::join_sum_lengths(__ARGS_81608BFNA5);
165
166            const CONC: &$crate::string::ArrayStr<LEN> =
167                &$crate::string::join_strs(__ARGS_81608BFNA5);
168
169            const STR: &$crate::__::str = CONC.as_str();
170
171            STR
172        }
173    }};
174}
175
176#[derive(Copy, Clone)]
177pub struct StrJoinArgs {
178    pub sep: __SepArg,
179    pub slice: &'static [&'static str],
180}
181
182pub const fn join_sum_lengths(StrJoinArgs { sep, slice }: StrJoinArgs) -> usize {
183    if slice.is_empty() {
184        0
185    } else {
186        concat_sum_lengths(__StrConcatArg::Str(slice)) + sep.len() * (slice.len() - 1)
187    }
188}
189
190pub const fn join_strs<const N: usize>(
191    StrJoinArgs { sep, slice: slices }: StrJoinArgs,
192) -> ArrayStr<N> {
193    let mut out = [0u8; N];
194    let mut out_i = 0usize;
195
196    let utf8e: Utf8Encoded;
197    let sep = match sep {
198        __SepArg::Char(c) => {
199            utf8e = encode_utf8(c);
200            utf8e.as_str()
201        }
202        __SepArg::Str(s) => s,
203    };
204
205    macro_rules! write_str {
206        ($str:expr) => {{
207            let slice = $str.as_bytes();
208            crate::for_range! {i in 0..slice.len() =>
209                out[out_i] = slice[i];
210                out_i += 1;
211            }
212        }};
213    }
214
215    if let [first, rem_slices @ ..] = slices {
216        write_str! {first}
217
218        crate::for_range! {si in 0..rem_slices.len() =>
219            write_str!{sep}
220            write_str!{rem_slices[si]}
221        }
222    }
223
224    ArrayStr(out)
225}
226
227////////////////////////////////////////////////////////////////////////////////
228
229#[macro_export]
230macro_rules! str_from_iter {
231    ($($rem:tt)*) => {{
232        $crate::__collect_const_iter_with!{
233            $crate::__::u8,
234            {},
235            |array, written_length, item| {
236                let byteser = $crate::string::__ElemDispatch(item).as_bytesable();
237                let bytes = byteser.as_bytes();
238                let item_len = bytes.len();
239                let mut i = written_length;
240                let mut j = 0;
241                while j < item_len {
242                    array[i] = $crate::__::MaybeUninit::new(bytes[j]);
243                    i += 1;
244                    j += 1;
245                }
246            },
247            elem_length = {
248                $crate::string::__ElemDispatch(item).len()
249            },
250            =>
251            $($rem)*
252        }
253
254        const __STR81608BFNA5: &$crate::__::str =
255            match core::str::from_utf8(&__ARR81608BFNA5) {
256                $crate::__::Ok(x) => x,
257                $crate::__::Err(_) => $crate::__::panic!("created string isn't UTF8"),
258            };
259
260        __STR81608BFNA5
261    }}
262}
263
264////////////////////////////////////////////////////////////////////////////////
265
266pub struct ArrayStr<const N: usize>([u8; N]);
267
268impl<const N: usize> ArrayStr<N> {
269    pub const fn as_str(&self) -> &str {
270        match core::str::from_utf8(&self.0) {
271            Ok(s) => s,
272            Err(_) => panic!("bug: konst made an invalid string"),
273        }
274    }
275}