1use proc_macro2::{Ident, TokenStream as TokenStream2};
2use quote::{format_ident, quote, ToTokens};
3use syn::{Attribute, BareFnArg, Generics, parse_quote, ReturnType, Type, Visibility};
4use crate::ast::{CommaPunctuated, CommaPunctuatedTokens, Depunctuated};
5use crate::composer::{CommaPunctuatedArgs, SemiPunctuatedArgs, SignatureAspect};
6use crate::ext::{Accessory, ArgsTransform, Mangle, Pop, PunctuateOne, Terminated, ToPath, ToType};
7use crate::lang::RustSpecification;
8use crate::presentation::{ArgPresentation, DictionaryName, InterfacePresentation, InterfacesMethodExpr, Name};
9
10#[derive(Clone, Debug)]
11#[allow(unused)]
12pub enum BindingPresentation {
13 Empty,
14 Constructor {
15 aspect: SignatureAspect<RustSpecification>,
16 name: TokenStream2,
17 ty: Type,
18 ctor_arguments: CommaPunctuatedArgs,
19 body_presentation: TokenStream2,
20 },
21 VariantConstructor {
22 aspect: SignatureAspect<RustSpecification>,
23 name: TokenStream2,
24 ty: Type,
25 ctor_arguments: CommaPunctuatedArgs,
26 body_presentation: TokenStream2,
27 },
28 Destructor {
29 aspect: SignatureAspect<RustSpecification>,
30 name: TokenStream2,
31 var: Type,
32 },
33 Getter {
34 aspect: SignatureAspect<RustSpecification>,
35 name: TokenStream2,
36 field_name: TokenStream2,
37 obj_var: Type,
38 field_type: Type,
39 },
40 Setter {
41 aspect: SignatureAspect<RustSpecification>,
42 name: TokenStream2,
43 field_name: TokenStream2,
44 obj_var: Type,
45 field_type: Type,
46 },
47 GetterOpaque {
48 aspect: SignatureAspect<RustSpecification>,
49 name: TokenStream2,
50 field_name: TokenStream2,
51 obj_var: Type,
52 field_type: Type,
53 },
54 SetterOpaque {
55 aspect: SignatureAspect<RustSpecification>,
56 name: TokenStream2,
57 field_name: TokenStream2,
58 obj_var: Type,
59 field_type: Type,
60 },
61 ObjAsTrait {
62 aspect: SignatureAspect<RustSpecification>,
63 name: Name<RustSpecification>,
64 item_var: Type,
65 trait_type: TokenStream2,
66 vtable_name: TokenStream2,
67 },
68 ObjAsTraitDestructor {
69 aspect: SignatureAspect<RustSpecification>,
70 name: Name<RustSpecification>,
71 item_type: TokenStream2,
72 trait_type: TokenStream2,
73 },
74 RegularFunction {
75 aspect: SignatureAspect<RustSpecification>,
76 name: TokenStream2,
77 is_async: bool,
78 arguments: CommaPunctuatedArgs,
79 input_conversions: TokenStream2,
80 return_type: ReturnType,
81 output_conversions: TokenStream2,
82 },
83 RegularFunctionWithBody {
84 aspect: SignatureAspect<RustSpecification>,
85 name: TokenStream2,
86 arguments: CommaPunctuatedArgs,
87 return_type: ReturnType,
88 body: TokenStream2,
89 },
90 RegularFunction2 {
91 aspect: SignatureAspect<RustSpecification>,
92 name: TokenStream2,
93 is_async: bool,
94 argument_names: CommaPunctuatedTokens,
95 arguments: CommaPunctuatedArgs,
96 full_fn_path: Type,
97 input_conversions: SemiPunctuatedArgs,
98 return_type: ReturnType,
99 output_conversions: TokenStream2,
100 },
101 Callback {
102 aspect: SignatureAspect<RustSpecification>,
103 name: Ident,
104 ffi_args: CommaPunctuated<BareFnArg>,
105 result: ReturnType,
106 conversion: InterfacePresentation,
107 },
108
109 TraitVTableInnerFn {
110 attrs: Vec<Attribute>,
111 name: TokenStream2,
112 name_and_args: TokenStream2,
113 output_expression: ReturnType,
114 },
115 StaticVTableInnerFnDeclaration {
116 name: TokenStream2,
117 fn_name: Ident
118 },
119 StaticVTableInnerFn {
120 aspect: SignatureAspect<RustSpecification>,
121 name: TokenStream2,
122 args: CommaPunctuatedArgs,
123 output: ReturnType,
124 body: TokenStream2,
125 },
126 StaticVTable {
127 attrs: Vec<Attribute>,
128 name: TokenStream2,
129 methods_declarations: CommaPunctuated<BindingPresentation>,
130 methods_implementations: Depunctuated<BindingPresentation>,
131 bindings: Depunctuated<BindingPresentation>,
132 fq_trait_vtable: TokenStream2,
133 },
134
135 Any {
136 attrs: Vec<Attribute>,
137 body: TokenStream2
138 }
139}
140
141impl BindingPresentation {
142 fn regular_fn_with_body<T: ToTokens>(aspect: &SignatureAspect<RustSpecification>, name: Name<RustSpecification>, arguments: CommaPunctuatedArgs, return_type: ReturnType, body: T) -> Self {
143 Self::RegularFunctionWithBody {
144 aspect: aspect.clone(),
145 name: name.mangle_tokens_default(),
146 arguments,
147 return_type,
148 body: body.to_token_stream(),
149 }
150 }
151 pub fn regular_non_void_fn_with_body<T: ToTokens>(aspect: &SignatureAspect<RustSpecification>, name: Name<RustSpecification>, arguments: CommaPunctuatedArgs, return_type: Type, body: T) -> Self {
152 Self::regular_fn_with_body(aspect, name, arguments, ReturnType::Type(Default::default(), return_type.into()), body)
153 }
154 pub fn regular_void_fn_with_body<T: ToTokens>(aspect: &SignatureAspect<RustSpecification>, name: Name<RustSpecification>, arguments: CommaPunctuatedArgs, body: T) -> Self {
155 Self::regular_fn_with_body(aspect, name, arguments, ReturnType::Default, body)
156 }
157 pub fn ctor_with_body<T: ToTokens + 'static>(aspect: &SignatureAspect<RustSpecification>, ty: Type, arguments: CommaPunctuatedArgs, return_type: Type, body_to_be_boxed: T) -> Self {
158 Self::regular_fn_with_body(aspect, Name::<RustSpecification>::Constructor(ty), arguments, ReturnType::Type(Default::default(), Box::new(return_type)), InterfacesMethodExpr::Boxed(body_to_be_boxed))
159 }
160}
161
162pub fn present_pub_function<T: ToTokens, U: ToTokens>(
163 aspect: &SignatureAspect<RustSpecification>,
164 name: U,
165 args: CommaPunctuated<T>,
166 output: ReturnType,
167 body: TokenStream2
168) -> TokenStream2 {
169 present_function(Visibility::Public(Default::default()), aspect, name, args, output, body)
170}
171pub fn present_function<T: ToTokens, N: ToTokens>(
172 acc: Visibility,
173 (attrs, lifetimes, generics): &SignatureAspect<RustSpecification>,
174 name: N,
175 args: CommaPunctuated<T>,
176 output: ReturnType,
177 body: TokenStream2) -> TokenStream2 {
178 let signature = match generics {
179 None => {
180 let comma_lifetimes = CommaPunctuated::from_iter(lifetimes.iter().filter(|lt| lt.ident.ne("static")).map(ToTokens::to_token_stream));
181 if comma_lifetimes.is_empty() {
182 quote!(#name(#args) #output)
183 } else {
184 quote!(#name<#comma_lifetimes>(#args) #output)
185 }
186 },
187 Some(Generics { params, where_clause, .. }) => {
188 if params.is_empty() {
189 quote!(#name(#args) #output #where_clause)
190 } else {
191 quote!(#name<#params>(#args) #output #where_clause)
192 }
193 }
194 };
195 let sig = present_signature(acc, signature);
196 quote! {
197 #(#attrs)*
198 #[no_mangle]
199 #sig { #body }
200 }
201}
202
203pub fn present_signature<A: ToTokens, S: ToTokens>(acc: A, signature: S) -> TokenStream2 {
204 quote!(#acc unsafe extern "C" fn #signature)
205}
206
207pub fn present_struct<Name: ToTokens, Impl: ToTokens>(
208 name: Name,
209 attrs: &Vec<Attribute>,
210 implementation: Impl
211) -> TokenStream2 {
212 quote! {
213 #[repr(C)]
214 #[derive(Clone)]
215 #(#attrs)*
216 pub struct #name #implementation
217 }
218}
219
220
221impl ToTokens for BindingPresentation {
222 fn to_tokens(&self, tokens: &mut TokenStream2) {
223 match self {
224 Self::Empty =>
225 quote!(),
226 Self::Constructor { aspect, name, ty, ctor_arguments, body_presentation} => {
227 let ffi_path = ty.to_path().arg_less();
228 present_pub_function(
229 aspect,
230 name,
231 ctor_arguments.clone(),
232 ReturnType::Type(Default::default(), ty.joined_mut().into()),
233 InterfacesMethodExpr::Boxed(quote!(#ffi_path #body_presentation)).to_token_stream())
234 },
235 Self::VariantConstructor { aspect, name, ty, ctor_arguments, body_presentation} => {
236 let variant_path = ty.to_path();
237 present_pub_function(
238 aspect,
239 name,
240 ctor_arguments.clone(),
241 ReturnType::Type(Default::default(), variant_path.popped().to_type().joined_mut().into()),
242 InterfacesMethodExpr::Boxed(quote!(#variant_path #body_presentation)).to_token_stream())
243 },
244 Self::Destructor { aspect, name, var } =>
245 present_pub_function(
246 aspect,
247 name,
248 quote!(ffi: #var).punctuate_one(),
249 ReturnType::Default,
250 InterfacesMethodExpr::UnboxAny(DictionaryName::Ffi).to_token_stream().terminated()
251 ),
252 Self::ObjAsTrait { aspect, name, item_var, trait_type, vtable_name } =>
253 present_pub_function(
254 aspect,
255 name,
256 quote!(obj: #item_var).punctuate_one(),
257 ReturnType::Type(Default::default(), trait_type.to_type().into()),
258 quote!(#trait_type {
259 object: obj as *const (),
260 vtable: &#vtable_name
261 })
262 ),
263 Self::ObjAsTraitDestructor { aspect, name, item_type, trait_type } => {
264 let attrs = &aspect.0;
265 present_pub_function(
266 aspect,
267 name,
268 quote!(#(#attrs)* obj: #trait_type).punctuate_one(),
269 ReturnType::Default,
270 InterfacesMethodExpr::UnboxAny(quote!(obj.object as *mut #item_type)).to_token_stream().terminated()
271 )
272 },
273 Self::Getter { name, field_name, obj_var, field_type, aspect } |
274 Self::GetterOpaque { name, field_name, obj_var, field_type, aspect } =>
275 present_pub_function(
276 aspect,
277 name,
278 quote!(obj: #obj_var).punctuate_one(),
279 ReturnType::Type(Default::default(), field_type.clone().into()),
280 quote!((*obj).#field_name)
281 ),
282 Self::Setter { name, field_name, obj_var, field_type, aspect } |
283 Self::SetterOpaque { name, field_name, obj_var, field_type, aspect } =>
284 present_pub_function(
285 aspect,
286 name,
287 CommaPunctuated::from_iter([quote!(obj: #obj_var), quote!(value: #field_type)]),
288 ReturnType::Default,
289 quote!((*obj).#field_name = value;)),
290 Self::RegularFunction { aspect, is_async: true, name, arguments, input_conversions, return_type, output_conversions } => {
291 let mut args = ArgPresentation::Field(crate::ast::inherited_named_field(format_ident!("runtime"), parse_quote!(*const std::os::raw::c_void))).punctuate_one();
292 args.extend(arguments.clone());
293 present_pub_function(
294 aspect,
295 name,
296 args,
297 return_type.clone(),
298 quote! {
299 let rt = &*(runtime as *const tokio::runtime::Runtime);
300 let obj = rt.block_on(async {
301 #input_conversions .await
302 });
303 #output_conversions
304 }
305 )
306 },
307 Self::RegularFunction { aspect, is_async: false, name, arguments, input_conversions, return_type, output_conversions } =>
308 present_pub_function(
309 aspect,
310 name,
311 arguments.clone(),
312 return_type.clone(),
313 quote!(let obj = #input_conversions; #output_conversions)
314 ),
315 Self::RegularFunctionWithBody { aspect, name, arguments, return_type, body } =>
316 present_pub_function(
317 aspect,
318 name,
319 arguments.clone(),
320 return_type.clone(),
321 body.to_token_stream()
322 ),
323 Self::RegularFunction2 { aspect, is_async: true, name, argument_names, arguments, full_fn_path, input_conversions, return_type, output_conversions } => {
324 let mut args = ArgPresentation::Field(crate::ast::inherited_named_field(format_ident!("runtime"), parse_quote!(*const std::os::raw::c_void))).punctuate_one();
325 args.extend(arguments.clone());
326 present_pub_function(
327 aspect,
328 name,
329 args.clone(),
330 return_type.clone(),
331 quote! {
332 let rt = unsafe { &*(runtime as *const tokio::runtime::Runtime) };
333 #input_conversions;
334 let obj = rt.block_on(async {
335 #full_fn_path(#argument_names).await
336 });
337 #output_conversions
338 }
339 )
350 },
351 Self::RegularFunction2 { aspect, is_async: false, name, argument_names, arguments, full_fn_path, input_conversions, return_type, output_conversions } =>
352 present_pub_function(
353 aspect,
354 name,
355 arguments.clone(),
356 return_type.clone(),
357 quote! {
358 #input_conversions;
359 let obj = #full_fn_path(#argument_names);
360 #output_conversions
361 }
362 ),
363 Self::Callback { aspect: (attrs, ..), name, ffi_args, result, conversion } => {
364 let result_impl = match result {
365 ReturnType::Default => Default::default(),
366 ReturnType::Type(_, ref ty) => {
367 let dtor_signature = present_signature(TokenStream2::default(), quote!((result: #ty)));
368 quote! { #result, destructor: #dtor_signature }
369 }
370 };
371 let caller_signature = present_signature(TokenStream2::default(), quote!((#ffi_args) #result_impl));
372 let implementation = quote!({ caller: #caller_signature, });
373 let definition = present_struct(name, attrs, implementation);
374 quote! {
375 #definition
376 #conversion
377 }
378 }
379 Self::StaticVTable { attrs, name, fq_trait_vtable, methods_declarations, methods_implementations, bindings } => quote! {
380 #[no_mangle]
381 #(#attrs)*
382 pub static #name: #fq_trait_vtable = {
383 #methods_implementations
384 #fq_trait_vtable { #methods_declarations }
385 };
386 #bindings
387 },
388 Self::TraitVTableInnerFn { attrs, name, name_and_args, output_expression } =>
389 quote!(#(#attrs)* pub #name: #name_and_args #output_expression),
390 Self::StaticVTableInnerFn { aspect, name, args, output, body } =>
391 present_function(Visibility::Inherited, aspect, name, args.clone(), output.clone(), body.clone()),
392 Self::StaticVTableInnerFnDeclaration { name, fn_name } =>
393 quote!(#fn_name: #name),
394 Self::Any { attrs, body } =>
395 quote!(#(#attrs)* #body)
396
397 }.to_tokens(tokens)
398 }
399}