1use quote::ToTokens;
2use syn::Lit;
3use syn::spanned::Spanned;
4
5use crate::diagnostic::Diagnostics;
6use crate::meta::Arg;
7use crate::meta::Args;
8use crate::types::Input;
9
10mod attr;
11mod data;
12mod fields;
13mod variants;
14
15pub use attr::*;
16pub use data::*;
17pub use fields::*;
18pub use variants::*;
19
20pub trait FromInput: Sized {
27 fn from_input(input: &Input) -> crate::Result<Self>;
28}
29
30pub struct Extract<T: FromInput>(T);
35
36impl<T: FromInput> Extract<T> {
37 pub fn inner(self) -> T {
38 self.0
39 }
40}
41
42impl<T: FromInput> std::ops::Deref for Extract<T> {
43 type Target = T;
44
45 fn deref(&self) -> &Self::Target {
46 &self.0
47 }
48}
49
50impl<T: FromInput> std::ops::DerefMut for Extract<T> {
51 fn deref_mut(&mut self) -> &mut Self::Target {
52 &mut self.0
53 }
54}
55
56impl<T: FromInput> FromInput for Extract<T> {
57 fn from_input(input: &Input) -> crate::Result<Self> {
58 T::from_input(input).map(Extract)
59 }
60}
61
62impl FromInput for proc_macro2::Ident {
63 fn from_input(input: &Input) -> crate::Result<Self> {
64 Ok(input.ident().clone())
65 }
66}
67
68impl FromInput for syn::Generics {
69 fn from_input(input: &Input) -> crate::Result<Self> {
70 Ok(input.generics().clone())
71 }
72}
73
74impl FromInput for syn::Visibility {
75 fn from_input(input: &Input) -> crate::Result<Self> {
76 Ok(input.vis().clone())
77 }
78}
79
80pub trait FromArg: Sized {
87 fn from_arg(arg: &Arg) -> crate::Result<Self>;
88}
89
90impl FromArg for bool {
91 fn from_arg(arg: &Arg) -> crate::Result<Self> {
92 match arg {
93 Arg::Flag(_) => Ok(true),
94 _ => Err(Diagnostics::error(arg.span(), "expected flag for bool")),
95 }
96 }
97}
98
99impl FromArg for String {
100 fn from_arg(arg: &Arg) -> crate::Result<Self> {
101 match arg.as_expr_lit() {
102 Some(Lit::Str(s)) => Ok(s.value()),
103 _ => Err(Diagnostics::error(arg.span(), "expected string literal")),
104 }
105 }
106}
107
108macro_rules! impl_from_arg_int {
109 ($($t:ty),*) => {
110 $(
111 impl FromArg for $t {
112 fn from_arg(arg: &Arg) -> crate::Result<Self> {
113 match arg.as_expr_lit() {
114 Some(Lit::Int(i)) => i.base10_parse::<$t>().map_err(|e| Diagnostics::error(i.span(), e.to_string())),
115 _ => Err(Diagnostics::error(arg.span(), concat!("expected integer literal for ", stringify!($t)))),
116 }
117 }
118 }
119 )*
120 };
121}
122
123impl_from_arg_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
124
125macro_rules! impl_from_arg_float {
126 ($($t:ty),*) => {
127 $(
128 impl FromArg for $t {
129 fn from_arg(arg: &Arg) -> crate::Result<Self> {
130 match arg.as_expr_lit() {
131 Some(Lit::Float(f)) => f.base10_parse::<$t>().map_err(|e| Diagnostics::error(f.span(), e.to_string())),
132 _ => Err(Diagnostics::error(arg.span(), concat!("expected float literal for ", stringify!($t)))),
133 }
134 }
135 }
136 )*
137 };
138}
139
140impl_from_arg_float!(f32, f64);
141
142impl FromArg for char {
143 fn from_arg(arg: &Arg) -> crate::Result<Self> {
144 match arg.as_expr_lit() {
145 Some(Lit::Char(c)) => Ok(c.value()),
146 _ => Err(Diagnostics::error(arg.span(), "expected char literal")),
147 }
148 }
149}
150
151impl FromArg for syn::Ident {
152 fn from_arg(arg: &Arg) -> crate::Result<Self> {
153 match arg {
154 Arg::Flag(i) => Ok(i.clone()),
155 _ => Err(Diagnostics::error(arg.span(), "expected identifier")),
156 }
157 }
158}
159
160impl FromArg for syn::Path {
161 fn from_arg(arg: &Arg) -> crate::Result<Self> {
162 match arg {
163 Arg::Flag(i) => Ok(syn::Path::from(i.clone())),
164 _ => Err(Diagnostics::error(
165 arg.span(),
166 "expected identifier for path",
167 )),
168 }
169 }
170}
171
172impl FromArg for syn::Expr {
173 fn from_arg(arg: &Arg) -> crate::Result<Self> {
174 match arg {
175 Arg::Expr(_, expr) => Ok(expr.clone()),
176 _ => Err(Diagnostics::error(arg.span(), "expected expression")),
177 }
178 }
179}
180
181impl FromArg for syn::LitStr {
182 fn from_arg(arg: &Arg) -> crate::Result<Self> {
183 match arg.as_expr_lit() {
184 Some(Lit::Str(s)) => Ok(s.clone()),
185 _ => Err(Diagnostics::error(arg.span(), "expected string literal")),
186 }
187 }
188}
189
190impl FromArg for syn::LitInt {
191 fn from_arg(arg: &Arg) -> crate::Result<Self> {
192 match arg.as_expr_lit() {
193 Some(Lit::Int(i)) => Ok(i.clone()),
194 _ => Err(Diagnostics::error(arg.span(), "expected integer literal")),
195 }
196 }
197}
198
199impl<T: FromArg> FromArg for Option<T> {
200 fn from_arg(arg: &Arg) -> crate::Result<Self> {
201 T::from_arg(arg).map(Some)
202 }
203}
204
205impl<T: FromArg> FromArg for Vec<T> {
206 fn from_arg(arg: &Arg) -> crate::Result<Self> {
207 match arg {
208 Arg::List(_, args) => args.iter().map(T::from_arg).collect(),
209 _ => Err(Diagnostics::error(arg.span(), "expected list argument")),
210 }
211 }
212}
213
214impl FromArg for Args {
215 fn from_arg(arg: &Arg) -> crate::Result<Self> {
216 match arg {
217 Arg::List(_, args) => syn::parse2(args.to_token_stream()).map_err(Diagnostics::from),
218 _ => Err(Diagnostics::error(arg.span(), "expected list argument")),
219 }
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 mod bool_impl {
228 use super::*;
229
230 #[test]
231 fn from_flag() {
232 let arg: Arg = syn::parse_str("skip").unwrap();
233 assert_eq!(bool::from_arg(&arg).unwrap(), true);
234 }
235
236 #[test]
237 fn from_expr_is_err() {
238 let arg: Arg = syn::parse_str("skip = true").unwrap();
239 assert!(bool::from_arg(&arg).is_err());
240 }
241 }
242
243 mod string_impl {
244 use super::*;
245
246 #[test]
247 fn from_expr_string_lit() {
248 let arg: Arg = syn::parse_str("rename = \"foo\"").unwrap();
249 assert_eq!(String::from_arg(&arg).unwrap(), "foo");
250 }
251
252 #[test]
253 fn from_lit_string() {
254 let arg: Arg = syn::parse_str("\"bar\"").unwrap();
255 assert_eq!(String::from_arg(&arg).unwrap(), "bar");
256 }
257
258 #[test]
259 fn from_flag_is_err() {
260 let arg: Arg = syn::parse_str("skip").unwrap();
261 assert!(String::from_arg(&arg).is_err());
262 }
263 }
264
265 mod int_impl {
266 use super::*;
267
268 #[test]
269 fn i64_from_lit() {
270 let arg: Arg = syn::parse_str("42").unwrap();
271 assert_eq!(i64::from_arg(&arg).unwrap(), 42);
272 }
273
274 #[test]
275 fn i64_from_expr() {
276 let arg: Arg = syn::parse_str("count = 7").unwrap();
277 assert_eq!(i64::from_arg(&arg).unwrap(), 7);
278 }
279
280 #[test]
281 fn i64_from_flag_is_err() {
282 let arg: Arg = syn::parse_str("skip").unwrap();
283 assert!(i64::from_arg(&arg).is_err());
284 }
285
286 #[test]
287 fn u32_from_lit() {
288 let arg: Arg = syn::parse_str("100").unwrap();
289 assert_eq!(u32::from_arg(&arg).unwrap(), 100u32);
290 }
291 }
292
293 mod char_impl {
294 use super::*;
295
296 #[test]
297 fn from_lit() {
298 let arg: Arg = syn::parse_str("'x'").unwrap();
299 assert_eq!(char::from_arg(&arg).unwrap(), 'x');
300 }
301
302 #[test]
303 fn from_flag_is_err() {
304 let arg: Arg = syn::parse_str("skip").unwrap();
305 assert!(char::from_arg(&arg).is_err());
306 }
307 }
308
309 mod ident_impl {
310 use super::*;
311
312 #[test]
313 fn from_flag() {
314 let arg: Arg = syn::parse_str("my_ident").unwrap();
315 let ident = syn::Ident::from_arg(&arg).unwrap();
316 assert_eq!(ident.to_string(), "my_ident");
317 }
318
319 #[test]
320 fn from_expr_is_err() {
321 let arg: Arg = syn::parse_str("x = 1").unwrap();
322 assert!(syn::Ident::from_arg(&arg).is_err());
323 }
324 }
325
326 mod option_impl {
327 use super::*;
328
329 #[test]
330 fn some_from_expr() {
331 let arg: Arg = syn::parse_str("rename = \"foo\"").unwrap();
332 assert_eq!(
333 Option::<String>::from_arg(&arg).unwrap(),
334 Some("foo".to_string())
335 );
336 }
337
338 #[test]
339 fn propagates_err() {
340 let arg: Arg = syn::parse_str("skip").unwrap();
341 assert!(Option::<String>::from_arg(&arg).is_err());
342 }
343 }
344
345 mod vec_impl {
346 use super::*;
347
348 #[test]
349 fn from_list() {
350 let arg: Arg = syn::parse_str("tags(\"a\", \"b\", \"c\")").unwrap();
351 let v = Vec::<String>::from_arg(&arg).unwrap();
352 assert_eq!(v, vec!["a", "b", "c"]);
353 }
354
355 #[test]
356 fn from_flag_is_err() {
357 let arg: Arg = syn::parse_str("skip").unwrap();
358 assert!(Vec::<String>::from_arg(&arg).is_err());
359 }
360 }
361
362 mod args_impl {
363 use super::*;
364
365 #[test]
366 fn from_list() {
367 let arg: Arg = syn::parse_str("inner(a = 1, b = 2)").unwrap();
368 let args = Args::from_arg(&arg).unwrap();
369 assert!(args.has("a"));
370 assert!(args.has("b"));
371 }
372
373 #[test]
374 fn from_flag_is_err() {
375 let arg: Arg = syn::parse_str("skip").unwrap();
376 assert!(Args::from_arg(&arg).is_err());
377 }
378 }
379}