konst_kernel 0.3.4

Foundational const functionality shared between konst and const_panic
Documentation
#![allow(clippy::len_without_is_empty)]

use crate::chr::{encode_utf8, Utf8Encoded};

#[doc(hidden)]
pub struct __NormalizeConcatArg<T: 'static>(pub &'static [T]);

impl __NormalizeConcatArg<char> {
    pub const fn conv(self) -> __StrConcatArg {
        __StrConcatArg::Char(self.0)
    }
}

impl __NormalizeConcatArg<&'static str> {
    pub const fn conv(self) -> __StrConcatArg {
        __StrConcatArg::Str(self.0)
    }
}

#[doc(hidden)]
#[derive(Copy, Clone)]
pub enum __StrConcatArg {
    Char(&'static [char]),
    Str(&'static [&'static str]),
}

#[doc(hidden)]
pub struct __MakeSepArg<T>(pub T);

#[doc(hidden)]
#[derive(Copy, Clone)]
pub enum __SepArg {
    Char(char),
    Str(&'static str),
}

impl __SepArg {
    const fn len(self) -> usize {
        match self {
            Self::Char(x) => x.len_utf8(),
            Self::Str(x) => x.len(),
        }
    }
}

#[doc(hidden)]
pub struct __ElemDispatch<T>(pub T);

macro_rules! __ref_unref_impls {
    ($($ref_token:tt $deref_token:tt)?) => {
        impl __MakeSepArg<$($ref_token)? char> {
            pub const fn conv(self) -> __SepArg {
                __SepArg::Char($($deref_token)? self.0)
            }
        }

        impl __MakeSepArg<$($ref_token)? &'static str> {
            pub const fn conv(self) -> __SepArg {
                __SepArg::Str(self.0)
            }
        }

        impl __ElemDispatch<$($ref_token)? char> {
            pub const fn as_bytesable(self) -> Utf8Encoded {
                encode_utf8($($deref_token)? self.0)
            }
            pub const fn len(self) -> usize {
                self.0.len_utf8()
            }
        }

        impl __ElemDispatch<$($ref_token)? &'static str> {
            pub const fn as_bytesable(self) -> &'static str {
                self.0
            }
            pub const fn len(self) -> usize {
                self.0.len()
            }
        }
    };
}

__ref_unref_impls! {}
__ref_unref_impls! {& *}

#[doc(hidden)]
#[macro_export]
macro_rules! __with_str_concat_slices {
    ($arg:expr, |$slices:ident| $with_slices:expr) => {
        match $arg {
            $crate::string::__StrConcatArg::Char($slices) => $with_slices,
            $crate::string::__StrConcatArg::Str($slices) => $with_slices,
        }
    };
}

////////////////////////////////////////////////////////////////////////////////

#[macro_export]
macro_rules! string_concat {
    ($(&)? []) => {
        ""
    };
    ($slice:expr $(,)*) => {{
        const __ARGS_81608BFNA5: $crate::string::__StrConcatArg =
            $crate::string::__NormalizeConcatArg($slice).conv();
        {
            const LEN: $crate::__::usize = $crate::string::concat_sum_lengths(__ARGS_81608BFNA5);

            const CONC: &$crate::string::ArrayStr<LEN> =
                &$crate::string::concat_strs(__ARGS_81608BFNA5);

            const STR: &$crate::__::str = CONC.as_str();

            STR
        }
    }};
}

pub const fn concat_sum_lengths(arg: __StrConcatArg) -> usize {
    let mut sum = 0usize;

    __with_str_concat_slices! {arg, |slices| {
        crate::for_range! {i in 0..slices.len() =>
            sum += __ElemDispatch(slices[i]).len();
        }
    }}

    sum
}

