use std::sync::{Arc, Mutex};
use typescript_types::{TsError, TsValue};
struct ObjectPool<T> {
pool: Mutex<Vec<T>>,
max_size: usize,
}
impl<T> ObjectPool<T> {
pub fn new(max_size: usize) -> Self {
Self { pool: Mutex::new(Vec::with_capacity(max_size)), max_size }
}
pub fn get<F>(&self, default: F) -> T
where
F: Fn() -> T,
{
let mut pool = self.pool.lock().unwrap();
pool.pop().unwrap_or_else(default)
}
pub fn put(&self, item: T) {
let mut pool = self.pool.lock().unwrap();
if pool.len() < self.max_size {
pool.push(item);
}
}
}
thread_local! {
pub static TS_VALUE_ARRAY_POOL: ObjectPool<Vec<TsValue>> = ObjectPool::new(100);
pub static TS_VALUE_OBJECT_POOL: ObjectPool<std::collections::HashMap<String, TsValue>> = ObjectPool::new(100);
pub static TS_VALUE_MAP_POOL: ObjectPool<Vec<(TsValue, TsValue)>> = ObjectPool::new(100);
}
pub mod napi;
pub use napi::{
LoadedNapiModule, NapiEnv, NapiExport, NapiFunction, NapiModule, NapiModuleLoader, NapiStatus, NapiValue, NapiValueType,
check_napi_status, create_napi_error, napi_to_ts_value, ts_value_to_napi,
};
pub mod benchmark;
pub use benchmark::{run_ffi_benchmark, run_napi_benchmark};
pub type FfiFunction = Arc<dyn for<'a> Fn(&'a [TsValue]) -> Result<TsValue, TsError> + Send + Sync>;
pub struct FfiModule {
functions: std::collections::HashMap<String, FfiFunction>,
name: String,
}
impl FfiModule {
pub fn new(name: &str) -> Self {
Self { functions: std::collections::HashMap::new(), name: name.to_string() }
}
pub fn add_function(&mut self, name: &str, func: FfiFunction) {
self.functions.insert(name.to_string(), func);
}
pub fn register_function<F>(&mut self, name: &str, func: F)
where
F: Fn(&[TsValue]) -> Result<TsValue, TsError> + Send + Sync + 'static,
{
self.functions.insert(name.to_string(), Arc::new(func));
}
pub fn call_function(&self, name: &str, args: &[TsValue]) -> Result<TsValue, TsError> {
if let Some(func) = self.functions.get(name) {
func(args)
}
else {
Err(TsError::ReferenceError(format!("FFI function '{}' not found in module '{}'", name, self.name)))
}
}
pub fn has_function(&self, name: &str) -> bool {
self.functions.contains_key(name)
}
pub fn name(&self) -> &str {
&self.name
}
pub fn get_functions(&self) -> Vec<String> {
self.functions.keys().cloned().collect()
}
}
pub struct FfiManager {
modules: std::collections::HashMap<String, FfiModule>,
napi_loader: NapiModuleLoader,
}
impl FfiManager {
pub fn new() -> Self {
Self { modules: std::collections::HashMap::new(), napi_loader: NapiModuleLoader::new() }
}
pub fn load_napi_module(&mut self, name: &str, path: &str) -> Result<&LoadedNapiModule, TsError> {
self.napi_loader.load_module(name, path)
}
pub fn get_napi_module(&self, name: &str) -> Option<&LoadedNapiModule> {
self.napi_loader.get_module(name)
}
pub fn has_napi_module(&self, name: &str) -> bool {
self.napi_loader.has_module(name)
}
pub fn unload_napi_module(&mut self, name: &str) -> Result<(), TsError> {
self.napi_loader.unload_module(name)
}
pub fn get_napi_module_names(&self) -> Vec<&String> {
self.napi_loader.module_names()
}
pub fn add_module(&mut self, name: &str, module: FfiModule) {
self.modules.insert(name.to_string(), module);
}
pub fn register_module(&mut self, module: FfiModule) {
let name = module.name().to_string();
self.modules.insert(name, module);
}
pub fn get_module(&self, name: &str) -> Option<&FfiModule> {
self.modules.get(name)
}
pub fn call_function(&self, module_name: &str, function_name: &str, args: &[TsValue]) -> Result<TsValue, TsError> {
if let Some(module) = self.modules.get(module_name) {
module.call_function(function_name, args)
}
else {
Err(TsError::ReferenceError(format!("FFI module '{}' not found", module_name)))
}
}
pub fn register_std_functions(&mut self) {
let mut std_module = FfiModule::new("std");
std_module.add_function(
"print",
Arc::new(|args| {
for arg in args {
print!("{}", arg.to_string());
}
Ok(TsValue::Undefined)
}),
);
std_module.add_function(
"println",
Arc::new(|args| {
for arg in args {
println!("{}", arg.to_string());
}
Ok(TsValue::Undefined)
}),
);
std_module.add_function(
"read_line",
Arc::new(|_| {
let mut input = String::new();
match std::io::stdin().read_line(&mut input) {
Ok(_) => Ok(TsValue::String(input.trim_end().to_string())),
Err(e) => Err(TsError::Other(format!("Failed to read line: {}", e))),
}
}),
);
std_module.add_function(
"exit",
Arc::new(|args| {
if let Some(TsValue::Number(code)) = args.get(0) {
std::process::exit(*code as i32);
}
else {
std::process::exit(0);
}
}),
);
std_module.add_function(
"throw_error",
Arc::new(|args| {
if let Some(TsValue::String(msg)) = args.get(0) {
Err(TsError::Other(msg.clone()))
}
else {
Err(TsError::TypeError("Expected error message".to_string()))
}
}),
);
self.add_module("std", std_module);
}
pub fn get_modules(&self) -> Vec<String> {
self.modules.keys().cloned().collect()
}
pub fn remove_module(&mut self, name: &str) {
self.modules.remove(name);
}
pub fn register_function<F>(&mut self, name: &str, func: F)
where
F: for<'a> Fn(&'a [TsValue]) -> TsValue + Send + Sync + 'static,
{
let default_module = self.modules.entry("default".to_string()).or_insert_with(|| FfiModule::new("default"));
let wrapped_func: FfiFunction = Arc::new(move |args: &[TsValue]| Ok(func(args)));
default_module.add_function(name, wrapped_func);
}
}
pub trait FromRust<T> {
fn from_rust(value: T) -> TsValue;
}
pub trait ToRust<T> {
fn to_rust(value: &TsValue) -> Result<T, TsError>;
}
impl FromRust<bool> for TsValue {
fn from_rust(value: bool) -> TsValue {
TsValue::Boolean(value)
}
}
impl ToRust<bool> for TsValue {
fn to_rust(value: &TsValue) -> Result<bool, TsError> {
match value {
TsValue::Boolean(b) => Ok(*b),
_ => Err(TsError::TypeError("Expected boolean".to_string())),
}
}
}
impl FromRust<f64> for TsValue {
fn from_rust(value: f64) -> TsValue {
TsValue::Number(value)
}
}
impl ToRust<f64> for TsValue {
fn to_rust(value: &TsValue) -> Result<f64, TsError> {
Ok(value.to_number())
}
}
impl FromRust<String> for TsValue {
fn from_rust(value: String) -> TsValue {
TsValue::String(value)
}
}
impl ToRust<String> for TsValue {
fn to_rust(value: &TsValue) -> Result<String, TsError> {
Ok(value.to_string())
}
}
impl FromRust<&str> for TsValue {
fn from_rust(value: &str) -> TsValue {
TsValue::String(value.to_string())
}
}
impl FromRust<Vec<TsValue>> for TsValue {
fn from_rust(value: Vec<TsValue>) -> TsValue {
TsValue::Array(value)
}
}
impl ToRust<Vec<TsValue>> for TsValue {
fn to_rust(value: &TsValue) -> Result<Vec<TsValue>, TsError> {
match value {
TsValue::Array(arr) => Ok(arr.clone()),
_ => Err(TsError::TypeError("Expected array".to_string())),
}
}
}
impl FromRust<Vec<(String, TsValue)>> for TsValue {
fn from_rust(value: Vec<(String, TsValue)>) -> TsValue {
TsValue::Object(value.into_iter().collect())
}
}
impl ToRust<Vec<(String, TsValue)>> for TsValue {
fn to_rust(value: &TsValue) -> Result<Vec<(String, TsValue)>, TsError> {
match value {
TsValue::Object(props) => Ok(props.iter().map(|(k, v)| (k.clone(), v.clone())).collect()),
_ => Err(TsError::TypeError("Expected object".to_string())),
}
}
}
impl FromRust<Vec<(TsValue, TsValue)>> for TsValue {
fn from_rust(value: Vec<(TsValue, TsValue)>) -> TsValue {
TsValue::Map(value)
}
}
impl ToRust<Vec<(TsValue, TsValue)>> for TsValue {
fn to_rust(value: &TsValue) -> Result<Vec<(TsValue, TsValue)>, TsError> {
match value {
TsValue::Map(entries) => Ok(entries.to_vec()),
_ => Err(TsError::TypeError("Expected map".to_string())),
}
}
}
impl FromRust<i128> for TsValue {
fn from_rust(value: i128) -> TsValue {
TsValue::BigInt(value)
}
}
impl ToRust<i128> for TsValue {
fn to_rust(value: &TsValue) -> Result<i128, TsError> {
match value {
TsValue::BigInt(bi) => Ok(*bi),
TsValue::Number(n) => Ok(*n as i128),
_ => Err(TsError::TypeError("Expected bigint or number".to_string())),
}
}
}
impl FromRust<i64> for TsValue {
fn from_rust(value: i64) -> TsValue {
TsValue::Date(value)
}
}
impl ToRust<i64> for TsValue {
fn to_rust(value: &TsValue) -> Result<i64, TsError> {
match value {
TsValue::Date(d) => Ok(*d),
TsValue::Number(n) => Ok(*n as i64),
_ => Err(TsError::TypeError("Expected date or number".to_string())),
}
}
}
impl FromRust<Option<TsValue>> for TsValue {
fn from_rust(value: Option<TsValue>) -> TsValue {
value.unwrap_or(TsValue::Null)
}
}
impl ToRust<Option<TsValue>> for TsValue {
fn to_rust(value: &TsValue) -> Result<Option<TsValue>, TsError> {
match value {
TsValue::Null | TsValue::Undefined => Ok(None),
_ => Ok(Some(value.clone())),
}
}
}
impl FromRust<Result<TsValue, TsError>> for TsValue {
fn from_rust(value: Result<TsValue, TsError>) -> TsValue {
match value {
Ok(v) => v,
Err(e) => TsValue::Error(e.to_string()),
}
}
}
impl ToRust<Result<TsValue, TsError>> for TsValue {
fn to_rust(value: &TsValue) -> Result<Result<TsValue, TsError>, TsError> {
match value {
TsValue::Error(msg) => Ok(Err(TsError::Other(msg.clone()))),
_ => Ok(Ok(value.clone())),
}
}
}
pub fn get_object_property(obj: &TsValue, key: &str) -> Result<TsValue, TsError> {
match obj {
TsValue::Object(props) => {
if let Some(value) = props.get(key) {
Ok(value.clone())
}
else {
Err(TsError::ReferenceError(format!("Property '{}' not found", key)))
}
}
_ => Err(TsError::TypeError("Expected object".to_string())),
}
}
pub fn set_object_property(obj: &TsValue, key: &str, value: TsValue) -> Result<TsValue, TsError> {
match obj {
TsValue::Object(props) => {
let mut new_props = props.clone();
new_props.insert(key.to_string(), value);
Ok(TsValue::Object(new_props))
}
_ => Err(TsError::TypeError("Expected object".to_string())),
}
}
pub fn get_array_element(arr: &TsValue, index: usize) -> Result<TsValue, TsError> {
match arr {
TsValue::Array(values) => {
if index < values.len() {
Ok(values[index].clone())
}
else {
Err(TsError::RangeError(format!("Index {} out of bounds", index)))
}
}
_ => Err(TsError::TypeError("Expected array".to_string())),
}
}
pub fn set_array_element(arr: &TsValue, index: usize, value: TsValue) -> Result<TsValue, TsError> {
match arr {
TsValue::Array(values) => {
if index < values.len() {
let mut new_values = values.clone();
new_values[index] = value;
Ok(TsValue::Array(new_values))
}
else {
Err(TsError::RangeError(format!("Index {} out of bounds", index)))
}
}
_ => Err(TsError::TypeError("Expected array".to_string())),
}
}