ctrlgen_impl/
generate.rs

1use proc_macro2::TokenStream;
2use quote::{quote as q, quote_spanned};
3use syn::{parse_quote, punctuated::Punctuated, Token, WhereClause};
4
5#[cfg(feature = "std")]
6fn borrow_toowned() -> TokenStream {
7    q! {::std::borrow::ToOwned}
8}
9#[cfg(all(feature = "alloc", not(feature = "std")))]
10fn borrow_toowned() -> TokenStream {
11    q! {::alloc::borrow::ToOwned}
12}
13#[cfg(all(not(feature = "alloc"), not(feature = "std")))]
14fn borrow_toowned() -> TokenStream {
15    panic!("Cannot use borrow::ToOwned without either `std` or `alloc` features of ctrlgen")
16}
17
18use crate::Proxy;
19
20use super::InputData;
21impl InputData {
22    pub fn make_where_clause(&self) -> WhereClause {
23        let mut where_clause = self
24            .generics
25            .where_clause
26            .clone()
27            .unwrap_or_else(|| WhereClause {
28                where_token: <Token![where]>::default(),
29                predicates: Punctuated::new(),
30            });
31        if let Some(returnval_trait) = self.params.returnval.as_ref() {
32            where_clause.predicates.push(parse_quote! {
33                #returnval_trait : ::ctrlgen::Returnval
34            })
35        }
36        where_clause
37    }
38
39    pub fn generate_enum(&self) -> TokenStream {
40        let returnval_handler = self.params.returnval.as_ref();
41        let custom_attrs = &self.params.enum_attr[..];
42        let visibility = &self.params.visibility;
43        let enum_name = &self.params.enum_name;
44        let mut variants = TokenStream::new();
45        for method in &self.methods {
46            let variant_name = method.variant_name();
47            let mut variant_params = TokenStream::new();
48            let doc_attr = &method.doc_attr;
49            for arg in &method.args {
50                let argument_name = &arg.name;
51                let argument_type = if !arg.to_owned {
52                    let ty = &arg.ty;
53                    q! {#ty}
54                } else {
55                    match &arg.ty {
56                        syn::Type::Reference(r) => {
57                            let ty = &*r.elem;
58                            let toowned = borrow_toowned();
59                            q! {<#ty as #toowned>::Owned}
60                        }
61                        _ => panic!(
62                            "Argument marked with `#[ctrlgen_to_owned]` must be a &reference"
63                        ),
64                    }
65                };
66                let mut custom_attributes = TokenStream::new();
67                for aa in &arg.enum_attr {
68                    custom_attributes.extend(q! {# #aa});
69                }
70                variant_params.extend(q! {
71                    #custom_attributes #argument_name : #argument_type,
72                });
73            }
74            if let Some(return_type) = &method.ret {
75                let mut custom_attributes = TokenStream::new();
76                for aa in &method.return_attr {
77                    custom_attributes.extend(q! {# #aa});
78                }
79                if let Some(returnval_trait) = returnval_handler {
80                    variant_params.extend(q! {
81                        #custom_attributes ret : <#returnval_trait as ::ctrlgen::Returnval>::Sender<#return_type>,
82                    });
83                }
84            } else {
85                if !method.return_attr.is_empty() {
86                    panic!("`ctrlgen_return_attr[]` used in method without a return type. Add `-> ()` to force using the return channel.");
87                }
88            }
89            let custom_attributes = &method.enum_attr;
90
91            variants.extend(q! {
92                #(#doc_attr)*
93                #(#custom_attributes)*
94                #variant_name { #variant_params },
95            });
96        }
97        let maybe_where = if let Some(returnval_trait) = returnval_handler {
98            q! {
99                where #returnval_trait : ::ctrlgen::Returnval
100            }
101        } else {
102            Default::default()
103        };
104        q! {
105            #(#custom_attrs)*
106            #visibility enum #enum_name
107            #maybe_where
108            {
109                #variants
110            }
111        }
112    }
113
114    pub fn generate_call_impl(&self) -> TokenStream {
115        let returnval_handler = self.params.returnval.as_ref();
116        let struct_name = &self.name;
117        let enum_name = &self.params.enum_name;
118        let is_async = self.has_async_functions();
119
120        let error_type = if let Some(returnval_trait) = returnval_handler {
121            q! {
122                <#returnval_trait as ::ctrlgen::Returnval>::SendError
123            }
124        } else {
125            q! { ::core::convert::Infallible }
126        };
127
128        let mut cases = TokenStream::new();
129
130        let context_name = q! { __ctrlgen_context };
131        let service_name = q! { __ctrlgen_service };
132
133        for method in &self.methods {
134            let method_name = &method.name;
135            let variant_name = method.variant_name();
136            let mut args = TokenStream::new();
137            for arg in &method.args {
138                let arg_name = &arg.name;
139                args.extend(q! {
140                    #arg_name,
141                })
142            }
143            let call_args = if self.params.context.is_some() {
144                q! { #context_name, #args }
145            } else {
146                args.clone()
147            };
148
149            let func_call = if method.r#async {
150                q! { #service_name.#method_name(#call_args).await }
151            } else {
152                q! { #service_name.#method_name(#call_args) }
153            };
154
155            let mut body = TokenStream::new();
156            if let (Some(_), Some(returnval_trait)) = (&method.ret, returnval_handler) {
157                args.extend(q! { ret, });
158                body.extend(q! {
159                    <#returnval_trait as ::ctrlgen::Returnval>::send(ret, #func_call)
160                });
161            } else {
162                body.extend(q! {
163                    #func_call;
164                    Ok(())
165                });
166            }
167
168            cases.extend(q! {
169                Self::#variant_name { #args } => {
170                    #body
171                }
172            })
173        }
174
175        let (impl_generics, _, _) = &self.generics.split_for_impl();
176        let struct_args = &self.struct_args;
177        let where_clause = self.make_where_clause();
178
179        let service_type = q! { #struct_name #struct_args };
180        let context_type = if let Some((_, ctx_type)) = &self.params.context {
181            q! { #ctx_type }
182        } else {
183            q! { () }
184        };
185
186        if !is_async {
187            q! {
188                impl #impl_generics ::ctrlgen::CallMut < #service_type > for #enum_name
189                #where_clause
190                {
191                    type Error = #error_type;
192                    type Context = #context_type;
193                    fn call_mut_with_ctx(self, #service_name: &mut #service_type, #context_name: Self::Context) -> ::core::result::Result<(), Self::Error> {
194                        match self {
195                            #cases
196                        }
197                    }
198                }
199            }
200        } else {
201            q! {
202                impl #impl_generics ::ctrlgen::CallMutAsync < #service_type > for #enum_name
203                #where_clause
204                {
205                    type Error = #error_type;
206                    type Context = #context_type;
207                    type Future<'__ctrlgen__lifetime> = impl core::future::Future<Output = ::core::result::Result<(), Self::Error>> + '__ctrlgen__lifetime
208                        where #service_type: '__ctrlgen__lifetime;
209                    fn call_mut_async_with_ctx<'__ctrlgen__lifetime>(self, #service_name: &'__ctrlgen__lifetime mut #service_type, #context_name: Self::Context) -> Self::Future<'__ctrlgen__lifetime> {
210                        async move {
211                            match self {
212                                #cases
213                            }
214                        }
215                    }
216                }
217            }
218        }
219    }
220
221    pub fn generate_proxies(&self) -> TokenStream {
222        let mut res = TokenStream::new();
223        for proxy in self.params.proxies.iter() {
224            res.extend(self.generate_proxy(proxy));
225        }
226        res
227    }
228
229    pub fn generate_proxy(&self, proxy: &Proxy) -> TokenStream {
230        match proxy {
231            crate::Proxy::Trait(kwd, x) => self.generate_proxy_trait(kwd, x),
232        }
233    }
234
235    pub fn generate_proxy_trait(&self, kwd: &Token![trait], trait_: &syn::Ident) -> TokenStream {
236        let returnval_handler = self.params.returnval.as_ref();
237        let proxy_name = trait_;
238        let enum_name = &self.params.enum_name;
239        let visibility = &self.params.visibility;
240
241        let mut methods = TokenStream::new();
242
243        for method in &self.methods {
244            let method_name = &method.name;
245            let variant_name = method.variant_name();
246            let mut args = TokenStream::new();
247            let mut arg_names = TokenStream::new();
248            let doc_attr = &method.doc_attr;
249            for arg in &method.args {
250                let arg_name = &arg.name;
251                let arg_type = &arg.ty;
252                args.extend(q! {
253                    #arg_name: #arg_type,
254                });
255                arg_names.extend(q! {
256                    #arg_name,
257                });
258            }
259            let span = method.name.span();
260            if let (Some(ret), Some(returnval_trait)) = (&method.ret, returnval_handler) {
261                methods.extend(quote_spanned! { span=>
262                    #(#doc_attr)*
263                    fn #method_name(&self, #args) -> <#returnval_trait as ::ctrlgen::Returnval>::RecvResult<#ret> {
264                        let ret = <#returnval_trait as ::ctrlgen::Returnval>::create();
265                        let msg = #enum_name::#variant_name { #arg_names ret: ret.0 };
266                        <Self as ::ctrlgen::Proxy<#enum_name>>::send(self, msg);
267                        <#returnval_trait as ::ctrlgen::Returnval>::recv(ret.1)
268                    }
269                })
270            } else {
271                methods.extend(quote_spanned! { span=>
272                    #(#doc_attr)*
273                    fn #method_name(&self, #args) {
274                        let msg = #enum_name::#variant_name { #arg_names };
275                        <Self as ::ctrlgen::Proxy<#enum_name>>::send(self, msg);
276                    }
277                })
278            }
279        }
280
281        q! {
282            #visibility #kwd #proxy_name: ::ctrlgen::Proxy<#enum_name> {
283                #methods
284            }
285
286            impl< T : ::ctrlgen::Proxy<#enum_name>> #proxy_name for T {}
287        }
288    }
289}