#![doc = include_str!("../README.md")]
#![allow(clippy::needless_doctest_main)]
use emscripten_val_sys::sys;
use std::ffi::{CStr, CString};
mod externs;
mod impls;
use crate::externs::*;
#[allow(non_camel_case_types)]
pub type EM_VAL = sys::EM_VAL;
#[macro_export]
macro_rules! argv {
($($rest:expr),*) => {{
&[$(&Val::from($rest)),*]
}};
}
#[repr(C)]
#[derive(Eq)]
pub struct Val {
handle: EM_VAL,
}
impl Val {
pub fn global(name: &str) -> Self {
let name = CString::new(name).unwrap();
Self {
handle: unsafe { sys::_emval_get_global(name.as_ptr()) },
}
}
pub fn take_ownership(v: sys::EM_VAL) -> Self {
Self { handle: v }
}
pub fn from_val(v: &Val) -> Self {
let handle = v.as_handle();
if v.uses_ref_count() {
unsafe {
sys::_emval_incref(handle);
}
}
Self { handle }
}
pub fn undefined() -> Self {
Self {
handle: sys::_EMVAL_UNDEFINED as EM_VAL,
}
}
pub fn object() -> Self {
Self {
handle: unsafe { sys::_emval_new_object() },
}
}
pub fn null() -> Self {
Self {
handle: sys::_EMVAL_NULL as EM_VAL,
}
}
pub fn array() -> Self {
Self {
handle: unsafe { sys::_emval_new_array() },
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Self {
let s = CString::new(s).unwrap();
Self {
handle: unsafe { sys::_emval_new_cstring(s.as_ptr() as _) },
}
}
pub fn module_property(s: &str) -> Self {
let s = CString::new(s).unwrap();
Self {
handle: unsafe { sys::_emval_get_module_property(s.as_ptr() as _) },
}
}
pub fn from_array<T: Clone + Into<Val>>(arr: &[T]) -> Self {
let v = Val::array();
for elem in arr {
v.call("push", argv![elem.clone().into()]);
}
v
}
pub fn as_handle(&self) -> EM_VAL {
self.handle
}
pub fn call(&self, f: &str, args: &[&Val]) -> Val {
unsafe {
let typeids = vec![sys::EmvalType; args.len() + 1];
let f = CString::new(f).unwrap();
let caller =
sys::_emval_get_method_caller(typeids.len() as u32, typeids.as_ptr() as _, 0);
for arg in args {
sys::_emval_incref(arg.handle);
}
let ret = sys::_emval_call_method(
caller,
self.handle,
f.as_ptr() as _,
std::ptr::null_mut(),
*(args.as_ptr() as *const *const ()) as _,
);
let ret = ret as u32 as EM_VAL;
Val::take_ownership(ret)
}
}
pub fn get<T: Clone + Into<Val>>(&self, prop: &T) -> Val {
let prop: Val = prop.clone().into();
Val {
handle: unsafe { sys::_emval_get_property(self.handle, prop.handle) },
}
}
pub fn set<T: Clone + Into<Val>, U: Clone + Into<Val>>(&self, prop: &T, val: &U) {
let prop: Val = prop.clone().into();
let val: Val = val.clone().into();
unsafe { sys::_emval_set_property(self.handle, prop.handle, val.handle) };
}
pub fn from_i32(i: i32) -> Self {
Self {
handle: unsafe { sys::_emval_take_value(sys::IntType, [i as *const ()].as_ptr() as _) },
}
}
pub fn from_u32(i: u32) -> Self {
Self {
handle: unsafe { sys::_emval_take_value(sys::IntType, [i as *const ()].as_ptr() as _) },
}
}
pub fn from_f32(i: f32) -> Self {
let i = i as i32 as *const ();
Self {
handle: unsafe { sys::_emval_take_value(sys::FloatType, [i].as_ptr() as _) },
}
}
pub fn from_f64(i: f64) -> Self {
let i = i as f32 as i32 as *const ();
Self {
handle: unsafe { sys::_emval_take_value(sys::FloatType, [i].as_ptr() as _) },
}
}
pub fn from_bool(i: bool) -> Self {
Self {
handle: if i {
sys::_EMVAL_TRUE as EM_VAL
} else {
sys::_EMVAL_FALSE as EM_VAL
},
}
}
fn uses_ref_count(&self) -> bool {
self.handle > sys::_EMVAL_LAST_RESERVED_HANDLE as EM_VAL
}
pub fn release_ownership(&mut self) -> EM_VAL {
let h = self.handle;
self.handle = std::ptr::null_mut();
h
}
pub fn has_own_property(&self, key: &str) -> bool {
Val::global("Object")
.get(&"prototype")
.get(&"hasOwnProperty")
.call("call", argv![self.clone(), key])
.as_bool()
}
pub fn as_f64(&self) -> f64 {
unsafe { sys::_emval_as(self.handle, sys::FloatType, std::ptr::null_mut()) }
}
pub fn as_f32(&self) -> f32 {
unsafe { sys::_emval_as(self.handle, sys::FloatType, std::ptr::null_mut()) as f32 }
}
pub fn as_i32(&self) -> i32 {
unsafe { sys::_emval_as(self.handle, sys::IntType, std::ptr::null_mut()) as i32 }
}
pub fn as_u32(&self) -> u32 {
unsafe { sys::_emval_as(self.handle, sys::IntType, std::ptr::null_mut()) as u32 }
}
pub fn as_bool(&self) -> bool {
unsafe { sys::_emval_as(self.handle, sys::BoolType, std::ptr::null_mut()) as i32 != 0 }
}
pub fn as_string(&self) -> String {
unsafe {
let ptr = _emval_as_str(self.handle);
let ret = CStr::from_ptr(ptr).to_string_lossy().to_string();
free(ptr as _);
ret
}
}
pub fn is_null(&self) -> bool {
self.handle == sys::_EMVAL_NULL as EM_VAL
}
pub fn is_undefined(&self) -> bool {
self.handle == sys::_EMVAL_UNDEFINED as EM_VAL
}
pub fn is_true(&self) -> bool {
self.handle == sys::_EMVAL_TRUE as EM_VAL
}
pub fn is_false(&self) -> bool {
self.handle == sys::_EMVAL_FALSE as EM_VAL
}
pub fn is_number(&self) -> bool {
unsafe { sys::_emval_is_number(self.handle) }
}
pub fn is_string(&self) -> bool {
unsafe { sys::_emval_is_string(self.handle) }
}
pub fn instanceof(&self, v: &Val) -> bool {
unsafe { sys::_emval_instanceof(self.as_handle(), v.as_handle()) }
}
pub fn is_array(&self) -> bool {
self.instanceof(&Val::global("Array"))
}
pub fn is_in(&self, v: &Val) -> bool {
unsafe { sys::_emval_in(self.as_handle(), v.as_handle()) }
}
pub fn type_of(&self) -> Val {
Val {
handle: unsafe { sys::_emval_typeof(self.handle) },
}
}
pub fn throw(&self) -> bool {
unsafe { sys::_emval_throw(self.as_handle()) }
}
pub fn await_(&self) -> Val {
Val {
handle: unsafe { sys::_emval_await(self.handle) },
}
}
pub fn delete<T: Clone + Into<Val>>(&self, prop: &T) -> bool {
unsafe { sys::_emval_delete(self.as_handle(), prop.clone().into().as_handle()) }
}
pub fn new(&self, args: &[&Val]) -> Val {
unsafe {
let typeids = vec![sys::EmvalType; args.len() + 1];
let caller =
sys::_emval_get_method_caller(typeids.len() as u32, typeids.as_ptr() as _, 1);
for arg in args {
sys::_emval_incref(arg.handle);
}
let ret = sys::_emval_call(
caller,
self.handle,
std::ptr::null_mut(),
*(args.as_ptr() as *const *const ()) as _,
);
let ret = ret as u32 as EM_VAL;
Val::take_ownership(ret)
}
}
fn gt<T: Clone + Into<Val>>(&self, v: &T) -> bool {
unsafe { sys::_emval_greater_than(self.handle, v.clone().into().handle) }
}
fn lt<T: Clone + Into<Val>>(&self, v: &T) -> bool {
unsafe { sys::_emval_less_than(self.handle, v.clone().into().handle) }
}
fn equals<T: Clone + Into<Val>>(&self, v: &T) -> bool {
unsafe { sys::_emval_equals(self.handle, v.clone().into().handle) }
}
pub fn strictly_equals<T: Clone + Into<Val>>(&self, v: &T) -> bool {
unsafe { sys::_emval_strictly_equals(self.handle, v.clone().into().handle) }
}
pub fn not(&self) -> bool {
unsafe { sys::_emval_not(self.handle) }
}
pub fn add_event_listener<F: (FnMut(&Val) -> Val) + 'static>(&self, ev: &str, f: F) {
unsafe {
let a: *mut Box<dyn FnMut(&Val) -> Val> = Box::into_raw(Box::new(Box::new(f)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
let ev = CString::new(ev).unwrap();
_emval_add_event_listener(self.handle, ev.as_ptr() as _, data as _);
}
}
pub fn from_fn0<F: (FnMut() -> Val) + 'static>(f: F) -> Val {
unsafe {
let a: *mut Box<dyn FnMut() -> Val> = Box::into_raw(Box::new(Box::new(f)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
Self {
handle: _emval_take_fn(0, data as _),
}
}
}
pub fn from_fn1<F: (FnMut(&Val) -> Val) + 'static>(f: F) -> Val {
unsafe {
#[allow(clippy::type_complexity)]
let a: *mut Box<dyn FnMut(&Val) -> Val> = Box::into_raw(Box::new(Box::new(f)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
Self {
handle: _emval_take_fn(1, data as _),
}
}
}
pub fn from_fn2<F: (FnMut(&Val, &Val) -> Val) + 'static>(f: F) -> Val {
unsafe {
#[allow(clippy::type_complexity)]
let a: *mut Box<dyn FnMut(&Val, &Val) -> Val> = Box::into_raw(Box::new(Box::new(f)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
Self {
handle: _emval_take_fn(2, data as _),
}
}
}
pub fn from_fn3<F: (FnMut(&Val, &Val, &Val) -> Val) + 'static>(f: F) -> Val {
unsafe {
#[allow(clippy::type_complexity)]
let a: *mut Box<dyn FnMut(&Val, &Val, &Val) -> Val> =
Box::into_raw(Box::new(Box::new(f)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
Self {
handle: _emval_take_fn(3, data as _),
}
}
}
pub fn from_fn4<F: (FnMut(&Val, &Val, &Val, &Val) -> Val) + 'static>(f: F) -> Val {
unsafe {
#[allow(clippy::type_complexity)]
let a: *mut Box<dyn FnMut(&Val, &Val, &Val, &Val) -> Val> =
Box::into_raw(Box::new(Box::new(f)));
let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
Self {
handle: _emval_take_fn(4, data as _),
}
}
}
pub fn to_vec<T: Clone + From<Val>>(&self) -> Vec<T> {
let len = self.get(&"length").as_u32();
let mut v: Vec<T> = vec![];
for i in 0..len {
v.push(T::from(self.get(&i)))
}
v
}
}