Skip to content

Commit cd0d1ff

Browse files
authored
Add Ts<T> wrapper type to avoid memory leaks (#71)
Add `Ts<T>` wrapper type to avoid memory leaks. This addresses #65 * Add a test for conversions of Ts<T> * Add an e2e test for Ts<T> * Return better error messages for Ts<T> conversions * include_str! README.md to lib.rs as documentation and doctestify its examples * Update expandtest for rustc 1.88.0 * Deprecate #[tsify(into_wasm_abi, from_wasm_abi)] There seems to be no way to emit #[deprecated] warnings. * Actually emit a #[deprecated] from the macro for into_wasm_abi/from_wasm_abi attributes
1 parent 7a211be commit cd0d1ff

21 files changed

Lines changed: 776 additions & 42 deletions

File tree

README.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,27 @@ wasm-bindgen = { version = "0.2" }
2525
```rust
2626
use serde::{Deserialize, Serialize};
2727
use tsify::Tsify;
28+
use tsify::Ts;
2829
use wasm_bindgen::prelude::*;
30+
use wasm_bindgen::JsError;
2931

3032
#[derive(Tsify, Serialize, Deserialize)]
31-
#[tsify(into_wasm_abi, from_wasm_abi)]
3233
pub struct Point {
3334
x: i32,
3435
y: i32,
3536
}
3637

3738
#[wasm_bindgen]
38-
pub fn into_js() -> Point {
39-
Point { x: 0, y: 0 }
39+
pub fn into_js() -> Result<Ts<Point>, JsError> {
40+
let point = Point { x: 0, y: 0 };
41+
Ok(point.into_ts()?)
4042
}
4143

4244
#[wasm_bindgen]
43-
pub fn from_js(point: Point) {}
45+
pub fn from_js(point: Ts<Point>) -> Result<(), JsError> {
46+
let point: Point = point.to_rust()?;
47+
Ok(())
48+
}
4449
```
4550

4651
Will generate the following `.d.ts` file:
@@ -73,8 +78,8 @@ This is the behavior due to [`typescript_custom_section`](https://rustwasm.githu
7378

7479
Tsify container attributes
7580

76-
- `into_wasm_abi` implements `IntoWasmAbi` and `OptionIntoWasmAbi`. This can be converted directly from Rust to JS via `serde_json` or `serde-wasm-bindgen`.
77-
- `from_wasm_abi` implements `FromWasmAbi` and `OptionFromWasmAbi`. This is the opposite operation of the above.
81+
- `into_wasm_abi` (deprecated) implements `IntoWasmAbi` and `OptionIntoWasmAbi`. This can be converted directly from Rust to JS via `serde_json` or `serde-wasm-bindgen`. Deprecated in favour of using `Ts<T>` as on function parameters and return type.
82+
- `from_wasm_abi` (deprecated) implements `FromWasmAbi` and `OptionFromWasmAbi`. This is the opposite operation of the above. Deprecated in favour of using `Ts<T>` as on function parameters and return type.
7883
- `namespace` generates a namespace for the enum variants.
7984
- `type` overrides at the container level.
8085
- `type_params` overrides params at the container level.
@@ -128,6 +133,8 @@ export interface Foo {
128133
## Optional Properties
129134

130135
```rust
136+
use tsify::Tsify;
137+
131138
#[derive(Tsify)]
132139
struct Optional {
133140
#[tsify(optional)]
@@ -152,6 +159,8 @@ export interface Optional {
152159
## Enum
153160

154161
```rust
162+
use tsify::Tsify;
163+
155164
#[derive(Tsify)]
156165
enum Color {
157166
Red,
@@ -180,6 +189,8 @@ export type Color =
180189
## Enum with namespace
181190

182191
```rust
192+
use tsify::Tsify;
193+
183194
#[derive(Tsify)]
184195
#[tsify(namespace)]
185196
enum Color {

src/error.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::fmt;
2+
3+
/// An error type wrapping serialization or deserialization errors from either `serde_json` or
4+
/// `serde_wasm_bindgen`, depending on whether the `json` or `js` feature is enabled.
5+
///
6+
#[derive(Debug)]
7+
pub struct Error {
8+
// All private internals
9+
//
10+
/// XXX: May want to put type-name printing behind an off-by-default feature flag,
11+
/// as it does add bloat that some users might not want.
12+
pub(crate) type_name: &'static str,
13+
/// true -> During deserialization; false -> during serialization
14+
pub(crate) de: bool,
15+
pub(crate) inner: SerializationError,
16+
}
17+
18+
#[cfg(all(feature = "json", not(feature = "js")))]
19+
type SerializationError = serde_json::Error;
20+
#[cfg(feature = "js")]
21+
type SerializationError = serde_wasm_bindgen::Error;
22+
23+
impl fmt::Display for Error {
24+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25+
if self.de {
26+
write!(
27+
f,
28+
"Failed to deserialize JsValue into type `{}`: {}",
29+
self.type_name, self.inner
30+
)
31+
} else {
32+
write!(
33+
f,
34+
"Failed to serialize type `{}` into JsValue: {}",
35+
self.type_name, self.inner
36+
)
37+
}
38+
}
39+
}
40+
41+
/// We implement [std::error::Error] so you can use `?` in `-> Result<_,
42+
/// wasm_bindgen::JsError>` functions to automatically throw.
43+
impl std::error::Error for Error {
44+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
45+
Some(&self.inner)
46+
}
47+
}

src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
#![doc = include_str!("../README.md")]
12
#![allow(clippy::wrong_self_convention)]
23

34
#[cfg(not(any(feature = "json", feature = "js")))]
45
compile_error!(
56
"Either the \"json\" or \"js\" feature must be enabled for tsify to function properly"
67
);
78

9+
mod ts;
10+
pub use ts::Ts;
11+
mod error;
12+
pub use error::Error;
13+
814
#[cfg(all(feature = "json", not(feature = "js")))]
915
pub use gloo_utils::format::JsValueSerdeExt;
1016
#[cfg(feature = "js")]
@@ -72,4 +78,16 @@ pub trait Tsify {
7278
{
7379
serde_wasm_bindgen::from_value(js.into())
7480
}
81+
82+
/// Calls `Ts::from_rust` on self, returning a `Result<Ts<Self>, crate::Error>`.
83+
///
84+
/// This can (and should) be used with the [`-> Result<_, JsError>`][wasm_bindgen::JsError]
85+
/// pattern from wasm-bindgen to automatically throw any Err value returned.
86+
fn into_ts(&self) -> Result<Ts<Self>, crate::Error>
87+
where
88+
Self: Sized,
89+
Self: serde::Serialize,
90+
{
91+
Ts::from_rust(self)
92+
}
7593
}

0 commit comments

Comments
 (0)