encrust_macros/
parser.rs

1use std::path::{Path, PathBuf};
2
3use proc_macro2::Span;
4use syn::{LitInt, LitStr, Token, bracketed, parse::Parse};
5
6#[cfg_attr(test, derive(Debug, PartialEq))]
7pub enum Literal {
8    U8(u8),
9    U16(u16),
10    U32(u32),
11    U64(u64),
12    U128(u128),
13    Usize(usize),
14    I8(i8),
15    I16(i16),
16    I32(i32),
17    I64(i64),
18    I128(i128),
19    Isize(isize),
20    String(String),
21    Array(Vec<Literal>),
22}
23
24impl Parse for Literal {
25    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
26        if input.peek(LitInt) || input.peek(Token![-]) {
27            let integer: LitInt = input.parse()?;
28
29            Ok(match integer.suffix() {
30                "i8" => Self::I8(integer.base10_parse::<i8>()?),
31                "i16" => Self::I16(integer.base10_parse::<i16>()?),
32                "i32" => Self::I32(integer.base10_parse::<i32>()?),
33                "i64" => Self::I64(integer.base10_parse::<i64>()?),
34                "i128" => Self::I128(integer.base10_parse::<i128>()?),
35                "isize" => Self::Isize(integer.base10_parse::<isize>()?),
36                "u8" => Self::U8(integer.base10_parse::<u8>()?),
37                "u16" => Self::U16(integer.base10_parse::<u16>()?),
38                "u32" => Self::U32(integer.base10_parse::<u32>()?),
39                "u64" => Self::U64(integer.base10_parse::<u64>()?),
40                "u128" => Self::U128(integer.base10_parse::<u128>()?),
41                "usize" => Self::Usize(integer.base10_parse::<usize>()?),
42                "" => {
43                    return Err(syn::Error::new(
44                        integer.span(),
45                        "No integer data type suffix supplied.",
46                    ));
47                }
48                _ => {
49                    return Err(syn::Error::new(
50                        integer.span(),
51                        format!(
52                            "Supplied integer type `{}` not supported by `encrust_integer`.",
53                            integer.suffix()
54                        ),
55                    ));
56                }
57            })
58        } else if input.peek(LitStr) {
59            let string: LitStr = input.parse()?;
60
61            Ok(Self::String(string.value()))
62        } else if input.peek(syn::token::Bracket) {
63            let mut content = Vec::new();
64            let buffer;
65            bracketed!(buffer in input);
66
67            while !buffer.is_empty() {
68                content.push(buffer.parse()?);
69
70                if !buffer.is_empty() {
71                    buffer.parse::<Token![,]>()?;
72                }
73            }
74
75            Ok(Self::Array(content))
76        } else {
77            Err(syn::Error::new(
78                input.span(),
79                "Unsupported input to `encrust`.",
80            ))
81        }
82    }
83}
84
85#[cfg_attr(test, derive(Debug, PartialEq))]
86pub struct LiteralVec(pub Vec<Literal>);
87
88impl Parse for LiteralVec {
89    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
90        let mut vec = Vec::new();
91
92        while !input.is_empty() {
93            vec.push(input.parse()?);
94
95            if !input.is_empty() {
96                input.parse::<Token![,]>()?;
97            }
98        }
99
100        Ok(Self(vec))
101    }
102}
103
104pub struct FilePath {
105    pub path: PathBuf,
106    pub span: Span,
107}
108
109impl Parse for FilePath {
110    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
111        let path_lit: LitStr = input.parse()?;
112        let path_str = path_lit.value();
113        let input_path = Path::new(path_str.as_str());
114
115        let path = if input_path.is_absolute() {
116            input_path.into()
117        } else {
118            Path::new(std::env!("CARGO_MANIFEST_DIR")).join(input_path)
119        };
120
121        Ok(Self {
122            path,
123            span: path_lit.span(),
124        })
125    }
126}
127
128#[cfg(feature = "hashstrings")]
129#[cfg_attr(test, derive(Debug, PartialEq))]
130pub struct ToHashString(pub String);
131
132#[cfg(feature = "hashstrings")]
133impl Parse for ToHashString {
134    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
135        let lit_str: LitStr = input.parse()?;
136
137        Ok(Self(lit_str.value()))
138    }
139}
140
141#[cfg(feature = "hashstrings")]
142#[cfg_attr(test, derive(Debug, PartialEq))]
143pub struct ToHashBytes(pub Vec<u8>);
144
145#[cfg(feature = "hashstrings")]
146impl Parse for ToHashBytes {
147    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
148        let mut bytes: Vec<u8> = Vec::new();
149        let buffer;
150        bracketed!(buffer in input);
151
152        while !buffer.is_empty() {
153            let lit: LitInt = buffer.parse()?;
154            bytes.push(lit.base10_parse()?);
155
156            if !buffer.is_empty() {
157                buffer.parse::<Token![,]>()?;
158            }
159        }
160
161        Ok(Self(bytes))
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn parse_numbers() {
171        let literal = syn::parse_str::<Literal>("-1i8").expect("Unable to parse literal");
172        assert_eq!(Literal::I8(-1), literal);
173        let literal = syn::parse_str::<Literal>("1u8").expect("Unable to parse literal");
174        assert_eq!(Literal::U8(1), literal);
175
176        let literal = syn::parse_str::<Literal>("-1i16").expect("Unable to parse literal");
177        assert_eq!(Literal::I16(-1), literal);
178        let literal = syn::parse_str::<Literal>("1u16").expect("Unable to parse literal");
179        assert_eq!(Literal::U16(1), literal);
180
181        let literal = syn::parse_str::<Literal>("-1i32").expect("Unable to parse literal");
182        assert_eq!(Literal::I32(-1), literal);
183        let literal = syn::parse_str::<Literal>("1u32").expect("Unable to parse literal");
184        assert_eq!(Literal::U32(1), literal);
185
186        let literal = syn::parse_str::<Literal>("-1i64").expect("Unable to parse literal");
187        assert_eq!(Literal::I64(-1), literal);
188        let literal = syn::parse_str::<Literal>("1u64").expect("Unable to parse literal");
189        assert_eq!(Literal::U64(1), literal);
190
191        let literal = syn::parse_str::<Literal>("-1i128").expect("Unable to parse literal");
192        assert_eq!(Literal::I128(-1), literal);
193        let literal = syn::parse_str::<Literal>("1u128").expect("Unable to parse literal");
194        assert_eq!(Literal::U128(1), literal);
195
196        let literal = syn::parse_str::<Literal>("-1isize").expect("Unable to parse literal");
197        assert_eq!(Literal::Isize(-1), literal);
198        let literal = syn::parse_str::<Literal>("1usize").expect("Unable to parse literal");
199        assert_eq!(Literal::Usize(1), literal);
200    }
201
202    #[test]
203    fn parse_number_fail_on_no_type() {
204        let literal = syn::parse_str::<Literal>("-1");
205        assert!(literal.is_err());
206    }
207
208    #[test]
209    fn parse_numbers_fail_on_outside_range() {
210        let literal = syn::parse_str::<Literal>("-1usize");
211        assert!(literal.is_err());
212
213        let literal = syn::parse_str::<Literal>("128i8");
214        assert!(literal.is_err());
215    }
216
217    #[test]
218    fn parse_string_literal() {
219        let literal =
220            syn::parse_str::<Literal>("\"The quick brown fox jumps over the lazy dog😊\"")
221                .expect("Unable to parse literal");
222        assert_eq!(
223            Literal::String("The quick brown fox jumps over the lazy dog😊".to_string()),
224            literal
225        );
226    }
227
228    #[test]
229    fn parse_array() {
230        let literal = syn::parse_str::<Literal>("[1u8,2u8,3u8]").expect("Unable to parse literal");
231        assert_eq!(
232            Literal::Array(vec![Literal::U8(1u8), Literal::U8(2u8), Literal::U8(3u8)]),
233            literal
234        );
235    }
236
237    #[test]
238    fn parse_vec() {
239        let literal = syn::parse_str::<LiteralVec>("1u8,2u8,3u8").expect("Unable to parse literal");
240        assert_eq!(
241            LiteralVec(vec![Literal::U8(1u8), Literal::U8(2u8), Literal::U8(3u8)]),
242            literal
243        );
244    }
245
246    #[test]
247    fn parse_paths() {
248        let path = syn::parse_str::<FilePath>("\"//absolute/path\"")
249            .expect("Unable to parse path literal");
250        assert_eq!(Path::new("//absolute/path"), path.path);
251
252        let rel_path =
253            syn::parse_str::<FilePath>("\"relative/path\"").expect("Unable to parse path literal");
254        assert_eq!(
255            Path::new(std::env!("CARGO_MANIFEST_DIR")).join("relative/path"),
256            rel_path.path
257        );
258    }
259
260    #[test]
261    fn parse_tohashstring() {
262        let string =
263            syn::parse_str::<ToHashString>("\"The quick brown fox jumps over the lazy dog😊\"")
264                .expect("Unable to parse literal");
265        assert_eq!(
266            ToHashString("The quick brown fox jumps over the lazy dog😊".to_string()),
267            string
268        );
269    }
270
271    #[test]
272    fn parse_tohashbytes() {
273        let bytes =
274            syn::parse_str::<ToHashBytes>("[0x01, 2, 3u8, 0b0]").expect("Unable to parse literal");
275        assert_eq!(ToHashBytes(vec![1, 2, 3, 0]), bytes);
276    }
277
278    #[test]
279    fn tohashbytes_fails_when_numbers_cannot_fit_u8() {
280        let too_large = syn::parse_str::<ToHashBytes>("[0, 256, 0]");
281        assert!(too_large.is_err());
282
283        let negative = syn::parse_str::<ToHashBytes>("[-1, 2, 3]");
284        assert!(negative.is_err());
285    }
286}