const_str/__ctfe/
parse.rs

1use core::marker::PhantomData;
2
3pub struct Parse<T, U>(T, PhantomData<fn(T) -> U>);
4
5impl<T, U> Parse<T, U> {
6    pub const fn new(t: T) -> Self {
7        Self(t, PhantomData)
8    }
9}
10
11impl Parse<&str, bool> {
12    pub const fn const_eval(&self) -> bool {
13        if crate::str::equal(self.0, "true") {
14            return true;
15        }
16        if crate::str::equal(self.0, "false") {
17            return false;
18        }
19        panic!("parse error")
20    }
21}
22
23impl Parse<&str, &str> {
24    pub const fn const_eval(&self) -> &str {
25        self.0
26    }
27}
28
29impl Parse<&str, char> {
30    pub const fn const_eval(&self) -> char {
31        let s = self.0.as_bytes();
32        if let Some((ch, count)) = crate::utf8::next_char(s) {
33            if count == s.len() {
34                return ch;
35            }
36        }
37        panic!("parse error")
38    }
39}
40
41trait IsSignedInteger {
42    const OUTPUT: bool;
43}
44
45macro_rules! mark_signed_integer {
46    ($s: expr, $($ty:ty),+) => {
47        $(
48            impl IsSignedInteger for $ty {
49                const OUTPUT: bool = $s;
50            }
51        )+
52    }
53}
54
55mark_signed_integer!(true, i8, i16, i32, i64, i128, isize);
56mark_signed_integer!(false, u8, u16, u32, u64, u128, usize);
57
58macro_rules! impl_integer_parse {
59    ($($ty: ty),+) => {$(
60        impl Parse<&str, $ty> {
61            pub const fn const_eval(&self) -> $ty {
62                let s = self.0.as_bytes();
63                let is_signed = <$ty as IsSignedInteger>::OUTPUT;
64                let (is_positive, digits) = match s {
65                    [] => panic!("parse error"),
66                    [x, xs @ ..] => match x {
67                        b'+' => (true, xs),
68                        b'-' if is_signed => (false, xs),
69                        _ => (true, s),
70                    },
71                };
72
73                let mut ans: $ty = 0;
74                let mut i = 0;
75                while i < digits.len() {
76                    let x = crate::ascii::num_from_dec_digit(digits[i]);
77
78                    match ans.checked_mul(10) {
79                        Some(val) => {
80                            let val = if is_positive {
81                                val.checked_add(x as _)
82                            } else {
83                                val.checked_sub(x as _)
84                            };
85                            match val {
86                                Some(val) => ans = val,
87                                None => panic!("parse error"),
88                            }
89                        }
90                        None => panic!("parse error"),
91                    };
92
93                    i += 1;
94                }
95
96                ans
97            }
98        }
99    )+};
100}
101
102impl_integer_parse!(u8, u16, u32, u64, u128, usize);
103impl_integer_parse!(i8, i16, i32, i64, i128, isize);
104
105/// Parse a value from a string slice.
106///
107/// The output type must be one of
108///
109/// + [`&str`](str)
110/// + [`char`]
111/// + [`bool`]
112/// + [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], [`usize`]
113/// + [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`]
114///
115/// This macro is [const-fn compatible](./index.html#const-fn-compatible).
116///
117/// # Examples
118///
119/// ```
120/// const S1: &str = "true";
121/// const X1: bool = const_str::parse!(S1, bool);
122/// assert_eq!(X1, true);
123///
124/// const S2: &str = "42";
125/// const X2: u32 = const_str::parse!(S2, u32);
126/// assert_eq!(X2, 42);
127///
128/// const S3: &str = "-1";
129/// const X3: i8 = const_str::parse!(S3, i8);
130/// assert_eq!(X3, -1);
131/// ```
132#[macro_export]
133macro_rules! parse {
134    ($s: expr, $ty: ty) => {{
135        $crate::__ctfe::Parse::<_, $ty>::new($s).const_eval()
136    }};
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_parse() {
145        macro_rules! test_parse {
146            ($s: expr, $ty: tt) => {{
147                const OUTPUT: $ty = $crate::parse!($s, $ty);
148                let ans: $ty = $s.parse().unwrap();
149                assert_eq!(OUTPUT, ans)
150            }};
151        }
152
153        test_parse!("true", bool);
154        test_parse!("false", bool);
155
156        test_parse!("啊", char);
157
158        test_parse!("0", u8);
159        test_parse!("-1", i8);
160        test_parse!("+42000", u32);
161        test_parse!("-42000", i32);
162    }
163
164    #[test]
165    fn test_parse_runtime() {
166        // Runtime tests for Parse<&str, bool>
167        let parse_true = Parse::<&str, bool>::new("true");
168        assert!(parse_true.const_eval());
169
170        let parse_false = Parse::<&str, bool>::new("false");
171        assert!(!parse_false.const_eval());
172
173        // Runtime tests for Parse<&str, &str>
174        let parse_str = Parse::<&str, &str>::new("hello");
175        assert_eq!(parse_str.const_eval(), "hello");
176
177        // Runtime tests for Parse<&str, char>
178        let parse_char = Parse::<&str, char>::new("a");
179        assert_eq!(parse_char.const_eval(), 'a');
180
181        let parse_unicode = Parse::<&str, char>::new("你");
182        assert_eq!(parse_unicode.const_eval(), '你');
183
184        // Runtime tests for Parse<&str, u8>
185        let parse_u8 = Parse::<&str, u8>::new("42");
186        assert_eq!(parse_u8.const_eval(), 42);
187
188        let parse_u8_zero = Parse::<&str, u8>::new("0");
189        assert_eq!(parse_u8_zero.const_eval(), 0);
190
191        // Runtime tests for Parse<&str, i8>
192        let parse_i8_pos = Parse::<&str, i8>::new("42");
193        assert_eq!(parse_i8_pos.const_eval(), 42);
194
195        let parse_i8_neg = Parse::<&str, i8>::new("-42");
196        assert_eq!(parse_i8_neg.const_eval(), -42);
197
198        // Runtime tests for Parse<&str, u64>
199        let parse_u64 = Parse::<&str, u64>::new("1234567890");
200        assert_eq!(parse_u64.const_eval(), 1234567890);
201
202        // Runtime tests for Parse<&str, i64>
203        let parse_i64 = Parse::<&str, i64>::new("-1234567890");
204        assert_eq!(parse_i64.const_eval(), -1234567890);
205    }
206}