firewheel_macros/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::{quote, ToTokens};
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::DataStruct) -> Vec<(TokenStream2, &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    let fields: Vec<_> = match &data.fields {
56        syn::Fields::Named(fields) => fields
57            .named
58            .iter()
59            .filter(|f| !should_skip(&f.attrs))
60            .map(|f| (f.ident.as_ref().unwrap().to_token_stream(), &f.ty))
61            .collect(),
62        syn::Fields::Unnamed(fields) => fields
63            .unnamed
64            .iter()
65            .filter(|f| !should_skip(&f.attrs))
66            .enumerate()
67            .map(|(i, f)| {
68                let accessor: syn::Index = i.into();
69                (accessor.to_token_stream(), &f.ty)
70            })
71            .collect(),
72        syn::Fields::Unit => Vec::new(),
73    };
74
75    fields
76}