const-str 0.3.2

compile-time string operations
Documentation
#![allow(unsafe_code)]

use super::StrBuf;
use super::ToStr;

use crate::utf8::CharEscapeDebug;

#[derive(Clone, Copy)]
pub struct FmtSpec {
    pub alternate: bool,
}

pub struct Display<T>(pub T, pub FmtSpec);

macro_rules! delegate_display {
    ($($ty: ty,)+) => {
        $(
            impl Display<$ty> {
                pub const fn output_len(&self) -> usize {
                    ToStr(self.0).output_len()
                }

                pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                    ToStr(self.0).const_eval()
                }
            }
        )+
    };
}

delegate_display!(&str, char, bool, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize,);

#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_display {
    ($x: expr, $spec: expr) => {{
        const OUTPUT_LEN: usize = $crate::__ctfe::Display($x, $spec).output_len();
        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
            $crate::__ctfe::Display($x, $spec).const_eval();
        $crate::__strbuf_as_str!(&OUTPUT_BUF)
    }};
}

pub struct Debug<T>(pub T, pub FmtSpec);

macro_rules! delegate_debug {
    ($($ty: ty,)+) => {
        $(
            impl Debug<$ty> {
                pub const fn output_len(&self) -> usize {
                    ToStr(self.0).output_len()
                }

                pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                    ToStr(self.0).const_eval()
                }
            }
        )+
    };
}

delegate_debug!(bool, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize,);

impl Debug<char> {
    pub const fn output_len(&self) -> usize {
        CharEscapeDebug::new(self.0).as_bytes().len()
    }

    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
        CharEscapeDebug::new(self.0).to_str_buf()
    }
}

impl Debug<&str> {
    pub const fn output_len(&self) -> usize {
        let mut s = self.0.as_bytes();
        let mut ans = 0;
        while let Some((ch, count)) = crate::utf8::next_code_point(s) {
            s = crate::bytes::advance(s, count);
            let e = unsafe { CharEscapeDebug::from_code_point(ch) };
            ans += e.as_bytes().len()
        }
        ans
    }

    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
        let mut buf = [0; N];
        let mut pos = 0;
        let mut s = self.0.as_bytes();
        while let Some((ch, count)) = crate::utf8::next_code_point(s) {
            s = crate::bytes::advance(s, count);
            let e = unsafe { CharEscapeDebug::from_code_point(ch) };
            let bytes = e.as_bytes();
            let mut i = 0;
            while i < bytes.len() {
                buf[pos] = bytes[i];
                i += 1;
                pos += 1;
            }
        }
        unsafe { StrBuf::new_unchecked(buf) }
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_debug {
    ($x: expr, $spec: expr) => {{
        const OUTPUT_LEN: usize = $crate::__ctfe::Debug($x, $spec).output_len();
        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
            $crate::__ctfe::Debug($x, $spec).const_eval();
        $crate::__strbuf_as_str!(&OUTPUT_BUF)
    }};
}

struct Hex<T>(T, FmtSpec, bool);

pub struct LowerHex<T>(pub T, pub FmtSpec);
pub struct UpperHex<T>(pub T, pub FmtSpec);

macro_rules! impl_integer_hex {
    ($unsigned: ty, $signed: ty) => {
        impl Hex<$unsigned> {
            const fn output_len(&self) -> usize {
                let mut x = self.0;
                let mut ans = 0;
                loop {
                    ans += 1;
                    x /= 16;
                    if x == 0 {
                        break;
                    }
                }
                if self.1.alternate {
                    ans += 2;
                }
                ans
            }

            const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                let mut buf = [0; N];
                let mut pos = 0;
                let mut x = self.0;
                loop {
                    let d = crate::ascii::num_to_hex_digit((x % 16) as u8);
                    buf[pos] = if self.2 {
                        crate::ascii::to_uppercase(d)
                    } else {
                        d
                    };
                    pos += 1;
                    x /= 16;
                    if x == 0 {
                        break;
                    }
                }
                if self.1.alternate {
                    buf[pos] = b'x';
                    pos += 1;
                    buf[pos] = b'0';
                    pos += 1;
                }
                constfn_assert!(pos == N);
                let buf = crate::bytes::reversed(buf);
                unsafe { StrBuf::new_unchecked(buf) }
            }
        }

        impl LowerHex<$unsigned> {
            pub const fn output_len(&self) -> usize {
                let h = Hex(self.0, self.1, false);
                h.output_len()
            }

            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                let h = Hex(self.0, self.1, false);
                h.const_eval()
            }
        }

