const_str/__ctfe/
to_str.rs

1#![allow(unsafe_code)]
2
3use super::str::StrBuf;
4use crate::utf8::CharEncodeUtf8;
5
6pub struct ToStr<T>(pub T);
7
8impl ToStr<&str> {
9    pub const fn output_len(&self) -> usize {
10        self.0.len()
11    }
12
13    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
14        StrBuf::from_str(self.0)
15    }
16}
17
18impl ToStr<bool> {
19    const fn bool_to_str(b: bool) -> &'static str {
20        if b {
21            "true"
22        } else {
23            "false"
24        }
25    }
26
27    pub const fn output_len(&self) -> usize {
28        Self::bool_to_str(self.0).len()
29    }
30
31    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
32        let bytes: &[u8] = Self::bool_to_str(self.0).as_bytes();
33        let buf = crate::bytes::merge([0; N], bytes);
34        unsafe { StrBuf::new_unchecked(buf) }
35    }
36}
37
38impl ToStr<char> {
39    pub const fn output_len(&self) -> usize {
40        self.0.len_utf8()
41    }
42
43    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
44        let ch = CharEncodeUtf8::new(self.0);
45        let buf = crate::bytes::merge([0; N], ch.as_bytes());
46        unsafe { StrBuf::new_unchecked(buf) }
47    }
48}
49
50macro_rules! impl_integer_to_str {
51    ($unsigned: ty, $signed: ty) => {
52        impl ToStr<$unsigned> {
53            pub const fn output_len(&self) -> usize {
54                let mut x = self.0;
55                let mut ans = 1;
56                while x > 9 {
57                    ans += 1;
58                    x /= 10;
59                }
60                ans
61            }
62
63            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
64                let mut buf = [0; N];
65                let mut pos = 0;
66                let mut x = self.0;
67                loop {
68                    buf[pos] = b'0' + (x % 10) as u8;
69                    pos += 1;
70                    x /= 10;
71                    if x == 0 {
72                        break;
73                    }
74                }
75                assert!(pos == N);
76                let buf = crate::bytes::reversed(buf);
77                unsafe { StrBuf::new_unchecked(buf) }
78            }
79        }
80
81        impl ToStr<$signed> {
82            pub const fn output_len(&self) -> usize {
83                let x = self.0;
84                let abs_len = ToStr(x.unsigned_abs()).output_len();
85                abs_len + (x < 0) as usize
86            }
87
88            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
89                let mut buf = [0; N];
90                let mut pos = 0;
91
92                let mut x = self.0.unsigned_abs();
93
94                loop {
95                    buf[pos] = b'0' + (x % 10) as u8;
96                    pos += 1;
97                    x /= 10;
98                    if x == 0 {
99                        break;
100                    }
101                }
102
103                if self.0 < 0 {
104                    buf[pos] = b'-';
105                    pos += 1;
106                }
107
108                assert!(pos == N);
109                let buf = crate::bytes::reversed(buf);
110                unsafe { StrBuf::new_unchecked(buf) }
111            }
112        }
113    };
114}
115
116impl_integer_to_str!(u8, i8);
117impl_integer_to_str!(u16, i16);
118impl_integer_to_str!(u32, i32);
119impl_integer_to_str!(u64, i64);
120impl_integer_to_str!(u128, i128);
121impl_integer_to_str!(usize, isize);
122
123/// Converts a value to a string slice.
124///
125/// The input type must be one of
126///
127/// + [`&str`]
128/// + [`char`]
129/// + [`bool`]
130/// + [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], [`usize`]
131/// + [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`]
132///
133/// This macro is [const-context only](./index.html#const-context-only).
134///
135/// # Examples
136///
137/// ```
138/// const A: &str = const_str::to_str!("A");
139/// assert_eq!(A, "A");
140///
141/// const B: &str = const_str::to_str!('我');
142/// assert_eq!(B, "我");
143///
144/// const C: &str = const_str::to_str!(true);
145/// assert_eq!(C, "true");
146///
147/// const D: &str = const_str::to_str!(1_u8 + 1);
148/// assert_eq!(D, "2");
149///
150/// const E: &str = const_str::to_str!(-21_i32 * 2);
151/// assert_eq!(E, "-42")
152/// ```
153///
154#[macro_export]
155macro_rules! to_str {
156    ($x: expr) => {{
157        const OUTPUT_LEN: usize = $crate::__ctfe::ToStr($x).output_len();
158        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
159            $crate::__ctfe::ToStr($x).const_eval();
160        OUTPUT_BUF.as_str()
161    }};
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167
168    #[test]
169    fn test_to_str() {
170        macro_rules! test_to_str {
171            ($ty: ty, $x: expr) => {{
172                const X: $ty = $x;
173                const OUTPUT_LEN: usize = ToStr(X).output_len();
174                const OUTPUT_BUF: StrBuf<OUTPUT_LEN> = ToStr(X).const_eval();
175
176                let output = OUTPUT_BUF.as_str();
177                let ans = X.to_string();
178                assert_eq!(OUTPUT_LEN, ans.len());
179                assert_eq!(output, ans);
180            }};
181        }
182
183        test_to_str!(&str, "lovelive superstar");
184
185        test_to_str!(bool, true);
186        test_to_str!(bool, false);
187
188        test_to_str!(char, '鲤');
189        test_to_str!(char, '鱼');
190
191        test_to_str!(u8, 0);
192        test_to_str!(u16, 0);
193        test_to_str!(u32, 0);
194        test_to_str!(u64, 0);
195        test_to_str!(u128, 0);
196
197        test_to_str!(u8, 10);
198        test_to_str!(u8, 128);
199        test_to_str!(u8, u8::MAX);
200
201        test_to_str!(u64, 1);
202        test_to_str!(u64, 10);
203        test_to_str!(u64, 42);
204        test_to_str!(u64, u64::MAX);
205
206        test_to_str!(u128, u128::MAX);
207
208        test_to_str!(i8, 0);
209        test_to_str!(i16, 0);
210        test_to_str!(i32, 0);
211        test_to_str!(i64, 0);
212        test_to_str!(i128, 0);
213
214        test_to_str!(i8, -10);
215        test_to_str!(i8, -42);
216        test_to_str!(i8, i8::MAX);
217        test_to_str!(i8, i8::MIN);
218
219        test_to_str!(i64, 1);
220        test_to_str!(i64, 10);
221        test_to_str!(i64, -42);
222        test_to_str!(i64, i64::MAX);
223        test_to_str!(i64, i64::MIN);
224
225        test_to_str!(i128, i128::MAX);
226        test_to_str!(i128, i128::MIN);
227    }
228}