bon_macros/builder/builder_gen/builder_derives/
into_future.rs1use 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 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 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 }
214
215 fn visit_bound_lifetimes_mut(&mut self, _: &mut syn::BoundLifetimes) {
216 }
219}