Cherry 😈
Cherry 😈8mo ago

Embedding all of deno in Rust?

So, I know that you can use deno_core to roll a javascript runtime in Rust and use that, and use it with your own api and whatnot. But what if you want to be able to leverage already created deno libraries, but in your Rust program? Think of it as a sort of plugin system in your Rust app that can leverage all of deno's api (including packages)
11 Replies
Cherry 😈
Cherry 😈8mo ago
I guess, after a bit of reading, what I'm asking for is how to embed the deno runtime (not just the deno_core) into a Rust app, such that regular deno packages should work (if this is even possible)
crowlKats
crowlKats8mo ago
the deno cli itself does not have a rust crate, and a bunch of functionality (including typescript typechecking, and other aspects) would need to be implemented manually ontop of the deno_runtime crate
Cherry 😈
Cherry 😈8mo ago
So far I've partially gotten a runtime working (along with a custom module loader which transpiles as well, and can do network requests; one library example I tested worked verbatim) But when it comes to async await it crashes with
thread 'main' panicked at ~\.cargo\registry\src\index.crates.io-6f17d22bba15001f\deno_unsync-0.3.2\src\task.rs:51:3:
assertion failed: Handle::current().runtime_flavor() == RuntimeFlavor::CurrentThread
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at ~\.cargo\registry\src\index.crates.io-6f17d22bba15001f\deno_unsync-0.3.2\src\task.rs:51:3:
assertion failed: Handle::current().runtime_flavor() == RuntimeFlavor::CurrentThread
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Any ideas how I could solve this?
crowlKats
crowlKats8mo ago
hmm not sure. cc @mmastrac since deno_unsync error
mmastrac
mmastrac8mo ago
@Cherry 🍒 make sure that you are using tokio's current_thread executor -- Deno only supports that particular threading model because of threading limitations in deno_core/v8. Can you share your tokio runtime builder expression?
Cherry 😈
Cherry 😈8mo ago
My code is pretty much verbatim from the examples (other than a custom module loader). I'll try out the current thread executor then!
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

mod module_loader;

use std::path::Path;
use std::rc::Rc;

use deno_runtime::deno_core::error::AnyError;
use deno_runtime::deno_core::op2;
use deno_runtime::deno_core::ModuleSpecifier;
use deno_runtime::permissions::PermissionsContainer;
use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions;

deno_runtime::deno_core::extension!(hello_runtime, ops = [op_hello]);

#[op2(fast)]
fn op_hello(#[string] text: &str) {
println!("Hello {}!", text);
}

#[tokio::main]
async fn main() -> Result<(), AnyError> {
let client = reqwest::Client::new();

let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("main.js");
let main_module = ModuleSpecifier::from_file_path(js_path).unwrap();
let mut worker = MainWorker::bootstrap_from_options(
main_module.clone(),
PermissionsContainer::allow_all(),
WorkerOptions {
module_loader: Rc::new(module_loader::TypescriptModuleLoader::new(client)),
extensions: vec![hello_runtime::init_ops()],
..Default::default()
},
);
worker.execute_main_module(&main_module).await?;
worker.run_event_loop(false).await?;
Ok(())
}
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

mod module_loader;

use std::path::Path;
use std::rc::Rc;

use deno_runtime::deno_core::error::AnyError;
use deno_runtime::deno_core::op2;
use deno_runtime::deno_core::ModuleSpecifier;
use deno_runtime::permissions::PermissionsContainer;
use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions;

deno_runtime::deno_core::extension!(hello_runtime, ops = [op_hello]);

#[op2(fast)]
fn op_hello(#[string] text: &str) {
println!("Hello {}!", text);
}

#[tokio::main]
async fn main() -> Result<(), AnyError> {
let client = reqwest::Client::new();

let js_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("main.js");
let main_module = ModuleSpecifier::from_file_path(js_path).unwrap();
let mut worker = MainWorker::bootstrap_from_options(
main_module.clone(),
PermissionsContainer::allow_all(),
WorkerOptions {
module_loader: Rc::new(module_loader::TypescriptModuleLoader::new(client)),
extensions: vec![hello_runtime::init_ops()],
..Default::default()
},
);
worker.execute_main_module(&main_module).await?;
worker.run_event_loop(false).await?;
Ok(())
}
mmastrac
mmastrac8mo ago
Interesting. We should add tokio::main to those examples -- you'll need to use this:
#[tokio::main(flavor = "current_thread")]
#[tokio::main(flavor = "current_thread")]
That should Just Work
Cherry 😈
Cherry 😈8mo ago
Hey, nice. It worked as you said, thanks a lot!
jeff.hykin
jeff.hykin8mo ago
@Cherry 🍒 this is something I tried a while ago but ended up stopping short. if you have a repo with an example setup I'd love to check it out!
Cherry 😈
Cherry 😈8mo ago
You can follow the official deno example, but with current_thread runtime https://github.com/denoland/deno/blob/main/runtime/examples/extension_with_ops/main.rs It'll work out of the box
jeff.hykin
jeff.hykin8mo ago
Oh cool! Thanks!