use crate::{
FromJSValue, IntoJSValue, JSContext, JSObject, JSObjectOps, JSResult, JSTypeOf, JSValue,
JSValueConversion, JSValueImpl, JSValueMapper, RongJSError,
};
use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
pub struct JSArray<V: JSValueImpl>(JSObject<V>);
impl<V: JSValueImpl> Deref for JSArray<V> {
type Target = JSObject<V>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<V: JSValueImpl> Clone for JSArray<V> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<V> IntoJSValue<V> for JSArray<V>
where
V: JSValueImpl,
{
fn into_js_value(self, _ctx: &JSContext<V::Context>) -> JSValue<V> {
self.0.into_js_value()
}
}
impl<V> FromJSValue<V> for JSArray<V>
where
V: JSTypeOf,
{
fn from_js_value(ctx: &JSContext<V::Context>, value: JSValue<V>) -> JSResult<Self> {
if value.is_array() {
JSObject::from_js_value(ctx, value).map(Self)
} else {
Err(RongJSError::NotJSArray())
}
}
}
pub trait JSArrayOps: JSValueImpl {
fn new(ctx: &Self::Context) -> Self;
fn get(&self, index: u32) -> Self;
fn set(&self, index: u32, value: Self) -> Self;
}
impl<V> JSArray<V>
where
V: JSObjectOps + JSArrayOps,
{
pub fn new(ctx: &JSContext<V::Context>) -> JSResult<Self> {
let value = V::new(ctx.as_ref());
value.try_map(|v| Self::from_js_value(ctx, JSValue::from_raw(ctx, v)))?
}
pub fn len(&self) -> u32 {
self.0.get::<_, u32>("length").unwrap_or(0)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get<T>(&self, index: u32) -> JSResult<Option<T>>
where
T: FromJSValue<V>,
{
if index >= self.len() {
return Ok(None);
}
let value = self.as_value().get(index);
let ctx = &self.get_ctx();
value.try_map(|v| T::from_js_value(ctx, JSValue::from_raw(ctx, v)).map(Some))?
}
pub fn set<T>(&self, index: u32, value: T) -> JSResult<&Self>
where
T: IntoJSValue<V>,
{
let ctx = self.get_ctx();
let value = <T as IntoJSValue<V>>::into_js_value(value, &ctx);
let result = self.as_value().set(index, value.into_value());
result.try_map(|_| self)
}
pub fn iter<T>(&self) -> ArrayIter<V, T>
where
T: FromJSValue<V>,
{
let count = self.len();
ArrayIter {
array: self.clone(),
index: 0,
count,
marker: PhantomData,
}
}
pub fn push<T>(&self, value: T) -> JSResult<()>
where
T: IntoJSValue<V>,
{
let ctx = self.get_ctx();
let value = <T as IntoJSValue<V>>::into_js_value(value, &ctx);
let index = self.len();
self.as_value().set(index, value.into_value());
Ok(())
}
pub fn pop<T>(&self) -> JSResult<Option<T>>
where
T: FromJSValue<V>,
{
if self.is_empty() {
return Ok(None);
}
let index = self.len() - 1;
let value = self.as_value().get(index);
self.0.del(index);
self.0.set("length", index)?;
let ctx = self.get_ctx();
T::from_js_value(&ctx, JSValue::from_raw(&ctx, value)).map(Some)
}
pub fn from_object(obj: JSObject<V>) -> Option<Self> {
if obj.as_value().is_array() {
Some(Self(obj))
} else {
None
}
}
}
pub struct ArrayIter<V, T>
where
V: JSObjectOps + JSArrayOps,
T: FromJSValue<V>,
{
array: JSArray<V>,
index: u32,
count: u32,
marker: PhantomData<T>,
}
impl<V, T> Iterator for ArrayIter<V, T>
where
V: JSObjectOps + JSArrayOps,
T: FromJSValue<V>,
{
type Item = JSResult<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.count {
let v = self.array.as_value().get(self.index);
let ctx = self.array.get_ctx();
let res = T::from_js_value(&ctx, JSValue::from_raw(&ctx, v));
self.index += 1;
Some(res)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<V, T> ExactSizeIterator for ArrayIter<V, T>
where
V: JSObjectOps + JSArrayOps,
T: FromJSValue<V>,
{
fn len(&self) -> usize {
(self.count - self.index) as usize
}
}
impl<V, T> IntoJSValue<V> for Vec<T>
where
V: JSObjectOps + JSArrayOps,
T: IntoJSValue<V>,
{
fn into_js_value(self, ctx: &JSContext<V::Context>) -> JSValue<V> {
let array = JSArray::new(ctx).unwrap();
for item in self {
array.push(item).expect("Failed to set value in array");
}
<JSArray<V> as IntoJSValue<V>>::into_js_value(array, ctx)
}
}
impl<V, T> FromJSValue<V> for Vec<T>
where
V: JSTypeOf,
V: JSObjectOps + JSArrayOps,
T: FromJSValue<V>,
{
fn from_js_value(ctx: &JSContext<V::Context>, value: JSValue<V>) -> JSResult<Self> {
if value.is_array() {
let array = JSArray::from_js_value(ctx, value)?;
let vec = array.iter::<T>().collect::<JSResult<Vec<_>>>()?;
Ok(vec)
} else {
Err(RongJSError::NotJSArray())
}
}
}
impl<V: JSValueImpl> crate::function::JSParameterType for JSArray<V> {}
impl<V> fmt::Display for JSArray<V>
where
V: JSTypeOf + JSValueConversion,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.deref().fmt(f)
}
}
impl<V> fmt::Debug for JSArray<V>
where
V: JSTypeOf + JSValueConversion,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "JSArray({})", self)
}
}