fp-bindgen 3.0.0

Bindings generator for full-stack WASM plugins
Documentation
use crate::{
    functions::{Function, FunctionList},
    generators::{
        rust_plugin::generate_type_bindings,
        rust_wasmer2_runtime::{
            format_function_bindings, format_import_function, generate_export_function_variables,
            write_bindings_file, ExportFunctionVariables,
        },
    },
    types::TypeMap,
};
use std::fs;

pub(crate) fn generate_bindings(
    import_functions: FunctionList,
    export_functions: FunctionList,
    types: TypeMap,
    path: &str,
) {
    fs::create_dir_all(path).expect("Could not create output directory");

    generate_type_bindings(&types, path);

    generate_function_bindings(import_functions, export_functions, &types, path);
}

fn generate_create_import_object_func(import_functions: &FunctionList) -> String {
    let imports = import_functions
        .iter()
        .map(|function| {
            let name = &function.name;
            format!(
                r#"namespace.insert(
            "__fp_gen_{name}",
            Function::new_native_with_env(store, env.clone(), _{name})
    );"#
            )
        })
        .collect::<Vec<_>>()
        .join("\n    ");

    format!(
        r#"fn create_import_object(store: &Store, env: &RuntimeInstanceData) -> wasmer::Exports {{
    let mut namespace = wasmer::Exports::new();
    namespace.insert(
            "__fp_host_resolve_async_value",
            Function::new_native_with_env(store, env.clone(), resolve_async_value)
    );
    {imports}
    namespace
}}"#
    )
}

fn format_export_function(function: &Function, types: &TypeMap) -> String {
    let ExportFunctionVariables {
        doc,
        modifiers,
        name,
        args,
        raw_args,
        wasm_args,
        return_type,
        raw_return_type,
        wasm_return_type,
        serialize_args,
        serialize_raw_args,
        arg_names,
        wasm_arg_names,
        raw_return_wrapper,
        return_wrapper,
    } = generate_export_function_variables(function, types);

    format!(
        r#"{doc}pub {modifiers}fn {name}(&self{args}) -> Result<{return_type}, InvocationError> {{
    {serialize_args}
    let result = self.{name}_raw({arg_names});
    {return_wrapper}result
}}
pub {modifiers}fn {name}_raw(&self{raw_args}) -> Result<{raw_return_type}, InvocationError> {{
    {serialize_raw_args}let function = self.instance
        .exports
        .get_native_function::<{wasm_args}, {wasm_return_type}>("__fp_gen_{name}")
        .map_err(|_| InvocationError::FunctionNotExported("__fp_gen_{name}".to_owned()))?;
    let result = function.call({wasm_arg_names})?;
    {raw_return_wrapper}Ok(result)
}}"#
    )
}

fn generate_function_bindings(
    import_functions: FunctionList,
    export_functions: FunctionList,
    types: &TypeMap,
    path: &str,
) {
    let imports = import_functions
        .iter()
        .map(|function| format_import_function(function, types))
        .collect::<Vec<_>>()
        .join("\n\n");
    let exports = export_functions
        .iter()
        .map(|function| format_export_function(function, types))
        .collect::<Vec<_>>()
        .join("\n\n");
    let new_func = r#"pub fn new(wasm_module: impl AsRef<[u8]>) -> Result<Self, RuntimeError> {
        let store = Self::default_store();
        let module = Module::new(&store, wasm_module)?;
        let mut env = RuntimeInstanceData::default();
        let mut wasi_env = wasmer_wasi::WasiState::new("fp").finalize().unwrap();
        let mut import_object = wasi_env.import_object(&module).unwrap();
        let namespace = create_import_object(module.store(), &env);
        import_object.register("fp", namespace);
        let instance = Instance::new(&module, &import_object).unwrap();
        env.init_with_instance(&instance).unwrap();
        Ok(Self { instance, env })
    }"#
    .to_string();
    let create_import_object_func = generate_create_import_object_func(&import_functions);

    write_bindings_file(
        format!("{path}/bindings.rs"),
        format_function_bindings(imports, exports, new_func, create_import_object_func),
    );
}