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}