oo_bindgen/backend/rust/
mod.rs

1use std::env;
2use std::path::{Path, PathBuf};
3use std::rc::Rc;
4
5use heck::ToUpperCamelCase;
6
7use crate::backend::*;
8use crate::model::*;
9
10use crate::backend::rust::rust_struct::RustStruct;
11use crate::backend::rust::rust_type::RustType;
12
13use crate::backend::rust::rust_type::LifetimeInfo;
14use crate::backend::rust::type_converter::TypeConverter;
15
16mod rust_struct;
17mod rust_type;
18mod type_converter;
19
20/// Generate the FFI (C ABI) interface for the library.
21///
22/// This layer consists of all the public symbols that will be exported by the shared library.
23/// The user must then go and write a number of functions referenced by the FFI functions to glue
24/// the C API layer to the underlying rust crate.
25///
26/// This function is typically called from a build.rs script
27pub fn generate_ffi(library: &Library) -> FormattingResult<()> {
28    RustCodegen::new(library).generate()
29}
30
31struct RustCodegen<'a> {
32    library: &'a Library,
33    dest_path: PathBuf,
34}
35
36impl<'a> RustCodegen<'a> {
37    fn new(lib: &'a Library) -> Self {
38        RustCodegen {
39            library: lib,
40            dest_path: Path::new(&env::var_os("OUT_DIR").unwrap()).join("ffi.rs"),
41        }
42    }
43
44    fn generate(self) -> FormattingResult<()> {
45        let mut f = FilePrinter::new(&self.dest_path)?;
46
47        for statement in self.library.statements() {
48            match statement {
49                Statement::StructDefinition(s) => match s {
50                    StructType::FunctionArg(s) => self.write_struct_definition(&mut f, s)?,
51                    StructType::FunctionReturn(s) => self.write_struct_definition(&mut f, s)?,
52                    StructType::CallbackArg(s) => self.write_struct_definition(&mut f, s)?,
53                    StructType::Universal(s) => self.write_struct_definition(&mut f, s)?,
54                },
55                Statement::EnumDefinition(handle) => self.write_enum_definition(&mut f, handle)?,
56                Statement::FunctionDefinition(handle) => {
57                    Self::write_function(&mut f, handle, &self.library.settings.c_ffi_prefix)?
58                }
59                Statement::InterfaceDefinition(t) => {
60                    self.write_interface(&mut f, t.untyped(), t.mode())?;
61                }
62                _ => (),
63            }
64            f.newline()?;
65        }
66
67        Ok(())
68    }
69
70    fn write_struct_definition<T>(
71        &self,
72        f: &mut dyn Printer,
73        handle: &Handle<Struct<T, Validated>>,
74    ) -> FormattingResult<()>
75    where
76        T: StructFieldType + RustType,
77    {
78        let struct_name = handle.name().to_upper_camel_case();
79        let c_lifetime = if handle.annotate_c_with_lifetime() {
80            "<'a>"
81        } else {
82            ""
83        };
84        let rust_lifetime = if handle.annotate_rust_with_lifetime() {
85            "<'a>"
86        } else {
87            ""
88        };
89        let public = "pub "; //if handle.has_conversion() { "" } else { "pub " };
90
91        // Write the C struct with private fields (if conversion required)
92        f.writeln("#[repr(C)]")?;
93        f.writeln("#[derive(Clone)]")?;
94        f.writeln(&format!("pub struct {struct_name}{c_lifetime}"))?;
95
96        blocked(f, |f| {
97            for element in &handle.fields {
98                f.writeln(&format!(
99                    "{}{}: {},",
100                    public,
101                    element.name,
102                    element.field_type.as_c_type()
103                ))?;
104            }
105            Ok(())
106        })?;
107
108        f.newline()?;
109
110        // Write accessors/mutators
111        f.writeln(&format!("impl{c_lifetime} {struct_name}{c_lifetime}"))?;
112        blocked(f, |f| {
113            for field in &handle.fields {
114                let field_lifetime = if field.field_type.rust_requires_lifetime() {
115                    "'a "
116                } else {
117                    ""
118                };
119                let fn_lifetime =
120                    if field.field_type.rust_requires_lifetime() && !handle.c_requires_lifetime() {
121                        "<'a>"
122                    } else {
123                        ""
124                    };
125                let ampersand = if field.field_type.is_copyable() {
126                    ""
127                } else {
128                    "&"
129                };
130
131                // Accessor
132                f.writeln("#[allow(clippy::needless_lifetimes)]")?;
133                f.writeln(&format!(
134                    "pub fn {name}{fn_lifetime}(&{lifetime}self) -> {ampersand}{return_type}",
135                    name = field.name,
136                    return_type = field.field_type.as_rust_type(),
137                    fn_lifetime = fn_lifetime,
138                    lifetime = field_lifetime,
139                    ampersand = ampersand
140                ))?;
141                blocked(f, |f| {
142                    if let Some(conversion) = field.field_type.conversion() {
143                        if conversion.is_unsafe() {
144                            f.writeln("unsafe {")?;
145                        }
146                        conversion.convert_from_c(
147                            f,
148                            &format!(
149                                "{ampersand}self.{name}",
150                                name = field.name,
151                                ampersand = ampersand
152                            ),
153                            "",
154                        )?;
155                        if conversion.is_unsafe() {
156                            f.writeln("}")?;
157                        }
158                        Ok(())
159                    } else {
160                        f.writeln(&format!(
161                            "{ampersand}self.{name}",
162                            name = field.name,
163                            ampersand = ampersand
164                        ))
165                    }
166                })?;
167
168                f.newline()?;
169
170                // Mutator
171                f.writeln("#[allow(clippy::needless_lifetimes)]")?;
172                f.writeln(&format!(
173                    "pub fn set_{name}{fn_lifetime}(&{lifetime}mut self, value: {element_type})",
174                    name = field.name,
175                    element_type = field.field_type.as_rust_type(),
176                    fn_lifetime = fn_lifetime,
177                    lifetime = field_lifetime
178                ))?;
179                blocked(f, |f| {
180                    if let Some(conversion) = field.field_type.conversion() {
181                        conversion.convert_to_c(f, "value", &format!("self.{} = ", field.name))?;
182                        f.write(";")
183                    } else {
184                        f.writeln(&format!("self.{} = value;", field.name))
185                    }
186                })?;
187
188                f.newline()?;
189            }
190            Ok(())
191        })?;
192
193        // Write the Rust version with all public fields
194        let rust_struct_name = format!("{struct_name}Fields");
195        if handle.has_conversion() {
196            f.writeln(&format!("pub struct {rust_struct_name}{rust_lifetime}"))?;
197            blocked(f, |f| {
198                for element in &handle.fields {
199                    f.writeln(&format!(
200                        "pub {}: {},",
201                        element.name,
202                        element.field_type.as_rust_type()
203                    ))?;
204                }
205                Ok(())
206            })?;
207
208            // Write the conversion to the C representation
209            f.writeln(&format!(
210                "impl{rust_lifetime} From<{rust_struct_name}{rust_lifetime}> for {struct_name}{c_lifetime}",
211            ))?;
212            blocked(f, |f| {
213                f.writeln(&format!("fn from(from: {rust_struct_name}) -> Self"))?;
214                blocked(f, |f| {
215                    f.writeln("Self")?;
216                    blocked(f, |f| {
217                        for element in &handle.fields {
218                            if let Some(conversion) = element.field_type.conversion() {
219                                conversion.convert_to_c(
220                                    f,
221                                    &format!("from.{}", element.name),
222                                    &format!("{}: ", element.name),
223                                )?;
224                                f.write(",")?;
225                            } else {
226                                f.writeln(&format!("{name}: from.{name},", name = element.name))?;
227                            }
228                        }
229                        Ok(())
230                    })
231                })
232            })
233        } else {
234            f.writeln(&format!(
235                "pub type {rust_struct_name}{c_lifetime} = {struct_name}{c_lifetime};"
236            ))
237        }
238    }
239
240    fn write_enum_definition(
241        &self,
242        f: &mut dyn Printer,
243        handle: &Handle<Enum<Validated>>,
244    ) -> FormattingResult<()> {
245        let enum_name = handle.name.to_upper_camel_case();
246        f.writeln("#[repr(C)]")?;
247        f.writeln("#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]")?;
248        f.writeln(&format!("pub enum {enum_name}"))?;
249        blocked(f, |f| {
250            for variant in &handle.variants {
251                f.writeln(&format!(
252                    "{} = {},",
253                    variant.name.to_upper_camel_case(),
254                    variant.value
255                ))?;
256            }
257            Ok(())
258        })?;
259
260        // Conversion routines
261        f.writeln(&format!("impl From<{enum_name}> for std::os::raw::c_int"))?;
262        blocked(f, |f| {
263            f.writeln(&format!("fn from(value: {enum_name}) -> Self"))?;
264            blocked(f, |f| {
265                f.writeln("match value")?;
266                blocked(f, |f| {
267                    for variant in &handle.variants {
268                        f.writeln(&format!(
269                            "{}::{} => {},",
270                            enum_name,
271                            variant.name.to_upper_camel_case(),
272                            variant.value
273                        ))?;
274                    }
275                    Ok(())
276                })
277            })
278        })?;
279
280        f.writeln(&format!("impl From<std::os::raw::c_int> for {enum_name}"))?;
281        blocked(f, |f| {
282            f.writeln("fn from(value: std::os::raw::c_int) -> Self")?;
283            blocked(f, |f| {
284                f.writeln("match value")?;
285                blocked(f, |f| {
286                    for variant in &handle.variants {
287                        f.writeln(&format!(
288                            "{} => {}::{},",
289                            variant.value,
290                            enum_name,
291                            variant.name.to_upper_camel_case(),
292                        ))?;
293                    }
294                    f.writeln(&format!(
295                        "_ => panic!(\"{{value}} is not a variant of {enum_name}\"),"
296                    ))
297                })
298            })
299        })
300    }
301
302    fn write_function(
303        f: &mut dyn Printer,
304        handle: &Handle<Function<Validated>>,
305        prefix: &str,
306    ) -> FormattingResult<()> {
307        f.writeln("#[allow(clippy::missing_safety_doc)]")?;
308        f.writeln("#[no_mangle]")?;
309        f.writeln(&format!(
310            "pub unsafe extern \"C\" fn {}_{}(",
311            prefix, handle.name
312        ))?;
313
314        f.write(
315            &handle
316                .arguments
317                .iter()
318                .map(|param| format!("{}: {}", param.name, param.arg_type.as_c_type()))
319                .collect::<Vec<String>>()
320                .join(", "),
321        )?;
322
323        fn write_error_return(
324            f: &mut dyn Printer,
325            _error: &ErrorType<Validated>,
326        ) -> FormattingResult<()> {
327            f.write(") -> std::os::raw::c_int")
328        }
329
330        // write the return type
331        match handle.get_signature_type() {
332            SignatureType::NoErrorNoReturn => {
333                f.write(")")?;
334            }
335            SignatureType::NoErrorWithReturn(t, _) => {
336                f.write(&format!(") -> {}", t.as_c_type()))?;
337            }
338            SignatureType::ErrorNoReturn(err) => {
339                write_error_return(f, &err)?;
340            }
341            SignatureType::ErrorWithReturn(err, ret, _) => {
342                if !handle.arguments.is_empty() {
343                    f.write(", ")?;
344                }
345                f.write(&format!("out: *mut {}", ret.as_c_type()))?;
346                write_error_return(f, &err)?;
347            }
348        }
349
350        blocked(f, |f| {
351            for param in &handle.arguments {
352                if let Some(converter) = param.arg_type.conversion() {
353                    converter.convert_from_c(f, &param.name, &format!("let {} = ", param.name))?;
354                    f.write(";")?;
355                }
356            }
357
358            fn basic_invocation(f: &mut dyn Printer, name: &str) -> FormattingResult<()> {
359                f.writeln(&format!("crate::{name}("))
360            }
361
362            // invoke the inner function
363            match handle.get_signature_type() {
364                SignatureType::NoErrorNoReturn => {
365                    basic_invocation(f, &handle.name)?;
366                }
367                SignatureType::NoErrorWithReturn(ret, _) => {
368                    if ret.has_conversion() {
369                        f.writeln(&format!("let _result = crate::{}(", handle.name))?;
370                    } else {
371                        basic_invocation(f, &handle.name)?;
372                    }
373                }
374                SignatureType::ErrorWithReturn(_, _, _) | SignatureType::ErrorNoReturn(_) => {
375                    f.writeln(&format!("match crate::{}(", &handle.name))?;
376                }
377            }
378
379            f.write(
380                &handle
381                    .arguments
382                    .iter()
383                    .map(|param| param.name.to_string())
384                    .collect::<Vec<String>>()
385                    .join(", "),
386            )?;
387            f.write(")")?;
388
389            match handle.get_signature_type() {
390                SignatureType::NoErrorNoReturn => {}
391                SignatureType::NoErrorWithReturn(ret, _) => {
392                    if let Some(conversion) = ret.conversion() {
393                        f.write(";")?;
394                        conversion.convert_to_c(f, "_result", "")?;
395                    }
396                }
397                SignatureType::ErrorNoReturn(err) => {
398                    blocked(f, |f| {
399                        let converter = TypeConverter::ValidatedEnum(err.inner.clone());
400                        f.writeln("Ok(()) =>")?;
401                        blocked(f, |f| {
402                            converter.convert_to_c(
403                                f,
404                                &format!("{}::Ok", err.inner.name.to_upper_camel_case()),
405                                "",
406                            )
407                        })?;
408                        f.writeln("Err(err) =>")?;
409                        blocked(f, |f| converter.convert_to_c(f, "err", ""))
410                    })?;
411                }
412                SignatureType::ErrorWithReturn(err, result_type, _) => {
413                    blocked(f, |f| {
414                        let converter = TypeConverter::ValidatedEnum(err.inner.clone());
415                        f.writeln("Ok(x) =>")?;
416                        blocked(f, |f| {
417                            if let Some(converter) = result_type.conversion() {
418                                converter.convert_to_c(f, "x", "let x = ")?;
419                                f.write(";")?;
420                            }
421                            f.writeln("out.write(x);")?;
422                            converter.convert_to_c(
423                                f,
424                                &format!("{}::Ok", err.inner.name.to_upper_camel_case()),
425                                "",
426                            )
427                        })?;
428                        f.writeln("Err(err) =>")?;
429                        blocked(f, |f| converter.convert_to_c(f, "err", ""))
430                    })?;
431                }
432            }
433
434            Ok(())
435        })
436    }
437
438    fn write_interface(
439        &self,
440        f: &mut dyn Printer,
441        handle: &Interface<Validated>,
442        mode: InterfaceCategory,
443    ) -> FormattingResult<()> {
444        let interface_name = handle.name.to_upper_camel_case();
445        // C structure
446        f.writeln("#[repr(C)]")?;
447        f.writeln("#[derive(Clone)]")?;
448        f.writeln(&format!("pub struct {interface_name}"))?;
449        blocked(f, |f| {
450            for cb in &handle.callbacks {
451                let lifetime = if cb.c_requires_lifetime() {
452                    "for<'a> "
453                } else {
454                    ""
455                };
456
457                f.writeln("#[allow(clippy::needless_lifetimes)]")?;
458                f.writeln(&format!(
459                    "pub {name}: Option<{lifetime}extern \"C\" fn(",
460                    name = cb.name,
461                    lifetime = lifetime
462                ))?;
463
464                f.write(
465                    &cb.arguments
466                        .iter()
467                        .map(|arg| format!("{}: {}", arg.name, arg.arg_type.as_c_type()))
468                        .chain(std::iter::once(format!(
469                            "{}: *mut std::os::raw::c_void",
470                            handle.settings.interface.context_variable_name
471                        )))
472                        .collect::<Vec<String>>()
473                        .join(", "),
474                )?;
475
476                f.write(&format!(") -> {}>,", cb.return_type.as_c_type()))?;
477            }
478
479            f.writeln(&format!(
480                "pub {}: Option<extern \"C\" fn(ctx: *mut std::os::raw::c_void)>,",
481                handle.settings.interface.destroy_func_name
482            ))?;
483
484            f.writeln(&format!(
485                "pub {}: *mut std::os::raw::c_void,",
486                handle.settings.interface.context_variable_name
487            ))?;
488            Ok(())
489        })?;
490
491        f.newline()?;
492
493        self.write_callback_helpers(
494            f,
495            mode,
496            &interface_name,
497            handle.settings.clone(),
498            handle.callbacks.iter(),
499        )?;
500
501        f.newline()?;
502
503        // Drop
504        f.writeln(&format!("impl Drop for {interface_name}"))?;
505        blocked(f, |f| {
506            f.writeln("fn drop(&mut self)")?;
507            blocked(f, |f| {
508                f.writeln(&format!(
509                    "if let Some(cb) = self.{}",
510                    handle.settings.interface.destroy_func_name
511                ))?;
512                blocked(f, |f| {
513                    f.writeln(&format!(
514                        "cb(self.{});",
515                        handle.settings.interface.context_variable_name
516                    ))
517                })
518            })
519        })?;
520
521        Ok(())
522    }
523
524    fn write_callback_helpers<'b, I: Iterator<Item = &'b CallbackFunction<Validated>>>(
525        &self,
526        f: &mut dyn Printer,
527        mode: InterfaceCategory,
528        name: &str,
529        settings: Rc<LibrarySettings>,
530        callbacks: I,
531    ) -> FormattingResult<()> {
532        let generate_send_and_sync = match mode {
533            InterfaceCategory::Synchronous => false,
534            InterfaceCategory::Asynchronous => true,
535            InterfaceCategory::Future => true,
536        };
537
538        // Send/Sync trait
539        if generate_send_and_sync {
540            f.writeln(&format!("unsafe impl Send for {name} {{}}"))?;
541            f.writeln(&format!("unsafe impl Sync for {name} {{}}"))?;
542        }
543
544        f.newline()?;
545
546        // Helper impl
547        f.writeln(&format!("impl {name}"))?;
548        blocked(f, |f| {
549            for callback in callbacks {
550                let lifetime = if callback.rust_requires_lifetime() {
551                    "<'a>"
552                } else {
553                    ""
554                };
555
556                // Function signature
557                f.writeln("#[allow(clippy::needless_lifetimes)]")?;
558                f.writeln(&format!(
559                    "pub(crate) fn {name}{lifetime}(&self, ",
560                    name = callback.name,
561                    lifetime = lifetime
562                ))?;
563                f.write(
564                    &callback
565                        .arguments
566                        .iter()
567                        .map(|arg| format!("{}: {}", arg.name, arg.arg_type.as_rust_type()))
568                        .collect::<Vec<_>>()
569                        .join(", "),
570                )?;
571                f.write(")")?;
572
573                if let Some(value) = &callback.return_type.get_value() {
574                    f.write(&format!(" -> Option<{}>", value.as_rust_type()))?;
575                }
576
577                // Function body
578                blocked(f, |f| {
579                    for arg in &callback.arguments {
580                        if let Some(converter) = arg.arg_type.conversion() {
581                            converter.convert_to_c(
582                                f,
583                                &arg.name,
584                                &format!("let {} = ", arg.name),
585                            )?;
586                            f.write(";")?;
587                        }
588                    }
589
590                    let params = &callback
591                        .arguments
592                        .iter()
593                        .map(|arg| arg.name.to_string())
594                        .chain(std::iter::once(format!(
595                            "self.{}",
596                            settings.interface.context_variable_name
597                        )))
598                        .collect::<Vec<_>>()
599                        .join(", ");
600                    let call = format!("cb({params})");
601
602                    if let Some(v) = &callback.return_type.get_value() {
603                        f.writeln(&format!("self.{}.map(|cb| ", callback.name))?;
604                        blocked(f, |f| {
605                            if let Some(conversion) = v.conversion() {
606                                f.writeln(&format!("let _result = {call};"))?;
607                                conversion.convert_from_c(f, "_result", "")
608                            } else {
609                                f.writeln(&call)
610                            }
611                        })?;
612                        f.write(")")?;
613                    } else {
614                        f.writeln(&format!("if let Some(cb) = self.{}", callback.name))?;
615                        blocked(f, |f| f.writeln(&call))?;
616                    }
617
618                    if callback.return_type.is_none() {
619                        f.write(";")?;
620                    }
621
622                    Ok(())
623                })?;
624            }
625            Ok(())
626        })
627    }
628}