shed/facet/proc_macros/container_impl.rs (345 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under both the MIT license found in the * LICENSE-MIT file in the root directory of this source tree and the Apache * License, Version 2.0 found in the LICENSE-APACHE file in the root directory * of this source tree. */ use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{parse_macro_input, Attribute, Error, Expr, Fields, Ident, ItemStruct, Token, Type}; use crate::facet_crate_name; #[derive(Debug)] struct ContainerMembers { field_idents: Vec<Ident>, field_inits: Vec<Expr>, facet_idents: Vec<Ident>, facet_types: Vec<Type>, delegate_idents: Vec<Ident>, delegate_types: Vec<Type>, delegate_facets: Vec<Vec<Type>>, } impl ContainerMembers { fn extract(container: &mut ItemStruct) -> Result<Self, Error> { let mut field_idents = Vec::new(); let mut field_inits = Vec::new(); let mut facet_idents = Vec::new(); let mut facet_types = Vec::new(); let mut delegate_idents = Vec::new(); let mut delegate_types = Vec::new(); let mut delegate_facets = Vec::new(); match &mut container.fields { Fields::Named(named_fields) => { for field in named_fields.named.iter_mut() { let mut attr_found = false; let mut new_attrs = Vec::new(); for attr in field.attrs.drain(..) { if attr.path.is_ident("init") { if attr_found { return Err(Error::new( attr.span(), concat!( "facet::container field must have exactly one ", "of 'init', 'facet' or 'delegate', found multiple" ), )); } attr_found = true; let expr: Expr = attr.parse_args()?; field_idents .push(field.ident.clone().expect("named field must have a name")); field_inits.push(expr); } else if attr.path.is_ident("facet") { if attr_found { return Err(Error::new( attr.span(), concat!( "facet::container field must have exactly one ", "of 'init', 'facet' or 'delegate', found multiple" ), )); } attr_found = true; let mut facet_type = field.ty.clone(); if let Type::TraitObject(obj) = &mut facet_type { obj.bounds.push(syn::parse2(quote!(::std::marker::Send))?); obj.bounds.push(syn::parse2(quote!(::std::marker::Sync))?); obj.bounds.push(syn::parse2(quote!('static))?); } field.ty = syn::parse2(quote!(::std::sync::Arc<#facet_type>))?; facet_idents .push(field.ident.clone().expect("named field must have a name")); facet_types.push(facet_type); } else if attr.path.is_ident("delegate") { if attr_found { return Err(Error::new( attr.span(), concat!( "facet::container field must have exactly one ", "of 'init', 'facet' or 'delegate', found multiple" ), )); } attr_found = true; let delegate_type = field.ty.clone(); let facets = extract_delegate_facets(&attr)?; delegate_idents .push(field.ident.clone().expect("named field must have a name")); delegate_types.push(delegate_type); delegate_facets.push(facets); } else { new_attrs.push(attr); } } if !attr_found { return Err(Error::new( field.span(), concat!( "facet::container field must have exactly one ", "of 'init', 'facet' or 'delegate', found none" ), )); } field.attrs = new_attrs; } } _ => { return Err(Error::new( container.ident.span(), "facet::container requires a struct with named fields", )); } } Ok(ContainerMembers { field_idents, field_inits, facet_idents, facet_types, delegate_idents, delegate_types, delegate_facets, }) } } pub fn container( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let _ = parse_macro_input!(attr as syn::parse::Nothing); let container = parse_macro_input!(item as ItemStruct); match gen_container(container) { Ok(output) => output, Err(e) => e.to_compile_error(), } .into() } fn gen_container(mut container: ItemStruct) -> Result<TokenStream, Error> { let facet_crate = format_ident!("{}", facet_crate_name()); let members = ContainerMembers::extract(&mut container)?; let container_name = &container.ident; let attr_impls = gen_attr_impls(&facet_crate, container_name, &members); let buildable_impl = gen_buildable_impl(&facet_crate, &container_name, &members); let async_buildable_impl = gen_async_buildable_impl(&facet_crate, &container_name, &members); Ok(quote! { #container #( #attr_impls )* #buildable_impl #async_buildable_impl }) } fn gen_buildable_impl( facet_crate: &Ident, container_name: &Ident, members: &ContainerMembers, ) -> TokenStream { let facet_idents = &members.facet_idents; let facet_types = &members.facet_types; let field_idents = &members.field_idents; let field_inits = &members.field_inits; let delegate_idents = &members.delegate_idents; let delegate_types = &members.delegate_types; quote! { impl<B> ::#facet_crate::Buildable<B> for #container_name where B: ::std::marker::Send + ::std::marker::Sync #( + ::#facet_crate::Builder<::std::sync::Arc<#facet_types>> )*, #( #delegate_types: ::#facet_crate::Buildable<B>, )* { fn build(builder: &mut B) -> ::std::result::Result<Self, ::#facet_crate::FactoryError> { // Build each delegate. #( let #delegate_idents = <#delegate_types as ::#facet_crate::Buildable<B>>::build(builder)?; )* // Build each facet. #( let #facet_idents = <B as ::#facet_crate::Builder< ::std::sync::Arc<#facet_types> >>::build(builder)?; )* // Initialize the other fields. #( let #field_idents = #field_inits; )* Ok(Self { #( #delegate_idents, )* #( #field_idents, )* #( #facet_idents, )* }) } } } } fn gen_async_buildable_impl( facet_crate: &Ident, container_name: &Ident, members: &ContainerMembers, ) -> TokenStream { let facet_idents = &members.facet_idents; let facet_types = &members.facet_types; let field_idents = &members.field_idents; let field_inits = &members.field_inits; let delegate_idents = &members.delegate_idents; let delegate_types = &members.delegate_types; // Desugared async-trait so that the builder lifetime can be specified. quote! { impl<'builder, B> ::#facet_crate::AsyncBuildable<'builder, B> for #container_name where B: ::std::marker::Send + ::std::marker::Sync + ::#facet_crate::AsyncBuilder #( + ::#facet_crate::AsyncBuilderFor<::std::sync::Arc<#facet_types>> )* + 'builder, #( #delegate_types: ::#facet_crate::AsyncBuildable<'builder, B>, )* { fn build_async(mut builder: B) -> ::std::pin::Pin<::std::boxed::Box< dyn std::future::Future< Output = ::std::result::Result<Self, ::#facet_crate::FactoryError> > + ::std::marker::Send + 'builder >> { let build = async move { // Mark needed facets as needed. Self::mark_needed(&mut builder); // Build the needed facets. <B as ::#facet_crate::AsyncBuilder>::build_needed(&mut builder).await?; // Build ourself. Ok(Self::construct(&builder)) }; ::std::boxed::Box::pin(build) } fn mark_needed(builder: &mut B) { // Mark facets we need as as needed. #( <B as ::#facet_crate::AsyncBuilderFor< ::std::sync::Arc<#facet_types> >>::need(builder); )* // Mark facets our delegates need as needed. #( <#delegate_types as ::#facet_crate::AsyncBuildable<'builder, B>> ::mark_needed(builder); )* } fn construct(builder: &B) -> Self { // Build delegates. #( let #delegate_idents = <#delegate_types as ::#facet_crate::AsyncBuildable<'builder, B>> ::construct(builder); )* // Get the facets out of the builder. #( let #facet_idents = <B as ::#facet_crate::AsyncBuilderFor< ::std::sync::Arc<#facet_types> >>::get(builder); )* // Initialize other fields. #( let #field_idents = #field_inits; )* Self { #( #delegate_idents, )* #( #field_idents, )* #( #facet_idents, )* } } } } } fn gen_attr_impls( facet_crate: &Ident, container_name: &Ident, members: &ContainerMembers, ) -> Vec<TokenStream> { let mut output = Vec::new(); let facet_idents = &members.facet_idents; let facet_types = &members.facet_types; let delegate_idents = &members.delegate_idents; let delegate_facets = &members.delegate_facets; for (facet_ident, facet_type) in facet_idents.iter().zip(facet_types) { output.push(quote! { impl ::#facet_crate::FacetRef<#facet_type> for #container_name { #[inline] fn facet_ref(&self) -> &(#facet_type) { self.#facet_ident.as_ref() } } impl ::#facet_crate::FacetRef<#facet_type> for &#container_name { #[inline] fn facet_ref(&self) -> &(#facet_type) { (*self).#facet_ident.as_ref() } } impl ::#facet_crate::FacetArc<#facet_type> for #container_name { #[inline] fn facet_arc(&self) -> ::std::sync::Arc<#facet_type> { self.#facet_ident.clone() } } impl ::#facet_crate::FacetArc<#facet_type> for &#container_name { #[inline] fn facet_arc(&self) -> ::std::sync::Arc<#facet_type> { (*self).#facet_ident.clone() } } }); } for (delegate_ident, delegate_facet) in delegate_idents.iter().zip(delegate_facets) { output.push(quote! { #( impl ::#facet_crate::FacetRef<#delegate_facet> for #container_name { #[inline] fn facet_ref(&self) -> &(#delegate_facet) { self.#delegate_ident.facet_ref() } } impl ::#facet_crate::FacetRef<#delegate_facet> for &#container_name { #[inline] fn facet_ref(&self) -> &(#delegate_facet) { self.#delegate_ident.facet_ref() } } impl ::#facet_crate::FacetArc<#delegate_facet> for #container_name { #[inline] fn facet_arc(&self) -> ::std::sync::Arc<#delegate_facet> { self.#delegate_ident.facet_arc() } } impl ::#facet_crate::FacetArc<#delegate_facet> for &#container_name { #[inline] fn facet_arc(&self) -> ::std::sync::Arc<#delegate_facet> { self.#delegate_ident.facet_arc() } } )* }); } output } fn extract_delegate_facets(attr: &Attribute) -> Result<Vec<Type>, Error> { let mut facets = Vec::new(); let args: Punctuated<Type, Token![,]> = attr.parse_args_with(Punctuated::parse_terminated)?; for mut arg in args { if let Type::TraitObject(obj) = &mut arg { obj.bounds.push(syn::parse2(quote!(::std::marker::Send))?); obj.bounds.push(syn::parse2(quote!(::std::marker::Sync))?); obj.bounds.push(syn::parse2(quote!('static))?); } facets.push(arg); } Ok(facets) }