use crate::mem::{block::Block, manager::Dealloc, optional_ref::OptionalRef};
use super::{
any_cast::AnyCast,
any_internal::AnyInternal,
bitset::{ref_type, REF_SUBSET_SUPERPOSITION},
js_array::JsArrayRef,
js_object::JsObjectRef,
null::Null,
ref_cast::RefCast,
type_::Type,
};
pub type Any<D> = OptionalRef<AnyInternal<D>>;
impl<D: Dealloc> Any<D> {
#[inline(always)]
unsafe fn u64(&self) -> u64 {
self.internal().0
}
#[inline(always)]
pub fn is<T: AnyCast<D>>(&self) -> bool {
unsafe { T::has_same_type(self.u64()) }
}
pub fn move_from<T: AnyCast<D>>(t: T) -> Self {
t.move_to_any()
}
#[allow(clippy::result_unit_err)]
pub fn try_move<T: AnyCast<D>>(self) -> Result<T, ()> {
if self.is::<T>() {
return Ok(unsafe { T::from_any_internal(self.move_to_internal().0) });
}
Err(())
}
pub fn get_type(&self) -> Type {
if self.is_ref() {
match ref_type(unsafe { self.internal().0 }) {
0b00 => Type::String,
0b01 => Type::Object,
0b10 => Type::Array,
_ => unreachable!(),
}
} else if self.is::<f64>() {
Type::Number
} else if self.is::<Null>() {
Type::Null
} else {
Type::Bool
}
}
#[allow(clippy::result_unit_err)]
#[inline(always)]
pub fn try_ref<T: RefCast<D>>(&self) -> Result<&Block<T, D>, ()> {
let v = unsafe { self.u64() };
if T::REF_SUBSET.has(v) {
let p = (v & REF_SUBSET_SUPERPOSITION) as *const Block<T, D>;
return Ok(unsafe { &*p });
}
Err(())
}
pub fn for_each<E>(
&self,
mut f: impl FnMut(Any<D>, &Any<D>) -> Result<(), E>,
) -> Result<(), E> {
match self.get_type() {
Type::Object => {
let o = self.clone().try_move::<JsObjectRef<D>>().unwrap();
for (k, v) in o.items() {
f(k.clone().move_to_any(), v)?;
}
}
Type::Array => {
let o = self.clone().try_move::<JsArrayRef<D>>().unwrap();
for (k, v) in o.items().iter().enumerate() {
f((k as f64).move_to_any(), v)?;
}
}
_ => {}
};
Ok(())
}
}
#[cfg(test)]
mod test {
use std::{collections::HashSet, rc::Rc};
use wasm_bindgen_test::wasm_bindgen_test;
use crate::{
js::{
js_array::{new_array, JsArrayRef},
js_object::{new_object, JsObjectRef},
js_string::{new_string, JsString, JsStringRef},
null::Null,
},
mem::global::Global,
};
use super::*;
#[test]
#[wasm_bindgen_test]
fn test_unsized() {
let _x: Rc<[u8]> = Rc::new([1, 3]);
}
#[test]
#[wasm_bindgen_test]
fn test_number() {
type A = Any<Global>;
assert_eq!(A::move_from(1.0).try_move(), Ok(1.0));
let x: A = A::move_from(-1.0);
assert_eq!(x.try_move(), Ok(-1.0));
assert_eq!(A::move_from(f64::INFINITY).try_move(), Ok(f64::INFINITY));
assert_eq!(
A::move_from(f64::NEG_INFINITY).try_move(),
Ok(f64::NEG_INFINITY)
);
assert!(A::move_from(f64::NAN).try_move::<f64>().unwrap().is_nan());
assert_eq!(A::move_from(true).try_move::<f64>(), Err(()));
assert_eq!(A::move_from(Null()).try_move::<f64>(), Err(()));
}
#[test]
#[wasm_bindgen_test]
fn test_bool() {
type A = Any<Global>;
assert_eq!(A::move_from(true).try_move(), Ok(true));
assert_eq!(A::move_from(false).try_move(), Ok(false));
assert_eq!(A::move_from(15.0).try_move::<bool>(), Err(()));
assert_eq!(A::move_from(Null()).try_move::<bool>(), Err(()));
}
#[test]
#[wasm_bindgen_test]
fn test_null() {
type A = Any<Global>;
assert!(A::move_from(Null()).is::<Null>());
assert!(!A::move_from(-15.7).is::<Null>());
assert!(!A::move_from(false).is::<Null>());
}
#[test]
#[wasm_bindgen_test]
fn test_type() {
type A = Any<Global>;
assert_eq!(A::move_from(15.0).get_type(), Type::Number);
assert_eq!(A::move_from(true).get_type(), Type::Bool);
assert_eq!(A::move_from(Null()).get_type(), Type::Null);
}
#[test]
#[wasm_bindgen_test]
fn test_string() {
type A = Any<Global>;
type StringRef = JsStringRef<Global>;
let sm = new_string(Global(), []);
let s = sm.to_ref();
assert!(A::move_from(s.clone()).is::<StringRef>());
let v = s.items();
assert!(v.is_empty());
assert!(!A::move_from(15.0).is::<StringRef>());
assert!(!A::move_from(true).is::<StringRef>());
assert!(!A::move_from(Null()).is::<StringRef>());
let s = new_string(Global(), [0x20, 0x21]).to_ref();
assert!(A::move_from(s.clone()).is::<StringRef>());
let v = s.items();
assert_eq!(v, [0x20, 0x21]);
let u = A::move_from(s);
{
let s = u.try_ref::<JsString>().unwrap();
let items = s.object().items();
assert_eq!(items, [0x20, 0x21]);
}
let s = u.try_move::<StringRef>().unwrap();
let items = s.items();
assert_eq!(items, [0x20, 0x21]);
}
#[test]
#[wasm_bindgen_test]
fn test_object() {
type A = Any<Global>;
type ObjectRef = JsObjectRef<Global>;
assert!(!A::move_from(Null()).is::<ObjectRef>());
let o: ObjectRef = new_object(Global(), []).to_ref();
assert!(A::move_from(o.clone()).is::<ObjectRef>());
let v = o.items();
assert!(v.is_empty());
assert!(!A::move_from(15.0).is::<ObjectRef>());
assert!(!A::move_from(true).is::<ObjectRef>());
let o: ObjectRef = new_object(Global(), []).to_ref();
let u = A::move_from(o);
assert_eq!(u.get_type(), Type::Object);
{
let o = u.try_move::<ObjectRef>().unwrap();
let items = o.items();
assert!(items.is_empty());
}
}
#[test]
#[wasm_bindgen_test]
fn test_array() {
type A = Any<Global>;
type ArrayRef = JsArrayRef<Global>;
assert!(!A::move_from(Null()).is::<ArrayRef>());
let o: ArrayRef = new_array(Global(), []).to_ref();
assert!(A::move_from(o.clone()).is::<ArrayRef>());
let v = o.items();
assert!(v.is_empty());
assert!(!A::move_from(15.0).is::<ArrayRef>());
assert!(!A::move_from(true).is::<ArrayRef>());
let o: ArrayRef = new_array(Global(), []).to_ref();
let u = A::move_from(o);
assert_eq!(u.get_type(), Type::Array);
{
let o = u.try_move::<ArrayRef>().unwrap();
let items = o.items();
assert!(items.is_empty());
}
}
#[test]
#[wasm_bindgen_test]
fn test_eq() {
let mut v = HashSet::<Any<Global>>::new();
v.insert(Any::move_from(1.0));
}
}