1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
mod abi;
mod dart;
mod js;
mod parser;
mod rust;

use crate::abi::{
    export, import, AbiFunction, AbiFuture, AbiIter, AbiObject, AbiStream, AbiType, FunctionType,
    NumType, Return, Var,
};
use crate::dart::DartGenerator;
use crate::js::{JsGenerator, TsGenerator, WasmMultiValueShim};
use crate::parser::Interface;
use crate::rust::RustGenerator;
use anyhow::Result;
use std::path::Path;
use std::process::Command;

pub use crate::abi::Abi;

pub struct FfiGen {
    iface: Interface,
}

impl FfiGen {
    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
        let s = std::fs::read_to_string(path)?;
        let iface = Interface::parse(&s)?;
        Ok(Self { iface })
    }

    pub fn generate_rust(&self, abi: Abi) -> Result<String> {
        let rust = RustGenerator::new(abi);
        let rust = rust.generate(self.iface.clone()).to_file_string()?;
        Ok(rust)
    }

    pub fn wasm_multi_value_shim<P: AsRef<Path>>(&self, path: P) -> Result<()> {
        WasmMultiValueShim::new().run(path, self.iface.clone())
    }

    pub fn generate_dart<P: AsRef<Path>>(
        &self,
        path: P,
        library: &str,
        cdylib: &str,
    ) -> Result<()> {
        let dart = DartGenerator::new(library.to_string(), cdylib.to_string());
        let dart = dart.generate(self.iface.clone()).to_file_string()?;
        std::fs::write(path.as_ref(), &dart)?;
        let status = Command::new("dart")
            .arg("format")
            .arg(path.as_ref())
            .status()?;
        if !status.success() {
            anyhow::bail!("dart format failed");
        }
        Ok(())
    }

    pub fn generate_js<P: AsRef<Path>>(&self, path: P) -> Result<()> {
        let js = JsGenerator::default();
        let js = js.generate(self.iface.clone()).to_file_string()?;
        std::fs::write(path.as_ref(), &js)?;
        let status = Command::new("prettier")
            .arg("--write")
            .arg(path.as_ref())
            .status()?;
        if !status.success() {
            anyhow::bail!("prettier failed");
        }
        Ok(())
    }

    pub fn generate_ts<P: AsRef<Path>>(&self, path: P) -> Result<()> {
        let ts = TsGenerator::default();
        let ts = ts.generate(self.iface.clone()).to_file_string()?;
        std::fs::write(path.as_ref(), &ts)?;
        let status = Command::new("prettier")
            .arg("--write")
            .arg(path.as_ref())
            .status()?;
        if !status.success() {
            anyhow::bail!("prettier failed");
        }
        Ok(())
    }
}

#[cfg(feature = "test_runner")]
pub mod test_runner {
    pub use crate::dart::test_runner::compile_pass as compile_pass_dart;
    pub use crate::js::test_runner::compile_pass as compile_pass_js;
    pub use crate::js::test_runner::compile_pass_ts;
    pub use crate::rust::test_runner::compile_pass as compile_pass_rust;

    #[macro_export]
    macro_rules! compile_pass {
        ($ident:ident, $iface:expr, ($($api:tt)*), ($($rust:tt)*), ($($dart:tt)*), ($($js:tt)*), ($($ts:tt)*)) => {
            mod $ident {
                #[test]
                fn rust() {
                    $crate::test_runner::compile_pass_rust($iface, genco::quote!($($api)*), genco::quote!($($rust)*)).unwrap();
                }

                #[test]
                fn dart() {
                    $crate::test_runner::compile_pass_dart($iface, genco::quote!($($api)*), genco::quote!($($dart)*)).unwrap();
                }

                #[test]
                fn js() {
                    $crate::test_runner::compile_pass_js($iface, genco::quote!($($api)*), genco::quote!($($js)*)).unwrap();
                }

                #[test]
                fn ts() {
                    $crate::test_runner::compile_pass_ts($iface, genco::quote!($($ts)*)).unwrap();
                }
            }
        }
    }
}