derive_deftly_macros/
define.rs1use super::framework::*;
4
5#[derive(Debug, Clone)]
6pub struct TemplateDefinition {
7 pub imported_definitions_d_escaped: TokenStream,
9 pub doc_attrs: DocAttributes,
10 export: Option<MacroExport>,
11 pub templ_name: TemplateName,
12 options: UnprocessedOptions,
13 pub template: TokenStream,
14}
15
16#[derive(Debug, Clone)]
17enum DefineMacroInput {
18 WithUses {
19 uses: modules::SomeUseModules,
20 rest: TokenStream,
21 },
22 Immediate(TemplateDefinition),
23}
24
25impl Parse for DefineMacroInput {
26 fn parse(input: ParseStream) -> syn::Result<Self> {
27 let uses = modules::UseModule::parse_several(input)?;
28 if let Some(uses) = modules::SomeUseModules::divert(uses) {
29 let rest = input.parse()?;
30 return Ok(DefineMacroInput::WithUses { uses, rest });
31 }
32
33 input.parse().map(DefineMacroInput::Immediate)
34 }
35}
36
37impl Parse for TemplateDefinition {
38 fn parse(input: ParseStream) -> syn::Result<Self> {
39 let doc_attrs = input.parse()?;
43 if let Some::<Token![$]>(dollar) = input.parse()? {
44 return Err(dollar.error(
45 "expected docs attributes and template name (note that expansions in template docs are only allowed if modules were imported with use `use`)"
46 ));
47 }
48 let export = MacroExport::parse_option(input)?;
49 let templ_name = input.parse()?;
50 let options =
51 UnprocessedOptions::parse(&input, OpContext::TemplateDefinition)?;
52 let la = input.lookahead1();
53 if la.peek(Token![=]) {
54 let equals: Token![=] = input.parse()?;
55 return Err(equals.error(
56 "You must now write `define_derive_deftly! { Template: ... }`, not `Template =`, since derive-deftly version 0.14.0"
57 ));
58 } else if la.peek(Token![:]) {
59 let _colon: Token![:] = input.parse()?;
60 } else {
61 return Err(la.error());
62 };
63 let template = input.parse()?;
64 Ok(TemplateDefinition {
65 imported_definitions_d_escaped: TokenStream::new(),
66 doc_attrs,
67 export,
68 templ_name,
69 options,
70 template,
71 })
72 }
73}
74
75pub fn escape_dollars(input: TokenStream) -> TokenStream {
127 enum St {
128 Dollar,
129 DollarBrace,
130 Other,
131 }
132
133 impl St {
134 fn exp_kw(&self) -> bool {
135 match self {
136 St::Dollar | St::DollarBrace => true,
137 St::Other => false,
138 }
139 }
140 }
141
142 fn handle_tt(itt: TokenTree, st: St, out: &mut TokenStream) -> St {
143 let ott = match itt {
144 TT::Group(g) => {
145 let delim = g.delimiter();
146 let span = g.span_open();
147 let stream = g.stream();
148 let st = match (st, delim) {
149 (St::Dollar, Delimiter::Brace) => St::DollarBrace,
150 _ => St::Other,
151 };
152 let stream = handle_ts(stream, st);
153 let g = group_new_with_span(delim, span, stream);
154 TT::Group(g)
155 }
156 TT::Punct(p) if p.as_char() == '$' => {
157 out.extend(quote_spanned! {p.span()=> #p orig_dollar });
158 return St::Dollar;
159 }
160 TT::Ident(i) if st.exp_kw() && i == "crate" => {
161 out.extend(quote_spanned! {i.span()=> _dd_intern_crate });
162 return St::Other;
163 }
164 other => other,
165 };
166 out.extend([ott]);
167 St::Other
168 }
169
170 fn handle_ts(input: TokenStream, mut st: St) -> TokenStream {
171 let mut out = TokenStream::new();
172 for itt in input {
173 st = handle_tt(itt, st, &mut out);
174 }
175 out
176 }
177
178 handle_ts(input, St::Other)
179}
180
181pub fn define_derive_deftly_func_macro(
183 input: TokenStream,
184) -> Result<TokenStream, syn::Error> {
185 dprint_block!(&input, "define_derive_deftly! input");
186
187 let input = syn::parse2(input)?;
188
189 match input {
190 DefineMacroInput::WithUses { uses, rest } => {
191 let output =
192 uses.output(format_ident!("define"), quote! {}, rest)?;
193 dprint_block!(
194 &output,
195 "define_derive_deftly! output, via modules",
196 );
197 Ok(output)
198 }
199 DefineMacroInput::Immediate(definition) => {
200 define_template(definition, "define_derive_deftly! output")
201 }
202 }
203}
204
205pub fn define_template(
206 definition: TemplateDefinition,
207 #[allow(unused)] dprint_prefix: &str,
208) -> syn::Result<TokenStream> {
209 let TemplateDefinition {
210 imported_definitions_d_escaped,
211 doc_attrs,
212 export,
213 templ_name,
214 options,
215 template,
216 } = definition;
217
218 let mut output = TokenStream::new();
219
220 let (template, parsed_template) = {
221 let mut template = template;
222 let parsed = Parser::parse2(
223 {
224 let ue = options.beta_enabled;
225 move |input: ParseStream| TopTemplate::parse(input, ue)
226 },
227 template.clone(),
228 )
229 .map_err(|e| {
230 e.into_compile_error().to_tokens(&mut output);
232 template = TokenStream::new();
234 ()
236 });
237 (template, parsed)
238 };
239
240 let _: Result<TopTemplate, ()> = parsed_template;
241 let template = escape_dollars(template);
242
243 let templ_mac_name = templ_name.macro_name();
244
245 let doc_attrs = doc_attrs.to_tokens_with_addendum(format_args!(
246 r#"
247
248This is a `derive_deftly` template. Do not invoke it directly.
249To use it, write: `#[derive(Deftly)] #[derive_deftly({})]`."#,
250 templ_name,
251 ));
252
253 let engine_macro;
254 let export_attr;
255 match export {
256 None => {
257 export_attr = quote! {};
258 engine_macro = engine_macro_name()?;
259 }
260 Some(pub_token) => {
261 let span = pub_token.span();
262 export_attr = quote_spanned!(span=> #[macro_export]);
263 engine_macro = quote_spanned!(span=> $crate::derive_deftly::derive_deftly_engine);
264 }
265 }
266
267 output.extend(quote! {
270 #doc_attrs
271 #export_attr
272 macro_rules! #templ_mac_name {
273 {
274 { $($driver:tt)* }
275 [ $($aoptions:tt)* ]
276 ( $($future:tt)* )
277 $($tpassthrough:tt)*
278 } => {
279 #engine_macro! {
280 { $( $driver )* }
281 [ $($aoptions)* ]
282 ()
283 { # template }
284 ( $crate; [#options] #templ_name;
285 { #imported_definitions_d_escaped } )
286 $($tpassthrough)*
287 }
288 };
289 { $($wrong:tt)* } => {
290 compile_error!{concat!(
291 "wrong input to derive-deftly template macro ",
292 stringify!(#templ_mac_name),
293 "; might be due to incompatible derive-deftly versions(s)",
294 )}
295 };
296 }
297 });
298
299 dprint_block!(&output, "{} {}", dprint_prefix, templ_mac_name);
300 Ok(output)
301}