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}