firewheel_macros/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6
7mod diff;
8mod firewheel_manifest;
9mod patch;
10
11#[proc_macro_derive(Diff, attributes(diff))]
12pub fn derive_diff(input: TokenStream) -> TokenStream {
13    diff::derive_diff(input)
14        .unwrap_or_else(syn::Error::into_compile_error)
15        .into()
16}
17
18#[proc_macro_derive(Patch, attributes(diff))]
19pub fn derive_patch(input: TokenStream) -> TokenStream {
20    patch::derive_patch(input)
21        .unwrap_or_else(syn::Error::into_compile_error)
22        .into()
23}
24
25/// Derive this to signify that a struct implements `Clone`, cloning
26/// does not allocate or deallocate data, and the data will not be
27/// dropped on the audio thread if the struct is dropped.
28#[proc_macro_derive(RealtimeClone)]
29pub fn derive_realtime_clone(input: TokenStream) -> TokenStream {
30    derive_realtime_clone_inner(input)
31        .unwrap_or_else(syn::Error::into_compile_error)
32        .into()
33}
34
35fn derive_realtime_clone_inner(input: TokenStream) -> syn::Result<TokenStream2> {
36    let input: syn::DeriveInput = syn::parse(input)?;
37    let identifier = &input.ident;
38    let (_, diff_path) = get_paths();
39
40    let (impl_generics, ty_generics, where_generics) = input.generics.split_for_impl();
41
42    Ok(quote! {
43        #[automatically_derived]
44        impl #impl_generics #diff_path::RealtimeClone for #identifier #ty_generics #where_generics {}
45    })
46}
47
48fn get_paths() -> (syn::Path, TokenStream2) {
49    let firewheel_path =
50        firewheel_manifest::FirewheelManifest::default().get_path("firewheel_core");
51    let diff_path = quote! { #firewheel_path::diff };
52
53    (firewheel_path, diff_path)
54}
55
56fn should_skip(attrs: &[syn::Attribute]) -> bool {
57    let mut skip = false;
58    for attr in attrs {
59        if attr.path().is_ident("diff") {
60            attr.parse_nested_meta(|meta| {
61                if meta.path.is_ident("skip") {
62                    skip = true;
63                }
64
65                Ok(())
66            })
67            .expect("infallible operation");
68        }
69    }
70
71    skip
72}
73
74fn struct_fields(data: &syn::Fields) -> impl Iterator<Item = (syn::Member, &syn::Type)> {
75    // NOTE: a trivial optimization would be to automatically
76    // flatten structs with only a single field so their
77    // paths can be one index shorter.
78    data.iter()
79        .enumerate()
80        .filter(|(_, f)| !should_skip(&f.attrs))
81        .map(|(i, f)| (as_member(f.ident.as_ref(), i), &f.ty))
82}
83
84fn as_member(ident: Option<&syn::Ident>, index: usize) -> syn::Member {
85    ident.map_or_else(
86        || syn::Member::from(index),
87        |ident| syn::Member::Named(ident.clone()),
88    )
89}
90
91#[derive(Default)]
92struct TypeSet<'a>(Vec<&'a syn::Type>);
93
94impl<'a> TypeSet<'a> {
95    pub fn insert(&mut self, ty: &'a syn::Type) -> bool {
96        // let already_exists = self.0.iter().any(|existing| match (ty, existing) {
97        //     (syn::Type::Path(a), syn::Type::Path(b)) => {
98        //         // If we want a concise set of type bounds, we'll
99        //         // need additional syn features -- I don't want to write this myself.
100        //         a.qself == b.qself
101        //             && a.path.segments.len() == b.path.segments.len()
102        //             && a.path
103        //                 .segments
104        //                 .iter()
105        //                 .zip(&b.path.segments)
106        //                 .all(|(a, b)| {
107        //                     a.arguments
108        //                 })
109        //     }
110        //     _ => false,
111        // });
112
113        // if already_exists {
114        //     return false;
115        // }
116
117        self.0.push(ty);
118        true
119    }
120}
121
122impl<'a> IntoIterator for TypeSet<'a> {
123    type Item = &'a syn::Type;
124    type IntoIter = <Vec<&'a syn::Type> as IntoIterator>::IntoIter;
125
126    fn into_iter(self) -> Self::IntoIter {
127        self.0.into_iter()
128    }
129}
130
131impl<'a> core::ops::Deref for TypeSet<'a> {
132    type Target = [&'a syn::Type];
133
134    fn deref(&self) -> &Self::Target {
135        &self.0
136    }
137}