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 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}