use std::fmt;
use std::mem::ManuallyDrop;
use crate::Error;
use crate::Tsify;
use wasm_bindgen::convert::{
FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, OptionFromWasmAbi, OptionIntoWasmAbi,
RefFromWasmAbi, VectorFromWasmAbi, VectorIntoWasmAbi,
};
use wasm_bindgen::describe::WasmDescribe;
use wasm_bindgen::describe::WasmDescribeVector;
use wasm_bindgen::{JsCast, JsValue};
#[repr(transparent)]
pub struct Ts<T: Tsify>(<T as Tsify>::JsType, std::marker::PhantomData<T>);
impl<T> Ts<T>
where
T: Tsify,
{
fn new(js: <T as Tsify>::JsType) -> Self {
Self(js, std::marker::PhantomData)
}
pub fn js_value(&self) -> JsValue
where
<T as Tsify>::JsType: JsCast,
{
self.0.unchecked_ref::<JsValue>().clone()
}
pub fn new_unchecked(js: JsValue) -> Self {
Self::new(js.unchecked_into())
}
}
impl<T: Tsify> From<Ts<T>> for JsValue {
fn from(value: Ts<T>) -> Self {
value.js_value()
}
}
impl<T: Tsify> fmt::Debug for Ts<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Ts").finish()
}
}
impl<T> Clone for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: Clone,
{
fn clone(&self) -> Self {
Self(self.0.clone(), std::marker::PhantomData)
}
}
impl<T> WasmDescribe for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: WasmDescribe,
{
fn describe() {
<T as Tsify>::JsType::describe()
}
}
impl<T> IntoWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: IntoWasmAbi,
{
type Abi = <T::JsType as IntoWasmAbi>::Abi;
fn into_abi(self) -> Self::Abi {
self.0.into_abi()
}
}
impl<'a, T> IntoWasmAbi for &'a Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: JsCast + WasmDescribe,
{
type Abi = <&'a JsValue as IntoWasmAbi>::Abi;
fn into_abi(self) -> Self::Abi {
self.0.unchecked_ref::<JsValue>().into_abi()
}
}
impl<T> FromWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: FromWasmAbi,
{
type Abi = <T::JsType as FromWasmAbi>::Abi;
unsafe fn from_abi(js: Self::Abi) -> Self {
Self(<T as Tsify>::JsType::from_abi(js), std::marker::PhantomData)
}
}
impl<T> OptionIntoWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: OptionIntoWasmAbi,
{
fn none() -> Self::Abi {
<T::JsType as OptionIntoWasmAbi>::none()
}
}
impl<T> OptionFromWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: OptionFromWasmAbi,
{
fn is_none(abi: &Self::Abi) -> bool {
<T as Tsify>::JsType::is_none(abi)
}
}
impl<T> RefFromWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: RefFromWasmAbi,
{
type Anchor = ManuallyDrop<Ts<T>>;
type Abi = <JsValue as RefFromWasmAbi>::Abi; unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
let js_type_anchor = JsValue::ref_from_abi(js);
let anchor_inner = ManuallyDrop::into_inner(js_type_anchor);
let js_type: <T as Tsify>::JsType = anchor_inner.unchecked_into();
ManuallyDrop::new(Ts::new(js_type))
}
}
impl<T> LongRefFromWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: LongRefFromWasmAbi,
{
type Abi = <JsValue as LongRefFromWasmAbi>::Abi;
type Anchor = Ts<T>;
unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
let js_value = <JsValue as LongRefFromWasmAbi>::long_ref_from_abi(js);
let js_type: <T as Tsify>::JsType = js_value.unchecked_into();
Self::new(js_type)
}
}
impl<T> WasmDescribeVector for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: WasmDescribeVector,
{
fn describe_vector() {
<T as Tsify>::JsType::describe_vector()
}
}
impl<T> VectorFromWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: VectorFromWasmAbi,
{
type Abi = <<T as Tsify>::JsType as VectorFromWasmAbi>::Abi;
unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
let vec_js = <<T as Tsify>::JsType as VectorFromWasmAbi>::vector_from_abi(js);
let vec = Vec::from(vec_js);
vec.into_iter()
.map(|js_item| Ts(js_item, std::marker::PhantomData))
.collect::<Vec<Ts<T>>>()
.into_boxed_slice()
}
}
impl<T> VectorIntoWasmAbi for Ts<T>
where
T: Tsify,
<T as Tsify>::JsType: VectorIntoWasmAbi,
{
type Abi = <<T as Tsify>::JsType as VectorIntoWasmAbi>::Abi;
fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
let vec_js: Vec<<T as Tsify>::JsType> = vector
.into_vec()
.into_iter()
.map(|ts_item| ts_item.0)
.collect();
<T as Tsify>::JsType::vector_into_abi(vec_js.into_boxed_slice())
}
}
impl<T: Tsify + serde::de::DeserializeOwned> Ts<T>
where
<T as Tsify>::JsType: Clone,
{
pub fn to_rust(&self) -> Result<T, Error> {
T::from_js(self.0.clone()).map_err(|inner| Error {
type_name: std::any::type_name::<T>(),
de: true,
inner,
})
}
}
impl<T: Tsify + serde::Serialize> Ts<T> {
pub fn from_rust(rust: &T) -> Result<Self, Error> {
let js_type = T::into_js(rust).map_err(|inner| Error {
type_name: std::any::type_name::<T>(),
de: false,
inner,
})?;
Ok(Self::new(js_type))
}
}