denoland / deno_bindgen

Write high-level Deno FFI libraries in Rust.
MIT License
274 stars 28 forks source link

Please document how to use `#[deno_bindgen]` when embedding #140

Closed joshtriplett closed 8 months ago

joshtriplett commented 8 months ago

The examples all show how to build a plugin to be loaded by code running in deno. I'd love to use #[deno_bindgen] to generate Rust bindings as a deno Extension that I can use when embedding deno within another program.

If deno_bindgen is the wrong approach for this, I'd appreciate a pointer towards the right approach, for trying to expose Rust types and methods as an ES6 module without having to manually write ops and JS/TS wrappers around those ops.

littledivy commented 8 months ago

deno_bindgen is for runtime FFI. For embedding usecases, you should use deno_ops: https://github.com/denoland/deno_core/tree/main/ops

An example can be found here: https://github.com/denoland/deno_core/blob/main/core/examples/hello_world.rs

The repository for issues related to deno_ops is denoland/deno_core

joshtriplett commented 8 months ago

@littledivy I had already looked at deno_ops (and for that matter the version exported from deno_core as op2). That makes it very easy to export individual functions from Rust to Deno. However, that then requires writing a JS or TS module that wraps those ops, to provide types and methods. What I was hoping for was the ability to export an ES6 module from Rust, similarly to what deno_bindgen provides or what rquickjs provides, without having to write those two wrappers (a Rust wrapper for each method exposing it as a function, and a JS/TS wrapper for each function turning it back into a method).

Ideally, I'd love to write something like this,:

#[deno_core::something(my_es6_module)]
mod my_module {
    #[deno_core::something_else]
    struct SomeType {
        field: Type,
    }

    #[deno_core::something]
    impl SomeType {
        fn new() -> Self { todo!() }
        fn method(&self, arg: Abc) { todo!() }
        fn another_method(&mut self, arg: Xyz) { todo!() }
    }

    // optionally some module initialization code, other types, constants, functions, etc
}

my_es6_module::init_ops_and_esm()

Right now, if I already have that type, I have to write three ops, export those in an extension, and write a JS/TS file defining a type with methods wrapping those three ops.

Is this something that just doesn't exist today? Is anything like this on the roadmap?

bartlomieju commented 8 months ago

Hey @joshtriplett. Something like this currently doesn't exist. Not sure about the roadmap at the moment, but I know that both @littledivy and @mmastrac have been discussion something like that as a potential new feature. So unfortunately for now you'll have to resort to writing the JS/TS glue code.

mmastrac commented 8 months ago

I've got some ideas on how we can extend our op system to do that. We're not too far off, and we recently landed the first bits of support for cpp_gc which was the last missing piece to integrate with GC.