ffi_gen/
lib.rs

1//! # ffi-gen
2//!
3//! Call rust from any language.
4#![deny(missing_docs)]
5
6mod abi;
7mod dart;
8mod js;
9mod parser;
10mod rust;
11
12use crate::abi::{
13    export, import, AbiFunction, AbiFuture, AbiIter, AbiObject, AbiStream, AbiType, FunctionType,
14    NumType, Return, Var,
15};
16use crate::dart::DartGenerator;
17use crate::js::{JsGenerator, TsGenerator, WasmMultiValueShim};
18use crate::parser::Interface;
19use crate::rust::RustGenerator;
20use anyhow::{Context, Result};
21use std::path::Path;
22use std::process::Command;
23
24pub use crate::abi::Abi;
25
26/// Main entry point to `ffi-gen`.
27pub struct FfiGen {
28    iface: Interface,
29}
30
31impl FfiGen {
32    /// Takes a path to an ffi-gen interface description file and constructs
33    /// a new `FfiGen` instance.
34    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
35        let s = std::fs::read_to_string(path)?;
36        let iface = Interface::parse(&s)?;
37        Ok(Self { iface })
38    }
39
40    /// Generates the rust api.
41    pub fn generate_rust(&self, abi: Abi) -> Result<String> {
42        let rust = RustGenerator::new(abi);
43        let rust = rust.generate(self.iface.clone()).to_file_string()?;
44        Ok(rust)
45    }
46
47    /// Patches the ffi functions in a wasm blob to use multi-value returns.
48    pub fn wasm_multi_value_shim<P: AsRef<Path>>(&self, path: P) -> Result<()> {
49        WasmMultiValueShim::new().run(path, self.iface.clone())
50    }
51
52    /// Generates dart bindings for the rust api.
53    pub fn generate_dart<P: AsRef<Path>>(
54        &self,
55        path: P,
56        library: &str,
57        cdylib: &str,
58    ) -> Result<()> {
59        let dart = DartGenerator::new(library.to_string(), cdylib.to_string());
60        let dart = dart.generate(self.iface.clone()).to_file_string()?;
61        std::fs::write(path.as_ref(), &dart)?;
62        let status = Command::new("dart")
63            .arg("format")
64            .arg(path.as_ref())
65            .status()
66            .context("dart not installed")?;
67        if !status.success() {
68            anyhow::bail!("dart format failed");
69        }
70        Ok(())
71    }
72
73    /// Generates js bindings for the rust api.
74    pub fn generate_js<P: AsRef<Path>>(&self, path: P) -> Result<()> {
75        let js = JsGenerator::default();
76        let js = js.generate(self.iface.clone()).to_file_string()?;
77        std::fs::write(path.as_ref(), &js)?;
78        let status = Command::new("prettier")
79            .arg("--write")
80            .arg(path.as_ref())
81            .status()
82            .context("prettier not installed")?;
83        if !status.success() {
84            anyhow::bail!("prettier failed");
85        }
86        Ok(())
87    }
88
89    /// Generates typescript type definitions for the js bindings.
90    pub fn generate_ts<P: AsRef<Path>>(&self, path: P) -> Result<()> {
91        let ts = TsGenerator::default();
92        let ts = ts.generate(self.iface.clone()).to_file_string()?;
93        std::fs::write(path.as_ref(), &ts)?;
94        let status = Command::new("prettier")
95            .arg("--write")
96            .arg(path.as_ref())
97            .status()
98            .context("prettier not installed")?;
99        if !status.success() {
100            anyhow::bail!("prettier failed");
101        }
102        Ok(())
103    }
104}
105
106#[cfg(feature = "test_runner")]
107#[doc(hidden)]
108pub mod test_runner {
109    pub use crate::dart::test_runner::compile_pass as compile_pass_dart;
110    pub use crate::js::test_runner::compile_pass as compile_pass_js;
111    pub use crate::js::test_runner::compile_pass_ts;
112    pub use crate::rust::test_runner::compile_pass as compile_pass_rust;
113
114    #[macro_export]
115    macro_rules! compile_pass {
116        ($ident:ident, $iface:expr, ($($api:tt)*), ($($rust:tt)*), ($($dart:tt)*), ($($js:tt)*), ($($ts:tt)*)) => {
117            mod $ident {
118                #[test]
119                fn rust() {
120                    $crate::test_runner::compile_pass_rust($iface, genco::quote!($($api)*), genco::quote!($($rust)*)).unwrap();
121                }
122
123                #[test]
124                fn dart() {
125                    $crate::test_runner::compile_pass_dart($iface, genco::quote!($($api)*), genco::quote!($($dart)*)).unwrap();
126                }
127
128                #[test]
129                fn js() {
130                    $crate::test_runner::compile_pass_js($iface, genco::quote!($($api)*), genco::quote!($($js)*)).unwrap();
131                }
132
133                #[test]
134                fn ts() {
135                    $crate::test_runner::compile_pass_ts($iface, genco::quote!($($ts)*)).unwrap();
136                }
137            }
138        }
139    }
140}