shipyard_proc 0.6.1

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

pub(crate) fn expand_world_borrow(
    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(),
                "WorldBorrow can only be implemented on structs",
            ))
        }
    };

    let mut gat_generics = generics.clone();
    for generic in gat_generics.params.iter_mut() {
        if let syn::GenericParam::Lifetime(lifetime) = generic {
            lifetime.lifetime = parse_quote!('__view);
            break;
        }
    }

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

    match fields {
        syn::Fields::Named(fields) => {
            let field_name = fields.named.iter().map(|field| &field.ident);
            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 = field_name
                .into_iter()
                .zip(field_type)
                .zip(field_is_default)
                .map(|((field_name, field_type), field_is_default)| {
                    if field_is_default {
                        quote!(
                            #field_name: core::default::Default::default()
                        )
                    } else {
                        quote!(
                            #field_name: <#field_type as ::shipyard::borrow::WorldBorrow>::world_borrow(world, last_run, current)?
                        )
                    }
                });

            Ok(quote!(
                impl #impl_generics ::shipyard::borrow::WorldBorrow for #name #ty_generics #where_clause {
                    type WorldView<'__view> = #name #gat_ty_generics;

                    fn world_borrow<'__w>(world: & '__w ::shipyard::World, last_run: Option<::shipyard::advanced::tracking::TrackingTimestamp>, current: ::shipyard::advanced::tracking::TrackingTimestamp) -> core::result::Result<Self::WorldView<'__w>, ::shipyard::error::GetStorage> {
                        Ok(#name {
                            #(#field),*
                        })
                    }
                }
            ))
        }
        syn::Fields::Unnamed(fields) => {
            let world_borrow = fields.unnamed.iter().map(|field| {
                let field_type = &field.ty;
                quote!(<#field_type as ::shipyard::borrow::WorldBorrow>::world_borrow(world, last_run, current)?)
            });

            Ok(quote!(
                impl #impl_generics ::shipyard::borrow::WorldBorrow for #name #ty_generics #where_clause {
                    type WorldView<'__view> = #name #gat_ty_generics;

                    fn world_borrow<'__w>(world: & '__w ::shipyard::World, last_run: Option<::shipyard::advanced::tracking::TrackingTimestamp>, current: ::shipyard::advanced::tracking::TrackingTimestamp) -> core::result::Result<Self::WorldView<'__w>, ::shipyard::error::GetStorage> {
                        Ok(#name(#(#world_borrow),*))
                    }
                }
            ))
        }
        syn::Fields::Unit => Err(Error::new(
            Span::call_site(),
            "Unit struct cannot borrow from World",
        )),
    }
}