derive_deftly_macros/
derive.rs1use super::prelude::*;
4
5enum InvocationEntry {
7 Precanned(TemplatePath, UnprocessedOptions),
8}
9
10struct InvocationAttr {
12 entries: Punctuated<InvocationEntry, token::Comma>,
13}
14
15#[derive(Default)]
17struct AdhocAttr {
18 pub_: Option<MacroExport>,
19}
20
21impl Parse for InvocationEntry {
22 fn parse(input: ParseStream) -> syn::Result<Self> {
23 let entry = if input.lookahead1().peek(Token![pub]) {
24 return Err(input.error("`pub` must be in #[derive_deftly_adhoc]"));
25 } else {
26 let path = input.parse()?;
27 let options = if input.peek(syn::token::Bracket) {
28 let tokens;
29 let _bracket = bracketed!(tokens in input);
30 UnprocessedOptions::parse(
31 &tokens,
32 OpContext::DriverApplicationCapture,
33 )?
34 } else {
35 UnprocessedOptions::default()
36 };
37 InvocationEntry::Precanned(path, options)
38 };
39 Ok(entry)
40 }
41}
42
43impl Parse for InvocationAttr {
44 fn parse(input: ParseStream) -> syn::Result<Self> {
45 let entries = Punctuated::parse_terminated(input)?;
46 Ok(InvocationAttr { entries })
47 }
48}
49
50fn check_for_misplaced_atrs(data: &syn::Data) -> syn::Result<()> {
51 let attrs = |attrs: &[syn::Attribute]| {
52 for attr in attrs {
53 if let Some(_) = ["derive_deftly", "derive_deftly_adhoc"]
54 .iter()
55 .find(|forbidden| attr.path().is_ident(forbidden))
56 {
57 return Err(attr.error(
58 "attribute is only meaningful at the data structure toplevel"
59 ));
60 }
61 }
62 Ok(())
63 };
64
65 let fields = |fs: &Punctuated<syn::Field, _>| {
66 for f in fs.iter() {
67 attrs(&f.attrs)?;
68 }
69 Ok(())
70 };
71
72 let variantish = |fs: &syn::Fields| match fs {
73 syn::Fields::Unit => Ok(()),
74 syn::Fields::Named(n) => fields(&n.named),
75 syn::Fields::Unnamed(u) => fields(&u.unnamed),
76 };
77
78 let variants = |vs: &Punctuated<syn::Variant, _>| {
79 for v in vs.iter() {
80 attrs(&v.attrs)?;
81 variantish(&v.fields)?;
82 }
83 Ok(())
84 };
85
86 match data {
87 syn::Data::Struct(s) => variantish(&s.fields),
88 syn::Data::Union(u) => fields(&u.fields.named),
89 syn::Data::Enum(e) => variants(&e.variants),
90 }
91}
92
93pub fn derive_deftly(
95 driver_stream: TokenStream,
96) -> Result<TokenStream, syn::Error> {
97 use engine::ChainNext;
98
99 let driver: syn::DeriveInput = syn::parse2(driver_stream.clone())?;
100
101 dprint_block!(&driver_stream, "#[derive(Deftly)] input");
102
103 let driver_mac_name =
104 format_ident!("derive_deftly_driver_{}", &driver.ident);
105
106 let precanned_paths: Vec<(TemplatePath, UnprocessedOptions)> = driver
107 .attrs
108 .iter()
109 .map(|attr| {
110 if !attr.path().is_ident("derive_deftly") {
111 return Ok(None);
112 }
113 let InvocationAttr { entries } = attr.parse_in_parens()?;
114 Ok(Some(entries))
115 })
116 .flatten_ok()
117 .flatten_ok()
118 .filter_map(|entry| match entry {
119 Err(e) => Some(Err(e)),
120 Ok(InvocationEntry::Precanned(path, options)) => {
121 Some(Ok((path, options)))
122 }
123 })
124 .collect::<syn::Result<Vec<_>>>()?;
125
126 let adhoc: Option<AdhocAttr> = driver
127 .attrs
128 .iter()
129 .filter(|attr| attr.path().is_ident("derive_deftly_adhoc"))
130 .inspect(|_: &&syn::Attribute| ())
131 .map(|attr| {
132 let adhoc = match &attr.meta {
133 syn::Meta::Path(_) => AdhocAttr { pub_: None },
134 syn::Meta::NameValue(nv) => {
135 return Err(nv
136 .eq_token
137 .error("arguments (if any) must be in parens"))
138 }
139 syn::Meta::List(syn::MetaList {
140 path: _,
141 delimiter,
142 tokens,
143 }) => {
144 match delimiter {
145 syn::MacroDelimiter::Paren(_) => Ok(()),
146 syn::MacroDelimiter::Brace(t) => Err(t.span),
147 syn::MacroDelimiter::Bracket(t) => Err(t.span),
148 }
149 .map_err(|span| span.error("expected parentheses"))?;
150 let pub_ = Parser::parse2(
151 MacroExport::parse_option,
152 tokens.clone(),
153 )?;
154 AdhocAttr { pub_ }
155 }
156 };
157 Ok::<AdhocAttr, syn::Error>(adhoc)
158 })
159 .inspect(|_: &Result<AdhocAttr, _>| ())
160 .reduce(|a, b| {
162 let pub_ = chain!(a?.pub_, b?.pub_).next();
163 Ok(AdhocAttr { pub_ })
164 })
165 .transpose()?;
166
167 check_for_misplaced_atrs(&driver.data)?;
168
169 let engine_macro = engine_macro_name()?;
170
171 let driver_escaped = escape_dollars(driver_stream);
193
194 let mut output = TokenStream::new();
195
196 let mut accum_start = TokenStream::new();
197
198 if let Some(adhoc) = adhoc {
199 accum_start.extend(quote!( _meta_used * ));
200
201 let macro_export = adhoc
202 .pub_
203 .map(|export| {
204 let macro_export =
205 quote_spanned!(export.span()=> #[macro_export]);
206 Ok::<_, syn::Error>(macro_export)
207 })
208 .transpose()?;
209
210 output.extend(quote! {
211 #[allow(unused_macros)]
212 #macro_export
213 macro_rules! #driver_mac_name {
214 {
215 { $($template:tt)* }
216 { ($orig_dollar:tt) $(future:tt)* }
217 $($dpassthrough:tt)*
218 } => {
219 #engine_macro!{
220 { #driver_escaped }
221 ( )
222 { $($template)* }
223 $($dpassthrough)*
224 }
225 };
226 { $($wrong:tt)* } => {
227 compile_error!{concat!(
228 "wrong input to derive-deftly driver inner macro ",
229 stringify!(#driver_mac_name),
230 "; might be due to incompatible derive-deftly versions(s)",
231 )}
232 };
233 }
234 });
235 }
236
237 let (chain_next, chain_rest);
238 {
239 let mut errs = ErrorAccumulator::default();
240 let mut chain = chain!(
241 precanned_paths
242 .into_iter()
243 .map(|(templ_path, aoptions)| {
244 let call = templ_path.macro_path().to_token_stream();
245 let ao_versions = OpCompatVersions::ours();
246 let after_driver = quote!( [ #ao_versions #aoptions ] () );
247 Ok(ChainNext { call, after_driver })
248 })
249 .filter_map(|r| errs.handle(r)),
250 [ChainNext {
251 call: engine_macro.clone(),
252 after_driver: quote!( . () ),
253 }],
254 );
255 chain_next = chain.next().expect("should have been nonempty!");
256 chain_rest = {
257 let mut rest = TokenStream::new();
258 for c in chain {
259 c.to_tokens(&mut rest);
260 }
261 rest
262 };
263
264 errs.finish()?;
265 }
266 let ChainNext { call, after_driver } = chain_next;
267
268 output.extend(quote! {
269 #call !{
270 { #driver }
271 #after_driver
272 [ #chain_rest ]
273 [ #accum_start ]
274 }
275 });
276
277 dprint_block!(&output, "#[derive(Deftly)] output for {}", &driver.ident);
278
279 Ok(output)
280}