keket_graph_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Attribute, ItemStruct, parse_macro_input, parse_quote};
4
5fn has_asset_deps_attr(attrs: &[Attribute]) -> bool {
6    attrs.iter().any(|attr| attr.path.is_ident("asset_deps"))
7}
8
9/// Derives the `AssetTree` trait for a struct.
10/// This macro will automatically implement the `AssetTree` trait for the struct,
11/// allowing it to return its asset dependencies based on the fields that have
12/// the `#[asset_deps]` attribute.
13#[proc_macro_derive(AssetTree, attributes(asset_deps))]
14pub fn asset_tree_struct(input: TokenStream) -> TokenStream {
15    let ItemStruct {
16        ident,
17        fields,
18        mut generics,
19        ..
20    } = parse_macro_input!(input as ItemStruct);
21    let generics_params = generics.params.clone();
22    let where_clause = generics.make_where_clause();
23    for param in &generics_params {
24        if let syn::GenericParam::Type(ty) = param {
25            where_clause.predicates.push(parse_quote!(#ty: AssetTree));
26        }
27    }
28    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
29    let fields = fields
30        .iter()
31        .filter(|field| has_asset_deps_attr(&field.attrs))
32        .filter_map(|field| field.ident.as_ref())
33        .collect::<Vec<_>>();
34    quote! {
35        impl #impl_generics AssetTree for #ident #ty_generics #where_clause {
36            fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic> {
37                let mut result = vec![];
38                #(
39                    result.extend(self.#fields.asset_dependencies());
40                )*
41                result
42            }
43        }
44    }
45    .into()
46}