tsain-core 0.1.0

Chain TypeScript and Rust in fast and secure way
Documentation
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())
    }
}