1#![recursion_limit="256"]
2
3#[macro_use] extern crate quote;
4extern crate proc_macro;
5extern crate devise_core;
6
7use proc_macro::TokenStream;
8
9use devise_core::*;
10use devise_core::ext::SpanDiagnosticExt;
11
12#[derive(Default)]
13struct Naked(bool);
14
15impl FromMeta for Naked {
16 fn from_meta(meta: &MetaItem) -> Result<Naked> {
17 if let Some(meta) = meta.list()?.next() {
18 if meta.path()?.is_ident("naked") {
19 return Ok(Naked(true));
20 }
21 }
22
23 Err(meta.span().error("expected `naked`"))
24 }
25}
26
27#[proc_macro_derive(FromMeta, attributes(meta))]
28pub fn derive_from_meta(input: TokenStream) -> TokenStream {
29 DeriveGenerator::build_for(input, quote!(impl ::devise::FromMeta))
30 .support(Support::NamedStruct)
31 .inner_mapper(MapperBuild::new()
32 .with_output(|_, output| quote! {
33 fn from_meta(
34 __meta: &::devise::MetaItem
35 ) -> ::devise::Result<Self> {
36 #[allow(unused_imports)]
37 use ::devise::ext::SpanDiagnosticExt;
38
39 #output
40 }
41 })
42 .try_fields_map(|_, fields| {
43 let naked = |field: &Field| -> bool {
44 Naked::one_from_attrs("meta", &field.attrs)
45 .unwrap()
46 .unwrap_or_default()
47 .0
48 };
49
50 for field in fields.iter() {
52 Naked::one_from_attrs("meta", &field.attrs)?;
53 }
54
55 let constructors = fields.iter().map(|f| {
56 let (ident, span) = (f.ident.as_ref().unwrap(), f.span().into());
57 quote_spanned!(span => #[allow(unused_assignments)] let mut #ident = None;)
58 });
59
60 let naked_matchers = fields.iter().filter(naked).map(|f| {
61 let (ident, span) = (f.ident.as_ref().unwrap(), f.span().into());
62 let (name, ty) = (ident.to_string(), &f.ty);
63
64 quote_spanned! { span =>
65 match __list.next() {
66 Some(__i) if __i.is_bare() => {
67 #ident = Some(<#ty>::from_meta(__i)?)
68 },
69 Some(__i) => return Err(__i.span().error(
70 "unexpected keyed parameter: expected literal or identifier")),
71 None => return Err(__span.error(
72 format!("missing expected parameter: `{}`", #name))),
73 };
74 }
75 });
76
77 let named_matchers = fields.iter().filter(|f| !naked(f)).map(|f| {
78 let (ident, span) = (f.ident.as_ref().unwrap(), f.span().into());
79 let (name, ty) = (ident.to_string(), &f.ty);
80
81 quote_spanned! { span =>
82 if __name == #name {
83 if #ident.is_some() {
84 return Err(__span.error(
85 format!("duplicate attribute parameter: {}", #name)));
86 }
87
88 #ident = Some(<#ty>::from_meta(__meta)?);
89 continue;
90 }
91 }
92 });
93
94 let builders = fields.iter().map(|f| {
95 let (ident, span) = (f.ident.as_ref().unwrap(), f.span().into());
96 let name = ident.to_string();
97
98 quote_spanned! { span =>
99 #ident: #ident.or_else(::devise::FromMeta::default)
100 .ok_or_else(|| __span.error(
101 format!("missing required attribute parameter: `{}`", #name)))?,
102 }
103 });
104
105 Ok(quote! {
106 use ::devise::Spanned;
107
108 let __span = __meta.span();
111 let mut __list = __meta.list()?;
112
113 #(#constructors)*
115
116 #(#naked_matchers)*
118
119 for __meta in __list {
121 let __span = __meta.span();
122 let __name = match __meta.name() {
123 Some(__ident) => __ident,
124 None => return Err(__span.error("expected key/value `key = value`")),
125 };
126
127 #(#named_matchers)*
128
129 let __msg = format!("unexpected attribute parameter: `{}`", __name);
130 return Err(__span.error(__msg));
131 }
132
133 Ok(Self { #(#builders)* })
135 })
136 })
137 )
138 .to_tokens()
139}