        impl UpperHex<$unsigned> {
            pub const fn output_len(&self) -> usize {
                let h = Hex(self.0, self.1, true);
                h.output_len()
            }

            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                let h = Hex(self.0, self.1, true);
                h.const_eval()
            }
        }

        impl LowerHex<$signed> {
            pub const fn output_len(&self) -> usize {
                let h = Hex(self.0 as $unsigned, self.1, false);
                h.output_len()
            }

            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                let h = Hex(self.0 as $unsigned, self.1, false);
                h.const_eval()
            }
        }

        impl UpperHex<$signed> {
            pub const fn output_len(&self) -> usize {
                let h = Hex(self.0 as $unsigned, self.1, true);
                h.output_len()
            }

            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                let h = Hex(self.0 as $unsigned, self.1, true);
                h.const_eval()
            }
        }
    };
}

impl_integer_hex!(u8, i8);
impl_integer_hex!(u16, i16);
impl_integer_hex!(u32, i32);
impl_integer_hex!(u64, i64);
impl_integer_hex!(u128, i128);
impl_integer_hex!(usize, isize);

#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_lowerhex {
    ($x: expr, $spec: expr) => {{
        const OUTPUT_LEN: usize = $crate::__ctfe::LowerHex($x, $spec).output_len();
        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
            $crate::__ctfe::LowerHex($x, $spec).const_eval();
        $crate::__strbuf_as_str!(&OUTPUT_BUF)
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_upperhex {
    ($x: expr, $spec: expr) => {{
        const OUTPUT_LEN: usize = $crate::__ctfe::UpperHex($x, $spec).output_len();
        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
            $crate::__ctfe::UpperHex($x, $spec).const_eval();
        $crate::__strbuf_as_str!(&OUTPUT_BUF)
    }};
}

pub struct Binary<T>(pub T, pub FmtSpec);

macro_rules! impl_integer_binary {
    ($unsigned: ty, $signed: ty) => {
        impl Binary<$unsigned> {
            pub const fn output_len(&self) -> usize {
                let mut x = self.0;
                let mut ans = 0;
                loop {
                    ans += 1;
                    x /= 2;
                    if x == 0 {
                        break;
                    }
                }
                if self.1.alternate {
                    ans += 2;
                }
                ans
            }

            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                let mut buf = [0; N];
                let mut pos = 0;
                let mut x = self.0;
                loop {
                    buf[pos] = b'0' + (x % 2) as u8;
                    pos += 1;
                    x /= 2;
                    if x == 0 {
                        break;
                    }
                }
                if self.1.alternate {
                    buf[pos] = b'b';
                    pos += 1;
                    buf[pos] = b'0';
                    pos += 1;
                }
                constfn_assert!(pos == N);
                let buf = crate::bytes::reversed(buf);
                unsafe { StrBuf::new_unchecked(buf) }
            }
        }

        impl Binary<$signed> {
            pub const fn output_len(&self) -> usize {
                let b = Binary(self.0 as $unsigned, self.1);
                b.output_len()
            }

            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
                let b = Binary(self.0 as $unsigned, self.1);
                b.const_eval()
            }
        }
    };
}

impl_integer_binary!(u8, i8);
impl_integer_binary!(u16, i16);
impl_integer_binary!(u32, i32);
impl_integer_binary!(u64, i64);
impl_integer_binary!(u128, i128);
impl_integer_binary!(usize, isize);

#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_binary {
    ($x: expr, $spec: expr) => {{
        const OUTPUT_LEN: usize = $crate::__ctfe::Binary($x, $spec).output_len();
        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
            $crate::__ctfe::Binary($x, $spec).const_eval();
        $crate::__strbuf_as_str!(&OUTPUT_BUF)
    }};
}