Skip to main content

dada_util_procmacro/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use synstructure::decl_derive;
4
5mod boxed_async_fn;
6mod salsa_serialize;
7
8decl_derive!([FromImpls, attributes(no_from_impl)] => from_impls_derive);
9decl_derive!([SalsaSerialize] => salsa_serialize::salsa_serialize_derive);
10
11fn from_impls_derive(s: synstructure::Structure) -> TokenStream {
12    let result = s
13        .variants()
14        .iter()
15        .map(|variant| {
16            let variant_name = &variant.ast().ident;
17            let fields = &variant.ast().fields;
18
19            for attr in variant
20                .ast()
21                .attrs
22                .iter()
23                .filter(|a| a.meta.path().is_ident("no_from_impl"))
24            {
25                if attr.meta.require_path_only().is_err() {
26                    return Err(syn::Error::new_spanned(
27                        attr,
28                        "`no_from_impl` does not accept arguments",
29                    ));
30                }
31            }
32
33            if variant
34                .ast()
35                .attrs
36                .iter()
37                .any(|a| a.meta.path().is_ident("no_from_impl"))
38            {
39                return Ok(quote!());
40            }
41
42            if fields.len() != 1 {
43                return Err(syn::Error::new_spanned(
44                    variant.ast().ident,
45                    "each variant must have exactly one field",
46                ));
47            }
48
49            let field_ty = &fields.iter().next().unwrap().ty;
50            Ok(s.gen_impl(quote! {
51                gen impl From<#field_ty> for @Self {
52                    fn from(value: #field_ty) -> Self {
53                        Self::#variant_name(value)
54                    }
55                }
56
57            }))
58        })
59        .collect::<syn::Result<proc_macro2::TokenStream>>();
60
61    match result {
62        Ok(tokens) => tokens.into(),
63        Err(err) => err.into_compile_error().into(),
64    }
65}
66
67/// Transforms an async fn to return a `Box<dyn Future<Output = T>>`.
68///
69/// Adapted from the [`async_recursion`](https://crates.io/crates/async-recursion) crate authored by
70/// Robert Usher and licensed under MIT/APACHE-2.0.
71#[proc_macro_attribute]
72pub fn boxed_async_fn(args: TokenStream, input: TokenStream) -> TokenStream {
73    boxed_async_fn::boxed_async_fn(args, input)
74}