use std::path::Path;
use deno_core::ModuleSpecifier;
use crate::{traits::ToModuleSpecifier, Error, Module, ModuleWrapper, Runtime, RuntimeOptions};
pub fn evaluate<T>(javascript: &str) -> Result<T, Error>
where
T: deno_core::serde::de::DeserializeOwned,
{
let mut runtime = Runtime::new(RuntimeOptions::default())?;
runtime.eval(javascript)
}
pub fn validate(javascript: &str) -> Result<bool, Error> {
let module = Module::new("test.js", javascript);
let mut runtime = Runtime::new(RuntimeOptions::default())?;
match runtime.load_modules(&module, vec![]) {
Ok(_) => Ok(true),
Err(Error::Runtime(_) | Error::JsError(_)) => Ok(false),
Err(e) => Err(e),
}
}
pub fn import(path: &str) -> Result<ModuleWrapper, Error> {
ModuleWrapper::new_from_file(path, RuntimeOptions::default())
}
pub fn resolve_path(
path: impl AsRef<std::path::Path>,
base_dir: Option<&Path>,
) -> Result<ModuleSpecifier, Error> {
let path = path.as_ref();
let url = match base_dir {
Some(dir) => path.to_module_specifier(dir),
None => path.to_module_specifier(&std::env::current_dir()?),
}?;
Ok(url)
}
pub fn init_platform(thread_pool_size: u32, idle_task_support: bool) {
let platform = deno_core::v8::Platform::new(thread_pool_size, idle_task_support);
deno_core::JsRuntime::init_platform(Some(platform.into()), true);
}
#[macro_use]
mod runtime_macros {
#[macro_export]
macro_rules! json_args {
($($arg:expr),*) => {
&($($arg),*)
};
}
#[macro_export]
macro_rules! big_json_args {
($($arg:expr),*) => {
&vec![
$($crate::deno_core::serde_json::Value::from($arg)),*
]
};
}
#[macro_export]
macro_rules! sync_callback {
(|$($arg:ident: $arg_ty:ty),*| $body:expr) => {
|args: &[$crate::serde_json::Value]| {
let mut args = args.iter();
$(
let $arg: $arg_ty = match args.next() {
Some(arg) => $crate::serde_json::from_value(arg.clone())?,
None => return Err($crate::Error::Runtime("Invalid number of arguments".to_string())),
};
)*
let result = $body?;
$crate::serde_json::Value::try_from(result).map_err(|e| $crate::Error::Runtime(e.to_string()))
}
}
}
#[macro_export]
macro_rules! async_callback {
(|$($arg:ident: $arg_ty:ty),*| $body:expr) => {
|args: Vec<$crate::serde_json::Value>| Box::pin(async move {
let mut args = args.iter();
$(
let $arg: $arg_ty = match args.next() {
Some(arg) => $crate::serde_json::from_value(arg.clone()).map_err(|e| $crate::Error::Runtime(e.to_string()))?,
None => return Err($crate::Error::Runtime("Invalid number of arguments".to_string())),
};
)*
let result = $body.await?;
$crate::serde_json::Value::try_from(result).map_err(|e| $crate::Error::Runtime(e.to_string()))
})
}
}
}
#[cfg(test)]
mod test_runtime {
use super::*;
use deno_core::{futures::FutureExt, serde_json};
#[test]
fn test_callback() {
let add = sync_callback!(|a: i64, b: i64| { Ok::<i64, Error>(a + b) });
let add2 = async_callback!(|a: i64, b: i64| async move { Ok::<i64, Error>(a + b) });
let args = vec![
serde_json::Value::Number(5.into()),
serde_json::Value::Number(5.into()),
];
let result = add(&args).unwrap();
assert_eq!(serde_json::Value::Number(10.into()), result);
let result = add2(args).now_or_never().unwrap().unwrap();
assert_eq!(serde_json::Value::Number(10.into()), result);
}
#[test]
fn test_evaluate() {
assert_eq!(5, evaluate::<i64>("3 + 2").expect("invalid expression"));
evaluate::<i64>("a5; 3 + 2").expect_err("Expected an error");
}
#[test]
fn test_validate() {
assert!(validate("3 + 2").expect("invalid expression"));
assert!(!validate("5;+-").expect("invalid expression"));
}
#[test]
fn test_resolve_path() {
assert!(resolve_path("test.js", None)
.expect("invalid path")
.to_string()
.ends_with("test.js"));
}
}