use crate::{JSContext, JSContextImpl, JSResult, RongJSError};
use std::fmt;
use std::hash::Hash;
mod convert;
pub use convert::*;
mod error;
pub use error::*;
mod exception;
pub use exception::*;
mod valuetype;
pub use valuetype::{JSTypeOf, JSValueType};
mod object;
pub use object::*;
mod array;
pub use array::*;
mod array_buffer;
pub use array_buffer::*;
mod typed_array;
pub use typed_array::*;
mod function;
pub use function::*;
mod symbol;
pub use symbol::*;
mod date;
pub use date::*;
pub trait JSValueImpl: Clone + PartialEq + Hash {
type RawValue: Copy;
type Context: JSContextImpl<Value = Self>;
fn from_borrowed_raw(
ctx: <Self::Context as JSContextImpl>::RawContext,
value: Self::RawValue,
) -> Self;
fn from_owned_raw(
ctx: <Self::Context as JSContextImpl>::RawContext,
value: Self::RawValue,
) -> Self;
fn into_raw_value(self) -> Self::RawValue;
fn as_raw_value(&self) -> &Self::RawValue;
fn as_raw_context(&self) -> &<Self::Context as JSContextImpl>::RawContext;
fn create_null(ctx: &Self::Context) -> Self;
fn create_undefined(ctx: &Self::Context) -> Self;
fn create_symbol(ctx: &Self::Context, descripiton: &str) -> Self;
fn from_json_str(ctx: &Self::Context, str: &str) -> Self;
fn create_date(ctx: &Self::Context, epoch_ms: f64) -> Self;
}
pub struct JSValue<V: JSValueImpl> {
inner: V,
}
impl<V: JSValueImpl> Clone for JSValue<V> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<V: JSValueImpl> PartialEq for JSValue<V> {
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
}
impl<V: JSValueImpl> Eq for JSValue<V> {}
impl<V: JSValueImpl> Hash for JSValue<V> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<V> JSValue<V>
where
V: JSValueImpl,
{
pub fn from_raw(_ctx: &JSContext<V::Context>, value: V) -> Self {
Self { inner: value }
}
pub fn as_value(&self) -> &V {
&self.inner
}
pub fn into_value(self) -> V {
self.inner
}
pub fn get_ctx(&self) -> JSContext<V::Context> {
JSContext::from_borrowed_raw_ptr(self.as_value().as_raw_context())
}
pub fn from<T>(ctx: &JSContext<V::Context>, val: T) -> Self
where
T: IntoJSValue<V>,
{
<T as IntoJSValue<V>>::into_js_value(val, ctx)
}
pub fn try_into<T>(self) -> JSResult<T>
where
T: FromJSValue<V>,
{
let ctx = self.get_ctx();
T::from_js_value(&ctx, self)
}
pub fn undefined(ctx: &JSContext<V::Context>) -> Self {
let value = V::create_undefined(ctx.as_ref());
JSValue::from_raw(ctx, value)
}
pub fn null(ctx: &JSContext<V::Context>) -> Self {
let value = V::create_null(ctx.as_ref());
JSValue::from_raw(ctx, value)
}
}
impl<V> JSValue<V>
where
V: JSValueImpl + JSTypeOf,
{
pub fn into_object(self) -> Option<JSObject<V>> {
self.take_is_object().map(|v| v.into())
}
}
impl<V> FromJSValue<V> for JSValue<V>
where
V: JSValueImpl,
{
fn from_js_value(_ctx: &JSContext<V::Context>, value: JSValue<V>) -> JSResult<Self> {
Ok(value)
}
}
impl<V> IntoJSValue<V> for JSValue<V>
where
V: JSValueImpl,
{
fn into_js_value(self, _ctx: &JSContext<V::Context>) -> JSValue<V> {
self
}
}
pub trait JsonToJSValue<V>
where
V: JSValueImpl,
{
fn json_to_js_value(self, ctx: &JSContext<V::Context>) -> JSResult<JSValue<V>>;
}
impl<V> JsonToJSValue<V> for &str
where
V: JSObjectOps + JSTypeOf,
{
fn json_to_js_value(self, ctx: &JSContext<V::Context>) -> JSResult<JSValue<V>> {
let result = V::from_json_str(ctx.as_ref(), self);
result.try_map(|v| JSValue::from_raw(ctx, v))
}
}
pub trait JSValueMapper<V: JSValueImpl> {
fn try_convert<T>(self) -> JSResult<T>
where
T: FromJSValue<V>;
fn try_map<T, F>(self, f: F) -> JSResult<T>
where
F: FnOnce(Self) -> T,
Self: Sized;
}
impl<V> JSValueMapper<V> for V
where
V: JSTypeOf,
V: JSObjectOps,
{
fn try_convert<T>(self) -> JSResult<T>
where
T: FromJSValue<V>,
{
self.try_map(|value| {
let ctx = JSContext::from_borrowed_raw_ptr(value.as_raw_context());
T::from_js_value(&ctx, JSValue::from_raw(&ctx, value))
})?
}
fn try_map<T, F>(self, f: F) -> JSResult<T>
where
F: FnOnce(Self) -> T,
{
if self.is_exception() {
let ctx: JSContext<V::Context> =
JSContext::from_borrowed_raw_ptr(self.as_raw_context());
Err(RongJSError::from_thrown_value(JSValue::from_raw(
&ctx, self,
)))
} else {
Ok(f(self))
}
}
}
#[macro_export]
macro_rules! impl_js_converter {
($target:ty, $in_type:ty, $out_type:ty, $create_fn:expr, $to_fn:expr) => {
impl TryInto<$out_type> for $target
where
Self: JSValueImpl,
{
type Error = RongJSError;
fn try_into(self) -> Result<$out_type, Self::Error> {
let mut result: $out_type = Default::default();
if unsafe { $to_fn(*self.as_raw_context(), *self.as_raw_value(), &mut result) } < 0
{
Err($crate::HostError::new(
$crate::error::E_TYPE,
format!(
"Expected JSValue to be type {}, but got {:?}",
std::any::type_name::<$out_type>(),
self.type_of()
),
)
.with_name("TypeError")
.into())
} else {
Ok(result)
}
}
}
impl<T> From<(&T, $in_type)> for $target
where
T: JSContextImpl<RawContext = <$target as JSRawContext>::RawContext>,
$target: JSValueImpl<Context = T>,
{
fn from(t: (&T, $in_type)) -> Self {
let ctx = t.0.as_raw();
let raw = unsafe { $create_fn(*ctx, t.1) };
Self::from_owned_raw(*ctx, raw)
}
}
};
($target:ty, $type:ty, $create_fn:expr, $to_fn:expr) => {
impl_js_converter!($target, $type, $type, $create_fn, $to_fn);
};
}
impl<V: JSValueImpl> crate::function::JSParameterType for JSValue<V> {}
impl<V> fmt::Display for JSValue<V>
where
V: JSTypeOf + JSValueConversion,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.type_of() {
JSValueType::Boolean => {
if let Ok(val) = self.clone().try_into::<bool>() {
write!(f, "{}", val)
} else {
write!(f, "boolean")
}
}
JSValueType::Number => {
if let Ok(val) = self.clone().try_into::<f64>() {
write!(f, "{}", val)
} else {
write!(f, "number")
}
}
JSValueType::String => {
if let Ok(val) = self.clone().try_into::<String>() {
write!(f, "{}", val)
} else {
write!(f, "string")
}
}
JSValueType::Date => {
if let Ok(val) = self.clone().try_into::<String>() {
write!(f, "{}", val)
} else {
write!(f, "Date")
}
}
JSValueType::Undefined => write!(f, "undefined"),
JSValueType::Null => write!(f, "null"),
JSValueType::BigInt => write!(f, "bigint"),
JSValueType::Object => write!(f, "object"),
JSValueType::Array => write!(f, "array"),
JSValueType::ArrayBuffer => write!(f, "arrayBuffer"),
JSValueType::Function => write!(f, "function"),
JSValueType::Constructor => write!(f, "constructor"),
JSValueType::Promise => write!(f, "promise"),
JSValueType::Symbol => write!(f, "symbol"),
JSValueType::Error => write!(f, "error"),
JSValueType::Exception => write!(f, "exception"),
JSValueType::Unknown => write!(f, "unknown"),
}
}
}
impl<V> fmt::Debug for JSValue<V>
where
V: JSTypeOf + JSValueConversion,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "JSValue({})", self)
}
}