1use super::framework::*;
4use define::TemplateDefinition;
5
6#[derive(Debug, Clone)]
11pub struct UseModule {
12 name: ModulePath,
13 #[allow(unused)]
14 beta_mods: beta::Enabled, }
16
17#[derive(Debug, Clone)]
19pub struct SomeUseModules {
20 outer: UseModule,
21 uses: Vec<UseModule>,
22}
23
24#[derive(Debug)]
30pub struct SharedDef {
31 stream_d_escaped: TokenStream,
32}
33
34pub type SharedDefs = Concatenated<SharedDef>;
35
36#[derive(Debug)]
38pub struct ModuleDefinition {
39 doc_attrs: DocAttributes,
40 export: Option<MacroExport>,
41 name: ModuleName,
42 uses: Vec<UseModule>,
43 defs: SharedDefs,
44 #[allow(unused)]
45 beta_mods: beta::Enabled, }
47
48#[derive(Debug, Default)]
50pub struct ImportedDefinitions {
51 parsed: Template<TokenAccumulator>,
52}
53impl Deref for ImportedDefinitions {
54 type Target = Template<TokenAccumulator>;
55 fn deref(&self) -> &Template<TokenAccumulator> {
56 &self.parsed
57 }
58}
59
60impl ModuleDefinition {
61 fn parse(
62 input: ParseStream,
63 beta_mods: beta::Enabled,
64 ) -> syn::Result<Self> {
65 let doc_attrs = input.parse()?;
66 let export = MacroExport::parse_option(input)?;
67 let name = input.parse()?;
68 let mut options = DdOptions::default();
69 options.parse_update(input, OpContext::ModuleDefinition)?;
70 let beta_content = options.beta_enabled.ok_or(error_generator!(
71 "beta derive-deftly feature used, without `beta_deftly` module option"
72 ));
73
74 let _colon: Token![:] = input.parse()?;
75 let uses = UseModule::parse_several(input)?;
76
77 let defs = SharedDefs::parse(
78 input,
79 beta_content,
80 OrigDollarHandledDiscriminants::NotFound,
81 )?;
82
83 Ok(ModuleDefinition {
84 doc_attrs,
85 export,
86 name,
87 uses,
88 defs,
89 beta_mods,
90 })
91 }
92}
93
94impl SharedDefs {
95 fn parse(
96 input: ParseStream,
97 beta_content: beta::MaybeEnabled,
98 exp_d_escaped: OrigDollarHandledDiscriminants,
99 ) -> syn::Result<Self> {
100 let mut defs = vec![];
117 while input.peek(Token![$]) {
118 let def_end_cursor = {
119 let input = input.fork();
120 let subst = beta::with_maybe_enabled(beta_content, || {
121 Subst::<TokenAccumulator>::parse_entire(&input)
122 })?;
123 match subst.sd {
124 SD::define(..) | SD::defcond(..) => {}
125 _other => return Err(subst.kw_span.error(
126 "only ${define..} and ${defcond..} are allowed here",
127 )),
128 }
129 input.cursor()
130 };
131
132 let stream_d_escaped = (|| {
133 let mut out = TokenStream::new();
134
135 let dollar: Token![$] = input.parse()?;
136 dollar.to_tokens(&mut out);
137
138 let got_d_escaped = deescape_orig_dollar(input)?;
139
140 if got_d_escaped.discriminant() != exp_d_escaped {
141 return Err(dollar.error(format_args!(
142 "escaping not as expected: got={:?}, exp={:?}",
143 exp_d_escaped,
144 got_d_escaped.discriminant(),
145 )));
146 }
147
148 let g_in: proc_macro2::Group = input.parse()?;
149
150 let o_d_span;
151 let g_out;
152
153 match got_d_escaped {
154 OrigDollarHandled::NotFound => {
155 o_d_span = dollar.span();
156 g_out = group_clone_set_stream(
157 &g_in,
158 escape_dollars(g_in.stream()),
159 );
160 }
161 OrigDollarHandled::Found(span) => {
162 o_d_span = span;
166 g_out = g_in;
167 }
168 }
169
170 out.extend(quote_spanned! {o_d_span=> orig_dollar });
171 g_out.to_tokens(&mut out);
172
173 if input.cursor() != def_end_cursor {
174 return Err(input.error(
175 "shared definitions precheck desynchronised with ad-hoc TS extraction"
176 ));
177 }
178
179 Ok(out)
180 })()
181 .map_err(adviseable::advise_incompatibility)?;
182
183 defs.push(SharedDef { stream_d_escaped })
184 }
185 Ok(Concatenated(defs))
186 }
187}
188
189impl UseModule {
190 pub fn parse_several(input: ParseStream) -> syn::Result<Vec<Self>> {
191 let mut uses = vec![];
192 while input.peek(Token![use]) {
193 let use_kw: Token![use] = input.parse()?;
194 let kw_span = use_kw.span();
195 let name = input.parse()?;
196 let _: Token![;] = input.parse()?;
197 uses.push(UseModule {
198 name,
199 beta_mods: beta::Enabled::new_for_modules_feature(kw_span)?,
200 });
201 }
202 Ok(uses)
203 }
204}
205
206impl SomeUseModules {
207 pub fn divert(mut uses: Vec<UseModule>) -> Option<Self> {
209 let outer = uses.pop()?;
210 Some(SomeUseModules { outer, uses })
211 }
212
213 pub fn output(
215 self,
216 mode: syn::Ident,
217 new_prefix_d_escaped: TokenStream,
218 main_passthrough: TokenStream,
219 ) -> syn::Result<TokenStream> {
220 let use_outer = self.outer.name.macro_path();
221 let uses = self
222 .uses
223 .into_iter()
224 .map(|u| {
225 let path = u.name.macro_path();
226 quote! { #path {} }
227 })
228 .collect_vec();
229
230 let engine = engine_macro_name()?;
231
232 Ok(quote! {
233 #use_outer! {
234 via_modules [ {} #(#uses)* #engine {} #mode () ]
235 { #new_prefix_d_escaped }
236 { #main_passthrough }
237 ( $ )
238 }
239 })
240 }
241}
242
243impl ToTokens for SharedDef {
244 fn to_tokens(&self, out: &mut TokenStream) {
245 self.stream_d_escaped.to_tokens(out);
246 }
247}
248
249impl Parse for ImportedDefinitions {
250 fn parse(input: ParseStream) -> syn::Result<Self> {
251 let impossible = || {
252 adviseable::advise_incompatibility(
253 input.error("unsupported content in imported definitions"),
254 )
255 };
256
257 let parsed: Template<TokenAccumulator> = beta::with_maybe_enabled(
258 beta::Enabled::new_for_imported_definitions(),
259 || input.parse(),
260 )?;
261
262 for te in &parsed.elements {
263 let subst = match te {
264 TE::Subst(subst) => subst,
265 _other => return Err(impossible()),
266 };
267 match &subst.sd {
268 SD::define(..) | SD::defcond(..) => {}
269 _other => return Err(impossible()),
270 }
271 }
272
273 Ok(ImportedDefinitions { parsed })
274 }
275}
276
277struct ExpansionsAndAttrsOnly;
278impl SpecialParseContext for ExpansionsAndAttrsOnly {
279 fn before_element_hook(
280 &mut self,
281 input: ParseStream,
282 ) -> syn::Result<Option<SpecialInstructions>> {
283 Ok(
284 if input.peek(Token![$])
285 || input.peek(Token![#])
286 || input.peek(token::Bracket)
287 {
289 None
290 } else {
291 Some(SpecialInstructions::EndOfTemplate)
292 },
293 )
294 }
295}
296
297impl TemplateDefinition {
298 pub fn parse_from_via_modules(
300 prefix_d_escaped: ParseStream,
301 main: ParseStream,
302 ) -> syn::Result<Self> {
303 let prefix_d_escaped: TokenStream = prefix_d_escaped.parse()?;
304 let imported_definitions: ImportedDefinitions =
305 syn::parse2(prefix_d_escaped.clone())?;
306
307 let doc_attrs = beta::with_maybe_enabled(
308 Err(error_generator!(
309 "beta template features are not available in the docs preamble, even with beta_deftly in the template options; but you could put the beta use into a reuseable module"
310 )),
311 || {
312 Template::<TokenAccumulator>::parse_special(
313 main,
314 &mut ExpansionsAndAttrsOnly,
315 )
316 },
317 )?;
318
319 let mut definition: TemplateDefinition =
320 main.parse()?;
323
324 definition.doc_attrs = {
325 let mut expanded = TokenAccumulator::new();
326 Template::expand_iter(
327 chain!(
328 &imported_definitions.parsed.elements,
329 &doc_attrs.elements,
330 ),
331 GeneralContext::NoDriver(&DriverlessContext {
332 defs: DefinitionsContext::default(),
333 driver_needed: error_generator!(
334 "not allowed outside the main template body; no driver data structure here"
335 ),
336 }),
337 &mut expanded,
338 );
339 let expanded = expanded.tokens()?;
340 syn::parse2(expanded)?
341 };
342 definition.imported_definitions_d_escaped = prefix_d_escaped;
343
344 let respan_span = Span::call_site();
351 let respan = |ts: &mut TokenStream| {
352 *ts = respan_hygiene(ts.clone(), respan_span)
353 };
354 respan(&mut definition.imported_definitions_d_escaped);
355 respan(&mut definition.template);
356 Ok(definition)
363 }
364}
365
366impl ModuleDefinition {
367 pub fn parse_from_via_modules(
368 prefix_d_escaped: ParseStream,
369 main: ParseStream,
370 beta_mods: beta::Enabled,
371 ) -> syn::Result<Self> {
372 let mut mod_def = ModuleDefinition::parse(main, beta_mods)?;
373 let imported = SharedDefs::parse(
374 prefix_d_escaped,
375 beta::Enabled::new_for_imported_definitions(),
376 OrigDollarHandledDiscriminants::Found,
377 )?;
378 mod_def.defs =
379 Concatenated(chain!(imported.0, mod_def.defs.0,).collect_vec());
380 Ok(mod_def)
381 }
382}
383
384pub fn define_derive_deftly_module(
386 input: TokenStream,
387) -> Result<TokenStream, syn::Error> {
388 dprint_block!(&input, "define_derive_deftly_module! input");
389
390 let beta_mods = beta::Enabled::new_for_modules_feature(input.span())?;
391
392 let md = Parser::parse2(
393 |input: ParseStream<'_>| ModuleDefinition::parse(input, beta_mods),
394 input,
395 )?;
396
397 define_module(md, "define_derive_deftly_module! output")
398}
399
400pub fn define_module(
401 md: ModuleDefinition,
402 #[allow(unused)] dprint_prefix: &str,
403) -> syn::Result<TokenStream> {
404 let ModuleDefinition {
405 doc_attrs,
406 export,
407 name,
408 uses,
409 defs,
410 beta_mods: _,
411 } = md;
412
413 let export_attr = export.map(|pub_token| {
414 let span = pub_token.span();
415 quote_spanned!(span=> #[macro_expor])
416 });
417
418 let mac_name = name.macro_name();
419 let doc_attrs = doc_attrs.to_tokens_with_addendum(format_args!(
420 r#"
421
422This is a `derive_deftly` reuseable template code module.
423Do not invoke it directly."#,
424 ));
425
426 let mk_return = |output| {
427 dprint_block!(&output, "{} {}", dprint_prefix, mac_name);
428 Ok(output)
429 };
430
431 if let Some(divert) = SomeUseModules::divert(uses) {
432 return mk_return(divert.output(
433 format_ident!("defmod"),
434 defs.to_token_stream(),
435 quote! { #doc_attrs #export_attr #name: },
436 )?);
437 }
438
439 mk_return(quote! {
440 #doc_attrs
441 #export_attr
442 macro_rules! #mac_name {
443 {
444 via_modules [
445 { $($our_opts:tt)* }
446 $next_macro:path
447 { $($next_opts:tt)* }
448 $($rest:tt)*
449 ]
450 { $($predefs:tt)* }
451 { $($main:tt)* }
452 ( $($extra:tt)* )
453 $($ignored:tt)*
454 } => {
455 $next_macro! {
456 via_modules [ { $($next_opts)* } $($rest)* ]
457 { #defs $($predefs)* }
458 { $($main)* }
459 ( $($extra)* )
460 }
461 };
462 { $($wrong:tt)* } => {
463 compile_error!{concat!(
464 "wrong input to derive-deftly module macro ",
465 stringify!(#mac_name: $($wrong)*),
466 "; might be due to incompatible derive-deftly versions(s)",
467 )}
468 };
469 }
470 })
471}