in shed/facet/proc_macros/factory_impl.rs [216:448]
fn gen_async_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 builder_facets_needed_ident = format_ident!("{}BuilderFacetsNeeded", factory_ty);
let builder_params_ident = format_ident!("{}BuilderParams", factory_ty);
let param_idents = ¶ms.param_idents;
let param_types = ¶ms.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 heads: BTreeSet<_> = facet_idents.iter().collect();
let mut facet_build_futs = BTreeMap::new();
let mut facet_build_graph = BTreeMap::new();
let mut builder_impls = Vec::new();
let mut build_facets = Vec::new();
let mut store_facets = Vec::new();
for (facet_ident, facet_type, fallibility, asyncness, facet_params) in facets.iter() {
let mut dependent_facets = Vec::new();
let mut mark_facets_needed = Vec::new();
let mut call_params = Vec::new();
let mut deps = 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"))?;
mark_facets_needed.push(quote! {
::#facet_crate::AsyncBuilderFor::<#param_type>::need(self);
});
dependent_facets.push(ident);
call_params.push(quote!(#ident.as_ref().unwrap()));
heads.remove(&ident);
deps.push(ident);
}
FactoryParam::Param(ident) => {
call_params.push(quote!(&__self_params.#ident));
}
}
}
let maybe_dot_await_factory = asyncness.maybe(quote!(.await));
let maybe_map_err = fallibility.maybe(quote! {
.map_err(|e| ::#facet_crate::AsyncFactoryError::from(
::#facet_crate::FactoryError::FacetBuildFailed {
name: stringify!(#facet_ident),
source: e.into(),
}))?
});
facet_build_graph.insert(facet_ident, deps);
builder_impls.push(quote! {
impl ::#facet_crate::AsyncBuilderFor<#facet_type> for #builder_ident<'_> {
fn need(&mut self) {
self.needed.#facet_ident = true;
#( #mark_facets_needed )*
}
fn get(&self) -> #facet_type {
// The proc macro should have arranged for all needed
// facets to have been marked as needed and thus built. It
// is invalid for this to be called if the facet wasn't
// built.
self.facets.#facet_ident.clone().expect(
concat!(
"bug in #[facet::factory]: facet '",
stringify!(#facet_ident),
"' was not marked as needed",
)
)
}
}
});
let get_dependent_facets = if dependent_facets.is_empty() {
quote!()
} else {
quote! {
let ( #( #dependent_facets, )* ) =
::#facet_crate::futures::try_join!(
#( #dependent_facets.clone(), )*
)?;
}
};
facet_build_futs.insert(
facet_ident,
quote! {
let #facet_ident = async {
if __self_needed.#facet_ident {
#get_dependent_facets
Ok::<_, ::#facet_crate::AsyncFactoryError>(Some(
__self_factory.#facet_ident( #( #call_params, )* )
#maybe_dot_await_factory
#maybe_map_err
))
} else {
Ok::<_, ::#facet_crate::AsyncFactoryError>(None)
}
}.shared();
},
);
store_facets.push(quote! {
__self_facets.#facet_ident = #facet_ident;
});
}
// Group facets into based on their depth from the heads of the dependency
// graph. This will be used to order construction of the facets in
// topological order.
let mut ident_depths = BTreeMap::new();
let mut queue: VecDeque<_> = heads.into_iter().map(|head| (head, 0)).collect();
let mut max_depth = 0;
while let Some((ident, depth)) = queue.pop_front() {
ident_depths.insert(ident, depth);
max_depth = depth;
for dep in facet_build_graph.get(&ident).unwrap().iter() {
queue.push_back((dep, depth + 1));
}
}
let mut levels = vec![vec![]; max_depth + 1];
for (ident, depth) in ident_depths.into_iter() {
levels[depth].push(ident);
}
for idents in levels.into_iter().rev() {
for ident in idents.iter() {
build_facets.push(facet_build_futs.remove(ident).unwrap());
}
}
let builder = quote! {
#[doc(hidden)]
pub struct #builder_params_ident {
#(
#param_idents: #param_types,
)*
}
#[doc(hidden)]
#[derive(Default)]
pub struct #builder_facets_ident {
#(
#facet_idents: ::std::option::Option<#facet_types>,
)*
}
#[doc(hidden)]
#[derive(Default)]
pub struct #builder_facets_needed_ident {
#(
#facet_idents: bool,
)*
}
impl #builder_params_ident {
#[doc(hidden)]
pub fn new( #( #param_idents: #param_types, )* ) -> Self {
Self {
#( #param_idents, )*
}
}
}
#[::#facet_crate::async_trait::async_trait]
impl ::#facet_crate::AsyncBuilder for #builder_ident<'_> {
async fn build_needed(
&mut self
) -> ::std::result::Result<(), ::#facet_crate::FactoryError> {
use ::#facet_crate::futures::future::FutureExt;
let __self_facets = &mut self.facets;
let __self_needed = &self.needed;
let __self_params = &self.params;
let __self_factory = self.factory;
#( #build_facets )*
let ( #( #facet_idents, )* ) =
::#facet_crate::futures::try_join!( #( #facet_idents.clone(), )* )
.map_err(|e| e.factory_error())?;
#( #store_facets )*
Ok(())
}
}
#(
#builder_impls
)*
#[doc(hidden)]
pub struct #builder_ident<'factory> {
factory: &'factory #factory_ty,
params: #builder_params_ident,
facets: #builder_facets_ident,
needed: #builder_facets_needed_ident,
}
impl #factory_ty {
/// Build an instance of a container from this factory.
pub async fn build<'factory, 'builder, T>(
&'factory self,
#( #param_idents: #param_types ),*
) -> ::std::result::Result<T, ::#facet_crate::FactoryError>
where
T: ::#facet_crate::AsyncBuildable<'builder, #builder_ident<'factory>>,
{
let builder = #builder_ident {
factory: &self,
params: #builder_params_ident::new(#( #param_idents, )*),
facets: #builder_facets_ident::default(),
needed: #builder_facets_needed_ident::default(),
};
T::build_async(builder).await
}
}
};
Ok(builder)
}