pub use deno_core::error::custom_error;
pub use deno_core::error::AnyError;
pub use deno_core::serde_json::{json, value::Value};
use deno_core::{op_sync, resolve_path, FsModuleLoader, JsRuntime, OpFn, OpState};
pub use import_fn::import_fn;
use std::rc::Rc;
fn get_args(value: &Value) -> Vec<Value> {
if let Value::Object(map) = &value {
if let Some(Value::Array(args)) = &map.get("args") {
return args.to_owned();
}
}
unreachable!();
}
pub struct ImportedFn {
op_fn: Box<OpFn>,
name: String,
scope: Option<String>,
}
pub fn create_sync_fn<F>(imported_fn: F, name: &str, scope: Option<String>) -> ImportedFn
where
F: Fn(Vec<Value>) -> Result<Value, AnyError> + 'static,
{
let op_fn = op_sync(
move |_state: &mut OpState, value: Value, _: ()| -> Result<Value, AnyError> {
imported_fn(get_args(&value))
},
);
ImportedFn {
op_fn,
name: name.to_string(),
scope,
}
}
struct ImportedName {
name: String,
scope: Option<String>,
}
pub struct Runtime {
runtime: JsRuntime,
imported_names: Vec<ImportedName>,
scopes: Vec<String>,
}
impl Default for Runtime {
fn default() -> Self {
let runtime = JsRuntime::new(deno_core::RuntimeOptions {
module_loader: Some(Rc::new(FsModuleLoader)),
..Default::default()
});
let imported_names = vec![];
let scopes = vec![];
Runtime {
runtime,
imported_names,
scopes,
}
}
}
impl Runtime {
pub fn new() -> Self {
Default::default()
}
pub fn import<F>(&mut self, imported_fn: F)
where
F: Fn() -> ImportedFn,
{
let import_fn = imported_fn();
if let Some(scope) = &import_fn.scope {
self.scopes.push(scope.clone());
}
self.runtime.register_op(&import_fn.name, import_fn.op_fn);
self.imported_names.push(ImportedName {
name: import_fn.name,
scope: import_fn.scope,
});
}
pub fn importing_finished(&mut self) {
let mut scope_definitions = String::new();
for scope in self.scopes.iter() {
scope_definitions.push_str(&format!(" window[{:?}] = {{}};\n", scope));
}
let mut name_definitions = String::new();
for import in &self.imported_names {
let scope = match &import.scope {
Some(scope) => format!("window[{:?}][{:?}]", scope, import.name),
None => format!("window[{:?}]", import.name),
};
name_definitions.push_str(&format!(
" {} = (...args) => Deno.core.opSync({:?}, {{args}});\n",
scope, import.name
));
}
let js_source = format!(
"\"use strict\";\n((window) => {{\n{}{}}})(this);",
scope_definitions, name_definitions,
);
self.runtime.sync_ops_cache();
self.runtime
.execute_script("rust:core.js", &js_source)
.expect("runtime exporting");
}
pub async fn load_module(&mut self, path_str: &str) -> Result<(), AnyError> {
let specifier = resolve_path(path_str)?;
let id = self.runtime.load_main_module(&specifier, None).await?;
let result = self.runtime.mod_evaluate(id);
self.runtime.run_event_loop(false).await?;
result.await?
}
}
#[macro_export]
macro_rules! runtime {
($($fn:ident),* $(,)?) => {
{
let mut runtime = crabzilla::Runtime::new();
$(
runtime.import($fn);
)*
runtime.importing_finished();
runtime
}
}
}
#[macro_export]
macro_rules! throw {
($message:expr) => {
return Err(crabzilla::custom_error("Error", $message));
};
}