use crate as tsain;
use crate::Error;
use serde::{Serialize, de::DeserializeOwned};
use std::{marker::PhantomData, mem::ManuallyDrop};
use wasm_bindgen::{
convert::{
FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, OptionFromWasmAbi, OptionIntoWasmAbi,
RefFromWasmAbi, VectorFromWasmAbi, VectorIntoWasmAbi,
},
describe::{WasmDescribe, WasmDescribeVector},
prelude::*,
};
#[repr(transparent)]
pub struct Ts<T>(JsValue, PhantomData<T>);
impl<T> Clone for Ts<T> {
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<T> std::fmt::Debug for Ts<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Ts").finish()
}
}
impl<T> Ts<T> {
pub fn new(js: JsValue) -> Self {
Self(js, std::marker::PhantomData)
}
pub fn js_value(&self) -> &JsValue {
&self.0
}
pub fn from_rust(value: &T) -> Result<Self, Error>
where
T: Serialize,
{
let x = tsain::to_value(value)?;
Ok(Self(x, PhantomData))
}
pub fn to_rust(self) -> Result<T, Error>
where
T: DeserializeOwned,
{
tsain::from_value(self.0)
}
}
impl<T> WasmDescribe for Ts<T> {
fn describe() {
JsValue::describe();
}
}
impl<T> IntoWasmAbi for Ts<T> {
type Abi = <JsValue as IntoWasmAbi>::Abi;
fn into_abi(self) -> Self::Abi {
self.0.into_abi()
}
}
impl<T> IntoWasmAbi for &Ts<T> {
type Abi = <JsValue as IntoWasmAbi>::Abi;
fn into_abi(self) -> Self::Abi {
self.0.clone().into_abi()
}
}
impl<T> FromWasmAbi for Ts<T> {
type Abi = <JsValue as IntoWasmAbi>::Abi;
unsafe fn from_abi(js: Self::Abi) -> Self {
Self(unsafe { JsValue::from_abi(js) }, PhantomData)
}
}
impl<T> OptionIntoWasmAbi for Ts<T> {
fn none() -> Self::Abi {
<JsValue as OptionIntoWasmAbi>::none()
}
}
impl<T> OptionFromWasmAbi for Ts<T> {
fn is_none(abi: &Self::Abi) -> bool {
JsValue::is_none(abi)
}
}
impl<T> RefFromWasmAbi for Ts<T> {
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 = unsafe { JsValue::ref_from_abi(js) };
let anchor_inner = ManuallyDrop::into_inner(js_type_anchor);
ManuallyDrop::new(Ts::new(anchor_inner))
}
}
impl<T> LongRefFromWasmAbi for Ts<T> {
type Abi = <JsValue as LongRefFromWasmAbi>::Abi;
type Anchor = Ts<T>;
unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
let js_value = unsafe { <JsValue as LongRefFromWasmAbi>::long_ref_from_abi(js) };
Self::new(js_value)
}
}
impl<T> WasmDescribeVector for Ts<T> {
fn describe_vector() {
JsValue::describe_vector();
}
}
impl<T> VectorFromWasmAbi for Ts<T> {
type Abi = <JsValue as VectorFromWasmAbi>::Abi;
unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
let vec_js = unsafe { <JsValue as VectorFromWasmAbi>::vector_from_abi(js) };
let vec = Vec::from(vec_js);
vec.into_iter()
.map(|js_item| Ts::new(js_item))
.collect::<Vec<Ts<T>>>()
.into_boxed_slice()
}
}
impl<T> VectorIntoWasmAbi for Ts<T> {
type Abi = <JsValue as VectorIntoWasmAbi>::Abi;
fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
let vec_js: Vec<JsValue> = vector
.into_vec()
.into_iter()
.map(|ts_item| ts_item.0)
.collect();
JsValue::vector_into_abi(vec_js.into_boxed_slice())
}
}