Skip to content

Commit

Permalink
added vector support
Browse files Browse the repository at this point in the history
  • Loading branch information
eneoli committed Aug 1, 2024
1 parent 2492b09 commit 958639b
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ Tsify container attributes
- `into_wasm_abi` implements `IntoWasmAbi` and `OptionIntoWasmAbi`. This can be converted directly from Rust to JS via `serde_json` or `serde-wasm-bindgen`.
- `from_wasm_abi` implements `FromWasmAbi` and `OptionFromWasmAbi`. This is the opposite operation of the above.
- `namespace` generates a namespace for the enum variants.
- `vector_into_wasm_abi` implements `VectorIntoWasmAbi`. This is the vector version of `into_wasm_abi` and is needed when working with vectors.
- `vector_from_wasm_abi` implements `VectorFromWasmAbi`. This is the opposite operation of the above.

Tsify field attributes

Expand Down
22 changes: 22 additions & 0 deletions tsify-next-macros/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ use crate::comments::extract_doc_comments;
pub struct TsifyContainerAttrs {
/// Implement `IntoWasmAbi` for the type.
pub into_wasm_abi: bool,
/// Implement `VectorIntoWasmAbi` for the type.
pub vector_into_wasm_abi: bool,
/// Implement `FromWasmAbi` for the type.
pub from_wasm_abi: bool,
/// Implement `VectorFromWasmAbi` for the type.
pub vector_from_wasm_abi: bool,
/// Whether the type should be wrapped in a Typescript namespace.
pub namespace: bool,
/// Information about how the type should be serialized.
Expand Down Expand Up @@ -46,7 +50,9 @@ impl TsifyContainerAttrs {
pub fn from_derive_input(input: &syn::DeriveInput) -> syn::Result<Self> {
let mut attrs = Self {
into_wasm_abi: false,
vector_into_wasm_abi: false,
from_wasm_abi: false,
vector_from_wasm_abi: false,
namespace: false,
ty_config: TypeGenerationConfig::default(),
comments: extract_doc_comments(&input.attrs),
Expand All @@ -66,13 +72,29 @@ impl TsifyContainerAttrs {
return Ok(());
}

if meta.path.is_ident("vector_into_wasm_abi") {
if attrs.vector_into_wasm_abi {
return Err(meta.error("duplicate attribute"));
}
attrs.vector_into_wasm_abi = true;
return Ok(());
}

if meta.path.is_ident("from_wasm_abi") {
if attrs.from_wasm_abi {
return Err(meta.error("duplicate attribute"));
}
attrs.from_wasm_abi = true;
return Ok(());
}

if meta.path.is_ident("vector_from_wasm_abi") {
if attrs.vector_from_wasm_abi {
return Err(meta.error("duplicate attribute"));
}
attrs.vector_from_wasm_abi = true;
return Ok(());
}

if meta.path.is_ident("namespace") {
if !matches!(input.data, syn::Data::Enum(_)) {
Expand Down
107 changes: 105 additions & 2 deletions tsify-next-macros/src/wasm_bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ pub fn expand(cont: &Container, decl: Decl) -> TokenStream {
}
});

let wasm_vector_abi = attrs.vector_into_wasm_abi || attrs.vector_from_wasm_abi;
let wasm_describe_vector = wasm_vector_abi.then(|| {
quote! {
impl #impl_generics WasmDescribeVector for #ident #ty_generics #where_clause {
#[inline]
fn describe_vector() {
<Self as Tsify>::JsType::describe_vector()
}
}
}
});

let use_serde = wasm_abi.then(|| match cont.serde_container.attrs.custom_serde_path() {
Some(path) => quote! {
use #path as _serde;
Expand All @@ -37,9 +49,19 @@ pub fn expand(cont: &Container, decl: Decl) -> TokenStream {
extern crate serde as _serde;
},
});

let into_wasm_abi = attrs.into_wasm_abi.then(|| expand_into_wasm_abi(cont));

let vector_into_wasm_abi = attrs
.vector_into_wasm_abi
.then(|| expand_vector_into_wasm_abi(cont));

let from_wasm_abi = attrs.from_wasm_abi.then(|| expand_from_wasm_abi(cont));

let vector_from_wasm_abi = attrs
.vector_from_wasm_abi
.then(|| expand_vector_from_wasm_abi(cont));

let typescript_type = decl.id();

let missing_as_null = attrs.ty_config.missing_as_null;
Expand All @@ -52,8 +74,8 @@ pub fn expand(cont: &Container, decl: Decl) -> TokenStream {
#use_serde
use tsify_next::Tsify;
use wasm_bindgen::{
convert::{FromWasmAbi, IntoWasmAbi, OptionFromWasmAbi, OptionIntoWasmAbi, RefFromWasmAbi},
describe::WasmDescribe,
convert::{FromWasmAbi, VectorFromWasmAbi, IntoWasmAbi, VectorIntoWasmAbi, OptionFromWasmAbi, OptionIntoWasmAbi, RefFromWasmAbi},
describe::WasmDescribe, describe::WasmDescribeVector,
prelude::*,
};

Expand All @@ -76,8 +98,11 @@ pub fn expand(cont: &Container, decl: Decl) -> TokenStream {

#typescript_custom_section
#wasm_describe
#wasm_describe_vector
#into_wasm_abi
#vector_into_wasm_abi
#from_wasm_abi
#vector_from_wasm_abi
};
}
}
Expand Down Expand Up @@ -147,6 +172,50 @@ fn expand_into_wasm_abi(cont: &Container) -> TokenStream {
}
}

fn expand_vector_into_wasm_abi(cont: &Container) -> TokenStream {
let ident = cont.ident();
let serde_path = cont.serde_container.attrs.serde_path();

let borrowed_generics = cont.generics();
let mut generics = cont.generics().clone();
generics
.make_where_clause()
.predicates
.push(parse_quote!(#ident #borrowed_generics: #serde_path::Serialize));

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

quote! {
impl #impl_generics VectorIntoWasmAbi for #ident #ty_generics #where_clause {
type Abi = <JsType as VectorIntoWasmAbi>::Abi;

#[inline]
fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
let values = vector
.iter()
.map(|value|
// wasm_bindgen doesn't forward the error message from the `into_js` result.
// https://github.com/rustwasm/wasm-bindgen/issues/2732
// Until that issue is fixed, we don't directly use `unwrap_throw()` and instead build our
// own error message.
// Convert to `value.into_js().unwrap_throw().into()` when fixed.
match value.into_js() {
Ok(js) => js.into(),
Err(err) => {
let loc = core::panic::Location::caller();
let msg = format!("(Converting type failed) {} ({}:{}:{})", err, loc.file(), loc.line(), loc.column());
// In theory, `wasm_bindgen::throw_str(&msg)` should work, but the error emitted by `wasm_bindgen::throw_str` cannot be picked up by `#[should_panic(expect = ...)]` in tests, so we use a regular panic.
panic!("{}", msg);
}
})
.collect();

JsValue::vector_into_abi(values)
}
}
}
}

fn expand_from_wasm_abi(cont: &Container) -> TokenStream {
let ident = cont.ident();
let serde_path = cont.serde_container.attrs.serde_path();
Expand Down Expand Up @@ -206,3 +275,37 @@ fn expand_from_wasm_abi(cont: &Container) -> TokenStream {
}
}
}

fn expand_vector_from_wasm_abi(cont: &Container) -> TokenStream {
let ident = cont.ident();
let serde_path = cont.serde_container.attrs.serde_path();

let mut generics = cont.generics().clone();

generics
.make_where_clause()
.predicates
.push(parse_quote!(Self: #serde_path::de::DeserializeOwned));

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

quote! {
impl #impl_generics VectorFromWasmAbi for #ident #ty_generics #where_clause {
type Abi = <JsType as VectorFromWasmAbi>::Abi;

#[inline]
unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
JsValue::vector_from_abi(js)
.into_iter()
.map(|value| {
let result = Self::from_js(value);
if let Err(err) = result {
wasm_bindgen::throw_str(err.to_string().as_ref());
}
result.unwrap_throw()
})
.collect()
}
}
}
}

0 comments on commit 958639b

Please sign in to comment.