ferment_sys/presentation/
binding.rs

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                    // quote! {
340                    //     let rt = unsafe { &*(runtime as *mut tokio::runtime::Runtime) };
341                    //     #input_conversions;
342                    //     let obj = rt.block_on(tokio::task::spawn_blocking(move || {
343                    //         tokio::runtime::Handle::current().block_on(async {
344                    //             #full_fn_path(#argument_names).await
345                    //         })
346                    //     })).unwrap();
347                    //     #output_conversions
348                    // }
349                )
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}