pub const fn concat_strs<const N: usize>(arg: __StrConcatArg) -> ArrayStr<N> {
    let mut out = [0u8; N];
    let mut out_i = 0usize;

    __with_str_concat_slices! {arg, |slices| {
        crate::for_range! {si in 0..slices.len() =>
            let byteser = __ElemDispatch(slices[si]).as_bytesable();
            let slice = byteser.as_bytes();
            crate::for_range! {i in 0..slice.len() =>
                out[out_i] = slice[i];
                out_i += 1;
            }
        }
    }}

    ArrayStr(out)
}

////////////////////////////////////////////////////////////////////////////////

#[macro_export]
macro_rules! string_join {
    ($sep:expr, $(&)? []) => {
        ""
    };
    ($sep:expr, $slice:expr $(,)*) => {{
        const __ARGS_81608BFNA5: $crate::string::StrJoinArgs = $crate::string::StrJoinArgs {
            sep: $crate::string::__MakeSepArg($sep).conv(),
            slice: $slice,
        };

        {
            const LEN: $crate::__::usize = $crate::string::join_sum_lengths(__ARGS_81608BFNA5);

            const CONC: &$crate::string::ArrayStr<LEN> =
                &$crate::string::join_strs(__ARGS_81608BFNA5);

            const STR: &$crate::__::str = CONC.as_str();

            STR
        }
    }};
}

#[derive(Copy, Clone)]
pub struct StrJoinArgs {
    pub sep: __SepArg,
    pub slice: &'static [&'static str],
}

pub const fn join_sum_lengths(StrJoinArgs { sep, slice }: StrJoinArgs) -> usize {
    if slice.is_empty() {
        0
    } else {
        concat_sum_lengths(__StrConcatArg::Str(slice)) + sep.len() * (slice.len() - 1)
    }
}

pub const fn join_strs<const N: usize>(
    StrJoinArgs { sep, slice: slices }: StrJoinArgs,
) -> ArrayStr<N> {
    let mut out = [0u8; N];
    let mut out_i = 0usize;

    let utf8e: Utf8Encoded;
    let sep = match sep {
        __SepArg::Char(c) => {
            utf8e = encode_utf8(c);
            utf8e.as_str()
        }
        __SepArg::Str(s) => s,
    };

    macro_rules! write_str {
        ($str:expr) => {{
            let slice = $str.as_bytes();
            crate::for_range! {i in 0..slice.len() =>
                out[out_i] = slice[i];
                out_i += 1;
            }
        }};
    }

    if let [first, rem_slices @ ..] = slices {
        write_str! {first}

        crate::for_range! {si in 0..rem_slices.len() =>
            write_str!{sep}
            write_str!{rem_slices[si]}
        }
    }

    ArrayStr(out)
}

////////////////////////////////////////////////////////////////////////////////

#[macro_export]
macro_rules! str_from_iter {
    ($($rem:tt)*) => {{
        $crate::__collect_const_iter_with!{
            $crate::__::u8,
            |array, written_length, item| {
                let byteser = $crate::string::__ElemDispatch(item).as_bytesable();
                let bytes = byteser.as_bytes();
                let item_len = bytes.len();
                let mut i = written_length;
                let mut j = 0;
                while j < item_len {
                    array[i] = $crate::__::MaybeUninit::new(bytes[j]);
                    i += 1;
                    j += 1;
                }
            },
            elem_length = {
                $crate::string::__ElemDispatch(item).len()
            },
            =>
            $($rem)*
        }

        const __STR81608BFNA5: &$crate::__::str =
            match core::str::from_utf8(&__ARR81608BFNA5) {
                $crate::__::Ok(x) => x,
                $crate::__::Err(_) => $crate::__::panic!("created string isn't UTF8"),
            };

        __STR81608BFNA5
    }}
}

////////////////////////////////////////////////////////////////////////////////

pub struct ArrayStr<const N: usize>([u8; N]);

impl<const N: usize> ArrayStr<N> {
    pub const fn as_str(&self) -> &str {
        match core::str::from_utf8(&self.0) {
            Ok(s) => s,
            Err(_) => panic!("bug: konst made an invalid string"),
        }
    }
}