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    #[test]
142    fn test_parse() {
143        macro_rules! test_parse {
144            ($s: expr, $ty: tt) => {{
145                const OUTPUT: $ty = $crate::parse!($s, $ty);
146                let ans: $ty = $s.parse().unwrap();
147                assert_eq!(OUTPUT, ans)
148            }};
149        }
150
151        test_parse!("true", bool);
152        test_parse!("false", bool);
153
154        test_parse!("啊", char);
155
156        test_parse!("0", u8);
157        test_parse!("-1", i8);
158        test_parse!("+42000", u32);
159        test_parse!("-42000", i32);
160    }
161}