rusty_bind_parser/swift/
function_helper.rs

1use std::fmt::Display;
2
3use convert_case::{Case, Casing};
4
5use crate::binding_types::{Arg, Function, RustWrapperType, WrapperType};
6use crate::extern_module_translator::Exceptions;
7use crate::swift::templates::TargetLanguageTypeName;
8use crate::EXPORTED_SYMBOLS_PREFIX;
9
10pub struct FunctionHelperVirtual {
11    pub function_name: String,
12    pub generated_args: String,
13    pub generated_function_body: Vec<String>,
14    pub generated_virtual_function_signature: String,
15    pub return_type: Option<WrapperType>,
16    pub arg_names: Vec<String>,
17    class_name: String,
18}
19
20impl FunctionHelperVirtual {
21    fn create_list_of_arguments_translated_to_cpp(function: &Function) -> Vec<String> {
22        let mut generated_args = function
23            .arguments
24            .iter()
25            .skip(1)
26            .map(|arg| match &arg.typ {
27                WrapperType {
28                    rust_type: RustWrapperType::Primitive | RustWrapperType::FieldlessEnum,
29                    ..
30                } => format!("_ {}: {}", arg.arg_name, arg.typ.wrapper_name),
31                _ => format!("_ {}: UnsafeMutableRawPointer", arg.arg_name),
32            })
33            .collect::<Vec<String>>();
34        generated_args.insert(0, "_ _self: AnyObject".to_owned());
35        generated_args
36    }
37
38    fn create_function_signature(function: &Function) -> String {
39        function
40            .arguments
41            .iter()
42            .skip(1)
43            .map(|arg| format!("_ {}: {}", arg.arg_name, arg.typ.get_name()))
44            .collect::<Vec<String>>()
45            .join(", ")
46    }
47
48    fn create_function_body(function: &Function) -> Vec<String> {
49        function
50            .arguments
51            .iter()
52            .skip(1)
53            .map(|arg| {
54                let inner_type_name = arg.typ.get_name();
55                let arg_name = &arg.arg_name;
56                format!("{inner_type_name}({arg_name})")
57            })
58            .collect()
59    }
60
61    /// Translates the intermediate form of a parsed function into
62    /// the elements ready-to-use in the C++ code generation process.
63    /// It prepares code for a C++ virtual method declaration and
64    /// extern "C" function that calls the virtual method. Due to this,
65    /// Rust code can call virtual C++ methods like trait functions.
66    ///
67    /// Pseudocode:
68    ///
69    /// Rust: SomeTrait {
70    ///     method_a();
71    /// }
72    ///
73    /// Swift: Class SomeTrait {
74    ///     method_a();
75    /// }
76    ///
77    /// @_cdecl __method_a(AnyType obj) {
78    ///     (obj as SomeTrait).method_a();
79    /// }
80    ///
81    pub fn from_virtual_function(function: &Function, class_name: &str) -> Self {
82        let generated_args =
83            FunctionHelperVirtual::create_list_of_arguments_translated_to_cpp(function);
84        let generated_args = generated_args.join(", ");
85        let generated_virtual_function_signature =
86            FunctionHelperVirtual::create_function_signature(function);
87        let generated_function_body: Vec<String> =
88            FunctionHelperVirtual::create_function_body(function);
89        let function_name = function.name.to_string();
90        let arg_names = function
91            .arguments
92            .iter()
93            .map(|arg| arg.arg_name.to_string())
94            .collect();
95        FunctionHelperVirtual {
96            function_name,
97            generated_args,
98            generated_function_body,
99            generated_virtual_function_signature,
100            return_type: function.return_type.clone(),
101            arg_names,
102            class_name: class_name.to_owned(),
103        }
104    }
105
106    /// Generates a virtual function declaration that can be used
107    /// within a class declaration.
108    ///
109    pub fn generate_virtual_declaration(self) -> String {
110        let FunctionHelperVirtual {
111            function_name,
112            generated_args: _,
113            generated_function_body: _,
114            generated_virtual_function_signature,
115            return_type,
116            ..
117        } = self;
118        let function_name = function_name.to_case(Case::Camel);
119        let return_type_string = if let Some(wrapper) = return_type {
120            wrapper.get_name_for_abstract_method()
121        } else {
122            "Void".to_owned()
123        };
124        format!("    func {function_name}({generated_virtual_function_signature}) -> {return_type_string}\n")
125    }
126
127    /// Generates an extern function that calls the virtual
128    /// method on a swift trait object that can be mapped to
129    /// some Rust trait object.
130    ///
131    pub fn generate_virtual_definition(self) -> String {
132        let FunctionHelperVirtual {
133            function_name,
134            generated_args,
135            generated_function_body,
136            generated_virtual_function_signature: _,
137            return_type,
138            arg_names: _,
139            class_name,
140        } = self;
141        let class_function_name = format!("{class_name}$");
142        let ffi_function_name =
143            format!("{EXPORTED_SYMBOLS_PREFIX}${class_function_name}{function_name}");
144        let generated_function_body = generated_function_body.join(", ");
145        let function_name = function_name.to_case(Case::Camel);
146        match return_type {
147                Some(WrapperType {
148                    rust_type: RustWrapperType::Primitive | RustWrapperType::FieldlessEnum,
149                    wrapper_name,
150                    ..
151                }) =>
152                format!(
153                    "@_cdecl(\"{ffi_function_name}\") func {ffi_function_name}({generated_args}) -> {wrapper_name} {{
154    return (_self as! {class_name}).{function_name}({generated_function_body})\n}}\n"),
155                None => format!(
156                    "@_cdecl(\"{ffi_function_name}\") func {ffi_function_name}({generated_args}) {{
157    (_self as! {class_name}).{function_name}({generated_function_body})\n}}\n"),
158                _ =>
159                    format!(
160                    "@_cdecl(\"{ffi_function_name}\") func {ffi_function_name}({generated_args}) -> UnsafeMutableRawPointer {{
161    return convertToOwnedType((_self as! {class_name}).{function_name}({generated_function_body}))\n}}\n"),
162            }
163    }
164}
165
166/// The structure contains already translated elements of a function.
167/// This can be usuful to generate declaration and definition of the
168/// given function in two different places.
169///
170pub struct FunctionTranslator {
171    pub function_name: String,
172    pub generated_args: String,
173    pub generated_function_body: Vec<String>,
174    pub return_type: Option<WrapperType>,
175    class_name: Option<String>,
176}
177
178impl FunctionTranslator {
179    /// Translates intermediate form of a class method into the Swift glue code.
180    ///
181    pub fn from_class_method(function: &Function, class_name: &str) -> Self {
182        FunctionTranslator::from_function(function, Some(class_name.to_owned()))
183    }
184
185    /// Translates intermediate form of a global function into the Swift glue code.
186    ///
187    pub fn from_global_function(function: &Function) -> Self {
188        FunctionTranslator::from_function(function, None)
189    }
190
191    /// Translates the intermediate form of a parsed function into
192    /// the elements ready-to-use in the Swift code generation process.
193    ///
194    fn from_function(function: &Function, class_name: Option<String>) -> Self {
195        let generated_args: String = function
196            .arguments
197            .iter()
198            .skip(class_name.is_some() as usize)
199            .map(|arg| {
200                let arg_name = &arg.arg_name;
201                let return_type_string = arg.typ.get_name();
202                format!("_ {arg_name}: {return_type_string}")
203            })
204            .collect::<Vec<String>>()
205            .join(", ");
206        let generated_function_body: Vec<String> = function
207            .arguments
208            .iter()
209            .skip(class_name.is_some() as usize)
210            .map(map_function_argument)
211            .collect();
212        let function_name = function.name.to_string();
213        FunctionTranslator {
214            function_name,
215            generated_args,
216            generated_function_body,
217            return_type: function.return_type.clone(),
218            class_name,
219        }
220    }
221
222    fn primitive_exception_throw(enum_name: impl Display, exception: impl Display) -> String {
223        let variant_check = format!(" == {enum_name}_{exception}");
224        format!(
225            "
226    else if (result.unwrapErr(){variant_check}) {{
227        throw {enum_name}_{exception}Exception(result.unwrapErr())
228    }}"
229        )
230    }
231
232    fn non_primitive_exception_throw(enum_name: impl Display, exception: impl Display) -> String {
233        let variant_check = format!(" == {enum_name}Tag_{exception}");
234        format!(
235            "
236    else if (result.unwrapErr().getTag(){variant_check}) {{
237        throw {enum_name}_{exception}Exception(result.unwrapErr())
238    }}"
239        )
240    }
241
242    /// Generates a method or global function definition.
243    ///
244    pub fn generate_definition(self) -> String {
245        let FunctionTranslator {
246            function_name,
247            generated_args,
248            mut generated_function_body,
249            return_type,
250            class_name,
251        } = self;
252
253        let class_function_name = if let Some(class_name) = &class_name {
254            format!("{class_name}$")
255        } else {
256            "".to_owned()
257        };
258        let ffi_function_name =
259            format!("{EXPORTED_SYMBOLS_PREFIX}${class_function_name}{function_name}");
260        let function_name = function_name.to_case(Case::Camel);
261        if class_name.is_some() {
262            generated_function_body.insert(0, "self._self".to_owned());
263        }
264        let generated_function_body = generated_function_body.join(", ");
265        if let Some(ref wrapper) = return_type {
266            if let Some(WrapperType {
267                wrapper_name,
268                rust_type: RustWrapperType::Result(_, err_type),
269                ..
270            }) = &return_type
271            {
272                let exception_enum_name = &err_type.wrapper_name;
273                let exceptions_throws = match &err_type.rust_type {
274                    RustWrapperType::Exceptions(Exceptions::Primitive(idents)) => idents
275                        .iter()
276                        .map(|exception| {
277                            Self::primitive_exception_throw(exception_enum_name, exception)
278                        })
279                        .collect::<String>(),
280                    RustWrapperType::Exceptions(Exceptions::NonPrimitive(idents)) => idents
281                        .iter()
282                        .map(|exception| {
283                            Self::non_primitive_exception_throw(exception_enum_name, exception)
284                        })
285                        .collect::<String>(),
286                    _ => panic!("Invalid wrapper type for exceptions wrapper"),
287                };
288                let return_type_string = wrapper.get_name();
289
290                format!("public func {function_name}({generated_args}) throws -> {return_type_string} {{
291    let result = {wrapper_name}({ffi_function_name}({generated_function_body}))
292    if (result.isOk()) {{
293        return result.unwrap()
294    }} {exceptions_throws}
295    else {{
296        fatalError(\"Unknown to ffi layer exception has been thrown\")
297    }}
298}}\n"
299                )
300            } else {
301                let return_type_string = wrapper.get_name();
302                format!(
303                    "    public func {function_name}({generated_args}) -> {return_type_string} {{
304        return {return_type_string}({ffi_function_name}({generated_function_body}))
305    }}\n"
306                )
307            }
308        } else {
309            format!(
310                "    public func {function_name}({generated_args}) {{
311        {ffi_function_name}({generated_function_body})
312    }}\n"
313            )
314        }
315    }
316}
317
318fn map_function_argument(arg: &Arg) -> String {
319    if arg.typ.reference_parameters.is_some()
320        && arg.typ.rust_type != RustWrapperType::Primitive
321        && arg.typ.rust_type != RustWrapperType::FieldlessEnum
322        && arg.typ.rust_type != RustWrapperType::Trait
323    {
324        format!("{}.asRef()", arg.arg_name)
325    } else {
326        format!("convertToOwnedType({})", arg.arg_name.to_owned())
327    }
328}