fn gen_sync_factory_builder()

in shed/facet/proc_macros/factory_impl.rs [87:214]


fn gen_sync_factory_builder(
    facet_crate: &Ident,
    factory_ty: &Ident,
    builder_ident: &Ident,
    params: &Params,
    facets: &Facets,
) -> Result<TokenStream, Error> {
    let builder_facets_ident = format_ident!("{}BuilderFacets", factory_ty);
    let param_idents = &params.param_idents;
    let param_types = &params.param_types;
    let facet_idents = &facets.facet_idents;
    let facet_types = &facets.facet_types;
    let facet_types_map = facet_idents
        .iter()
        .zip(facet_types)
        .collect::<BTreeMap<_, _>>();

    let mut builder_impls = Vec::new();

    for (facet_ident, facet_type, fallibility, asyncness, facet_params) in facets.iter() {
        let mut call_params = Vec::new();
        let mut make_facets = Vec::new();

        for facet_param in facet_params {
            match facet_param {
                FactoryParam::Facet(ident) => {
                    let param_type = facet_types_map
                        .get(ident)
                        .ok_or_else(|| Error::new(ident.span(), "unrecognised facet name"))?;
                    make_facets.push(quote! {
                        let #ident: #param_type = self.build()?;
                    });
                    call_params.push(quote!(&#ident));
                }
                FactoryParam::Param(ident) => {
                    call_params.push(quote!(&self.facets.#ident));
                }
            }
        }

        if asyncness == Asyncness::Asynchronous {
            panic!("should not generate sync builder for async factory");
        }
        let maybe_map_err = fallibility.maybe(quote! {
            .map_err(|e| ::#facet_crate::FactoryError::FacetBuildFailed {
                name: stringify!(#facet_ident),
                source: e.into(),
            })?
        });

        builder_impls.push(quote! {

            impl ::#facet_crate::Builder<#facet_type> for #builder_ident<'_> {

                fn build<'builder>(&'builder mut self) -> ::std::result::Result<
                    #facet_type,
                    ::#facet_crate::FactoryError,
                >  {
                    if let Some(facet) = self.facets.#facet_ident.as_ref() {
                        return Ok(facet.clone());
                    }
                    use ::#facet_crate::Builder as _;
                    #( #make_facets )*
                    let #facet_ident =
                        self.factory.#facet_ident( #( #call_params ),* )
                            #maybe_map_err;
                    debug_assert!(self.facets.#facet_ident.is_none());
                    self.facets.#facet_ident = Some(#facet_ident.clone());
                    Ok(#facet_ident)
                }
            }

        })
    }

    let builder = quote! {
        #[doc(hidden)]
        pub struct #builder_facets_ident {
            #(
                #param_idents: #param_types,
            )*
            #(
                #facet_idents: ::std::option::Option<#facet_types>,
            )*
        }

        impl #builder_facets_ident {
            #[doc(hidden)]
            pub fn new( #( #param_idents: #param_types, )* ) -> Self {
                Self {
                    #( #param_idents, )*
                    #(
                        #facet_idents: ::std::default::Default::default(),
                    )*
                }
            }
        }

        #(
            #builder_impls
        )*

        #[doc(hidden)]
        pub struct #builder_ident<'factory> {
            factory: &'factory #factory_ty,
            facets: #builder_facets_ident,
        }

        impl #factory_ty {
            /// Build an instance of a container from this factory.
            pub fn build<'factory, T>(
                &'factory self,
                #( #param_idents: #param_types ),*
            ) -> ::std::result::Result<T, ::#facet_crate::FactoryError>
            where
                T: ::#facet_crate::Buildable<#builder_ident<'factory>>,
            {
                let mut builder = #builder_ident {
                    factory: &self,
                    facets: #builder_facets_ident::new(#( #param_idents, )*),
                };
                T::build(&mut builder)
            }
        }
    };

    Ok(builder)
}