shipyard_proc 0.6.1

Crate providing attribute macro to Shipyard
Documentation
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Error, Result};

pub(crate) fn expand_borrow_info(
    name: syn::Ident,
    generics: syn::Generics,
    data: syn::Data,
) -> Result<TokenStream> {
    let fields = match data {
        syn::Data::Struct(data_struct) => data_struct.fields,
        _ => {
            return Err(Error::new(
                Span::call_site(),
                "BorrowInfo can only be implemented on structs",
            ))
        }
    };

    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

    match fields {
        syn::Fields::Named(fields) => {
            let field_type = fields.named.iter().map(|field| &field.ty);

            let field_is_default = fields.named.iter().map(|field| {
                field.attrs.iter().any(|attr| {
                    let mut is_default = false;

                    if attr.path().is_ident("shipyard") {
                        let _ = attr.parse_nested_meta(|meta| {
                            is_default = is_default || meta.path.is_ident("default");

                            Ok(())
                        });
                    }

                    is_default
                })
            });

            let field_info = field_type.clone().zip(field_is_default.clone()).map(
                |(field_type, field_is_default)| {
                    if field_is_default {
                        quote!(();)
                    } else {
                        quote!(
                            <#field_type>::borrow_info(info);
                        )
                    }
                },
            );
            let field_tracking =
                field_type
                    .zip(field_is_default)
                    .map(|(field_type, field_is_default)| {
                        if field_is_default {
                            quote!(();)
                        } else {
                            quote!(
                                <#field_type>::enable_tracking(enable_tracking_fn);
                            )
                        }
                    });

            Ok(quote!(
                unsafe impl #impl_generics ::shipyard::borrow::BorrowInfo for #name #ty_generics #where_clause {
                    fn borrow_info(info: &mut Vec<::shipyard::scheduler::info::TypeInfo>) {
                        #(#field_info)*
                    }
                    fn enable_tracking(
                        enable_tracking_fn: &mut Vec<fn(&::shipyard::all_storages::AllStorages) -> core::result::Result<(), ::shipyard::error::GetStorage>>,
                    ) {
                        #(#field_tracking)*
                    }
                }
            ))
        }
        syn::Fields::Unnamed(fields) => {
            let field_type = fields.unnamed.iter().map(|field| &field.ty);
            let field_type_clone = field_type.clone();

            Ok(quote!(
                unsafe impl #impl_generics ::shipyard::borrow::BorrowInfo for #name #ty_generics #where_clause {
                    fn borrow_info(info: &mut Vec<::shipyard::scheduler::info::TypeInfo>) {
                        #(<#field_type_clone>::borrow_info(info);)*
                    }
                    fn enable_tracking(
                        enable_tracking_fn: &mut Vec<fn(&::shipyard::all_storages::AllStorages) -> core::result::Result<(), ::shipyard::error::GetStorage>>,
                    ) {
                        #(<#field_type>::enable_tracking(enable_tracking_fn);)*
                    }
                }
            ))
        }
        syn::Fields::Unit => Err(Error::new(
            Span::call_site(),
            "Unit struct cannot borrow from World",
        )),
    }
}