bon_macros/builder/builder_gen/builder_derives/
into_future.rs

1use crate::builder::builder_gen::models::BuilderGenCtx;
2use crate::builder::builder_gen::top_level_config::IntoFutureConfig;
3use crate::util::prelude::*;
4use std::borrow::Cow;
5use std::collections::BTreeSet;
6use syn::visit_mut::VisitMut;
7
8impl BuilderGenCtx {
9    pub(super) fn derive_into_future(&self, config: &IntoFutureConfig) -> Result<TokenStream> {
10        if self.finish_fn.asyncness.is_none() {
11            // While it is technically possible to call a synchronous function
12            // inside of the `IntoFuture::into_future()`, it's better force the
13            // user to mark the function as `async` explicitly. Otherwise it may
14            // indicate of some logic bug where the developer mistakenly marks
15            // a function that could be sync with `derive(IntoFuture)`.
16            bail!(
17                &self.finish_fn.ident,
18                "`#[builder(derive(IntoFuture(...)))` can only be used with async functions; \
19                using it with a synchronous function is likely a mistake"
20            );
21        }
22
23        if let Some(unsafety) = &self.finish_fn.unsafety {
24            bail!(
25                unsafety,
26                "`#[builder(derive(IntoFuture(...)))` is not supported for unsafe functions \
27                because `IntoFuture::into_future()` method is a safe method"
28            );
29        }
30
31        if let Some(arg) = self.finish_fn_args().next() {
32            bail!(
33                &arg.config.finish_fn.span(),
34                "`#[builder(derive(IntoFuture(...)))` is incompatible with `#[builder(finish_fn)]` members \
35                because `IntoFuture::into_future()` method accepts zero parameters"
36            );
37        }
38
39        let state_mod = &self.state_mod.ident;
40        let builder_ident = &self.builder_type.ident;
41        let state_var = &self.state_var;
42        let finish_fn_ident = &self.finish_fn.ident;
43        let box_ = &config.box_ident;
44
45        let SignatureForIntoFuture {
46            generics_decl,
47            generic_args,
48            where_clause,
49            builder_lifetime,
50            output_ty,
51        } = self.signature_for_into_future();
52
53        let state_lifetime = builder_lifetime
54            .clone()
55            .unwrap_or_else(|| syn::Lifetime::new("'static", Span::call_site()));
56
57        let builder_lifetime = Option::into_iter(builder_lifetime);
58
59        let send_bound = if config.is_send {
60            quote! { + ::core::marker::Send }
61        } else {
62            quote! {}
63        };
64
65        let bon = &self.bon;
66
67        let alloc = if cfg!(feature = "std") {
68            quote!(::std)
69        } else if cfg!(feature = "alloc") {
70            quote!(#bon::__::alloc)
71        } else {
72            bail!(
73                &config.box_ident,
74                "`#[builder(derive(IntoFuture(Box)))]` requires either `std` or \
75                `alloc` feature to be enabled"
76            )
77        };
78
79        let tokens = quote! {
80            #[automatically_derived]
81            impl<
82                #(#generics_decl,)*
83                #state_var: #state_mod::IsComplete + #state_lifetime
84            >
85            ::core::future::IntoFuture for #builder_ident<#(#generic_args,)* #state_var>
86            #where_clause
87            {
88                type Output = #output_ty;
89                type IntoFuture = ::core::pin::Pin<
90                    #alloc::boxed::#box_<
91                        dyn ::core::future::Future<Output = Self::Output>
92                        #send_bound
93                        #(+ #builder_lifetime)*
94                    >
95                >;
96
97                fn into_future(self) -> Self::IntoFuture {
98                    #alloc::boxed::#box_::pin(#builder_ident::#finish_fn_ident(self))
99                }
100            }
101        };
102
103        Ok(tokens)
104    }
105
106    /// Handle the special case for a builder that captures lifetimes.
107    ///
108    /// Collapse all lifetimes into a single `'builder` lifetime. This is
109    /// because `dyn Trait` supports only a single `+ 'lifetime` bound.
110    fn signature_for_into_future(&self) -> SignatureForIntoFuture<'_> {
111        let generics_decl = &self.generics.decl_without_defaults;
112        let generic_args = &self.generics.args;
113        let where_clause = &self.generics.where_clause;
114
115        let output_ty = match &self.finish_fn.output {
116            syn::ReturnType::Default => Cow::Owned(syn::parse_quote!(())),
117            syn::ReturnType::Type(_, output_ty) => Cow::Borrowed(output_ty.as_ref()),
118        };
119
120        let contains_lifetimes = matches!(
121            self.generics.args.first(),
122            Some(syn::GenericArgument::Lifetime(_))
123        );
124
125        if !contains_lifetimes {
126            return SignatureForIntoFuture {
127                generics_decl: Cow::Borrowed(generics_decl),
128                generic_args: Cow::Borrowed(generic_args),
129                where_clause: where_clause.as_ref().map(Cow::Borrowed),
130                builder_lifetime: None,
131                output_ty,
132            };
133        }
134
135        let builder_lifetime = syn::Lifetime::new("'builder", Span::call_site());
136
137        let new_generic_args = generic_args
138            .iter()
139            .map(|arg| match arg {
140                syn::GenericArgument::Lifetime(_) => {
141                    syn::GenericArgument::Lifetime(builder_lifetime.clone())
142                }
143                _ => arg.clone(),
144            })
145            .collect::<Vec<_>>();
146
147        let mut original_lifetimes = BTreeSet::new();
148        let mut new_generics_decl = vec![syn::parse_quote!(#builder_lifetime)];
149
150        for param in generics_decl {
151            match param {
152                syn::GenericParam::Lifetime(lifetime) => {
153                    original_lifetimes.insert(&lifetime.lifetime.ident);
154                }
155                _ => {
156                    new_generics_decl.push(param.clone());
157                }
158            }
159        }
160
161        let mut replace_lifetimes = ReplaceLifetimes {
162            replacement: &builder_lifetime,
163            original_lifetimes: &original_lifetimes,
164        };
165
166        for decl in &mut new_generics_decl {
167            replace_lifetimes.visit_generic_param_mut(decl);
168        }
169
170        let mut new_where_clause = where_clause.clone();
171
172        if let Some(where_clause) = &mut new_where_clause {
173            replace_lifetimes.visit_where_clause_mut(where_clause);
174        }
175
176        let mut output_ty = output_ty.into_owned();
177
178        replace_lifetimes.visit_type_mut(&mut output_ty);
179
180        SignatureForIntoFuture {
181            generics_decl: Cow::Owned(new_generics_decl),
182            generic_args: Cow::Owned(new_generic_args),
183            where_clause: new_where_clause.map(Cow::Owned),
184            builder_lifetime: Some(builder_lifetime),
185            output_ty: Cow::Owned(output_ty),
186        }
187    }
188}
189
190struct SignatureForIntoFuture<'a> {
191    generics_decl: Cow<'a, [syn::GenericParam]>,
192    generic_args: Cow<'a, [syn::GenericArgument]>,
193    where_clause: Option<Cow<'a, syn::WhereClause>>,
194    builder_lifetime: Option<syn::Lifetime>,
195    output_ty: Cow<'a, syn::Type>,
196}
197
198struct ReplaceLifetimes<'a> {
199    replacement: &'a syn::Lifetime,
200    original_lifetimes: &'a BTreeSet<&'a syn::Ident>,
201}
202
203impl VisitMut for ReplaceLifetimes<'_> {
204    fn visit_lifetime_mut(&mut self, lifetime: &mut syn::Lifetime) {
205        if self.original_lifetimes.contains(&lifetime.ident) {
206            *lifetime = self.replacement.clone();
207        }
208    }
209
210    fn visit_item_mut(&mut self, _: &mut syn::Item) {
211        // Don't recurse into child items. They don't inherit the parent item's
212        // lifetimes.
213    }
214
215    fn visit_bound_lifetimes_mut(&mut self, _: &mut syn::BoundLifetimes) {
216        // Don't recurse into bound lifetime declarations. They introduce
217        // local lifetimes that we should keep as is
218    }
219}