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
25fn get_paths() -> (syn::Path, TokenStream2) {
26    let firewheel_path =
27        firewheel_manifest::FirewheelManifest::default().get_path("firewheel_core");
28    let diff_path = quote! { #firewheel_path::diff };
29
30    (firewheel_path, diff_path)
31}
32
33fn should_skip(attrs: &[syn::Attribute]) -> bool {
34    let mut skip = false;
35    for attr in attrs {
36        if attr.path().is_ident("diff") {
37            attr.parse_nested_meta(|meta| {
38                if meta.path.is_ident("skip") {
39                    skip = true;
40                }
41
42                Ok(())
43            })
44            .expect("infallible operation");
45        }
46    }
47
48    skip
49}
50
51fn struct_fields(data: &syn::Fields) -> impl Iterator<Item = (syn::Member, &syn::Type)> {
52    // NOTE: a trivial optimization would be to automatically
53    // flatten structs with only a single field so their
54    // paths can be one index shorter.
55    data.iter()
56        .enumerate()
57        .filter(|(_, f)| !should_skip(&f.attrs))
58        .map(|(i, f)| (as_member(f.ident.as_ref(), i), &f.ty))
59}
60
61fn as_member(ident: Option<&syn::Ident>, index: usize) -> syn::Member {
62    ident.map_or_else(
63        || syn::Member::from(index),
64        |ident| syn::Member::Named(ident.clone()),
65    )
66}
67
68#[derive(Default)]
69struct TypeSet<'a>(Vec<&'a syn::Type>);
70
71impl<'a> TypeSet<'a> {
72    pub fn insert(&mut self, ty: &'a syn::Type) -> bool {
73        // let already_exists = self.0.iter().any(|existing| match (ty, existing) {
74        //     (syn::Type::Path(a), syn::Type::Path(b)) => {
75        //         // If we want a concise set of type bounds, we'll
76        //         // need additional syn features -- I don't want to write this myself.
77        //         a.qself == b.qself
78        //             && a.path.segments.len() == b.path.segments.len()
79        //             && a.path
80        //                 .segments
81        //                 .iter()
82        //                 .zip(&b.path.segments)
83        //                 .all(|(a, b)| {
84        //                     a.arguments
85        //                 })
86        //     }
87        //     _ => false,
88        // });
89
90        // if already_exists {
91        //     return false;
92        // }
93
94        self.0.push(ty);
95        true
96    }
97}
98
99impl<'a> IntoIterator for TypeSet<'a> {
100    type Item = &'a syn::Type;
101    type IntoIter = <Vec<&'a syn::Type> as IntoIterator>::IntoIter;
102
103    fn into_iter(self) -> Self::IntoIter {
104        self.0.into_iter()
105    }
106}
107
108impl<'a> core::ops::Deref for TypeSet<'a> {
109    type Target = [&'a syn::Type];
110
111    fn deref(&self) -> &Self::Target {
112        &self.0
113    }
114}