Code Monkey home page Code Monkey logo

serde-wasm-bindgen's Introduction

Crates.io docs.rs GitHub Sponsors

This is a native integration of Serde with wasm-bindgen. It allows to convert Rust data types into native JavaScript types and vice versa.

Initially this library was created while working for @Cloudflare as an alternative implementation to the JSON-based Serde support built into the wasm-bindgen but, nowadays serde-wasm-bindgen is the officially preferred approach. It provides much smaller code size overhead than JSON, and, in most common cases, provides much faster serialization/deserialization as well.

Usage

Copied almost verbatim from the wasm-bindgen guide:

Add dependencies

To use serde-wasm-bindgen, you first have to add it as a dependency in your Cargo.toml. You also need the serde crate, with the derive feature enabled, to allow your types to be serialized and deserialized with Serde.

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"

Derive the Serialize and Deserialize Traits

Add #[derive(Serialize, Deserialize)] to your type. All of your type members must also be supported by Serde, i.e. their types must also implement the Serialize and Deserialize traits.

Note that you don't need to use the #[wasm_bindgen] macro.

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct Example {
    pub field1: HashMap<u32, String>,
    pub field2: Vec<Vec<f32>>,
    pub field3: [f32; 4],
}

Send it to JavaScript with serde_wasm_bindgen::to_value

#[wasm_bindgen]
pub fn send_example_to_js() -> Result<JsValue, JsValue> {
    let mut field1 = HashMap::new();
    field1.insert(0, String::from("ex"));

    let example = Example {
        field1,
        field2: vec![vec![1., 2.], vec![3., 4.]],
        field3: [1., 2., 3., 4.]
    };

    Ok(serde_wasm_bindgen::to_value(&example)?)
}

Receive it from JavaScript with serde_wasm_bindgen::from_value

#[wasm_bindgen]
pub fn receive_example_from_js(val: JsValue) -> Result<(), JsValue> {
    let example: Example = serde_wasm_bindgen::from_value(val)?;
    /* …do something with `example`… */
    Ok(())
}

JavaScript Usage

In the JsValue that JavaScript gets, field1 will be a Map<number, string>, field2 will be an Array<Array<number>>, and field3 will be an Array<number>.

import { send_example_to_js, receive_example_from_js } from "example";

// Get the example object from wasm.
let example = send_example_to_js();

// Add another "Vec" element to the end of the "Vec<Vec<f32>>"
example.field2.push([5, 6]);

// Send the example object back to wasm.
receive_example_from_js(example);

Supported Types

Note that, even though it might often be the case, by default this library doesn't attempt to be strictly compatible with JSON, instead prioritising better compatibility with common JavaScript idioms and representations.

If you need JSON compatibility (e.g. you want to serialize HashMap<String, …> as plain objects instead of JavaScript Map instances), use the Serializer::json_compatible() preset.

By default, Rust ⬄ JavaScript conversions in serde-wasm-bindgen follow this table:

Rust JavaScript Also supported in from_value
() and Option<T>::None undefined null
bool boolean
f32, f64 number
u8, i8, …, u32, i32 number in the safe integer range
u64, i64, usize, isize number in the safe integer range bigint
u128, i128 bigint
String string
char single-codepoint string
Enum::Variant { … } as configured in Serde
HashMap<K, V>, BTreeMap, etc. Map<K, V> any iterable over [K, V]
Struct { key1: value1, … } { key1: value1, … } object
tuple, Vec<T>, HashSet, etc. T[] array any iterable over T
serde_bytes byte buffer Uint8Array ArrayBuffer, Array

The first two columns show idiomatic representations on Rust and JavaScript sides, while the 3rd column shows which JavaScript values are additionally supported when deserializing from JavaScript to the Rust type.

Serializer configuration options

You can customize serialization from Rust to JavaScript by setting the following options on the Serializer::new() instance (all default to false):

  • .serialize_missing_as_null(true): Serialize (), unit structs and Option::None to null instead of undefined.
  • .serialize_maps_as_objects(true): Serialize maps into plain JavaScript objects instead of ES2015 Maps.
  • .serialize_large_number_types_as_bigints(true): Serialize u64, i64, usize and isize to bigints instead of attempting to fit them into the safe integer number or failing.
  • .serialize_bytes_as_arrays(true): Serialize bytes into plain JavaScript arrays instead of ES2015 Uint8Arrays.

You can also use the Serializer::json_compatible() preset to create a JSON compatible serializer. It enables serialize_missing_as_null, serialize_maps_as_objects, and serialize_bytes_as_arrays under the hood.

Preserving JavaScript values

Sometimes you want to preserve original JavaScript value instead of converting it into a Rust type. This is particularly useful for types that can't be converted without losing the data, such as Date, RegExp or 3rd-party types.

serde_wasm_bindgen::preserve allows you to do just that:

#[derive(Serialize, Deserialize)]
pub struct Example {
    pub regular_field: i32,

    #[serde(with = "serde_wasm_bindgen::preserve")]
    pub preserved_date: js_sys::Date,

    #[serde(with = "serde_wasm_bindgen::preserve")]
    pub preserved_arbitrary_value: JsValue,
}

TypeScript support

There's no built-in type generation in this crate, but you can tsify with the js feature which integrates with serde-wasm-bindgen under the hood. Aside from generating structural typings, it also allows to derive IntoWasmAbi / FromWasmAbi so that you don't have to write from_value / to_value by hand.

License

Licensed under the MIT license. See the LICENSE file for details.

serde-wasm-bindgen's People

Contributors

ashleygwilliams avatar awestlake87 avatar bhalleycf avatar carlsverre avatar dependabot[bot] avatar ekleog avatar fosskers avatar hkalbasi avatar jneem avatar liamolucko avatar mfish33 avatar mischnic avatar rreverser avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

serde-wasm-bindgen's Issues

Produce Object instead of Map when converting to JsValue

serde_wasm_bindgen::to_value returns Map. It should return an Object, as that's what's wanted quite often.

The following function, when called, should return the same output as it's input:

fn identity(value: JsValue) -> Result<JsValue, Error> {
    let value: serde_json::Value = serde_wasm_bindgen::from_value(value)?;
    serde_wasm_bindgen::to_value(value)
}

However that is not the case.

There should at least be a way to configure what is returned.

Additional Details:
This came up because I'm writing a JS library that uses json_patch. More specifically, the following snippet:

let mut doc = serde_wasm_bindgen::from_value(doc)?;
let patches: Vec<PatchOperation> = serde_wasm_bindgen::from_value(patches)?;
json_patch::patch(&mut doc, &patches)?
serde_wasm_bindgen::to_value(&doc)

When passed with objects, after manipulation, it returns Maps, which is undesirable.

Internally tagged enumerations cannot deserialize byte buffers

When deserializing a JsValue to an enum that is internally tagged within JavaScript, serve_bytes::ByteBufs produce errors:

  • When the JavaScript value is a Uint8Array, the deserialization fails with the error invalid type: any, expected byte array.
  • When the value is an ArrayBuffer, the deserialization fails with the error invalid type: map, expected byte array.

When the #[serde(tag = "type")] is removed, and externally tagged enums are passed, the serialization works for both input types. Below, you can check out a code example for the bug:

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
enum MyEnum {
    Buffer { buffer: serde_bytes::ByteBuf },
    Text { text: String },
}

#[wasm_bindgen]
pub fn echo(val: JsValue) {
    web_sys::console::log_1(&val);
    let des: MyEnum = serde_wasm_bindgen::from_value(val).unwrap();
    log::trace!("{:?}", des);
}

The JavaScript representation of MyEnum shall look as defined below:

type MyEnum = 
  | {type: "Buffer"; buffer: Uint8Array | ArrayBuffer}
  | {type: "Text"; text: string}

Version 0.4.0 makes JS code that breaks at load time on Safari 14

We just had to swiftly revert our dependency on serde-wasm-bindgen back to 0.3.1, as the introduction of BigInt support makes BigUint64Array and BigInt64Array be referenced at module scope in the *_bg.js wrapper, causing the module to fail to load in Safari 14 and earlier. Safari does not have these types until version 15.0 (released last Sep 20th, so there's still lots of Safari 14 out there).

This can break the whole web application (depending on one's bundling, I guess), even one that makes no use of this new BigInt support, in those browser versions.

So version 0.4 (and the README, if this stays as-is) should really carry a nice big warning about this effect on browser support.

Hitting an unreachable! panic in serializing

We're hitting an unreachable! panic inside the call to serde_wasm_bindgen::to_value. I've been tracking down the serialization code for the type we're serializing and can't find any unreachable! statement anywhere.

Also can't find an unreachable! statement inside this crate, but maybe there is something in dependent crates?

Did anyone every encounter an unreachable! panic while serializing to JsValue?

IntoWasmAbi

Is it possible to implement IntoWasmAbi for a type that supports Serialize via serde-wasm-bindgen? Passing them directly instead of passing JsValue would make it more type-safe to pass those objects into JavaScript using #[wasm_bindgen] interfaces.

Deserializing a BigInt inside a tagged enum fails

I put together a runnable repro here: https://github.com/LPGhatguy/serde-wasm-bindgen-bug-report

Defining an enum like...

#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
enum Foo {
    A { value: u64 },
}

and trying to deserialize it from a JS object containing a BigInt for the u64 fails with this error in Chrome:

serde_wasm_bindgen_bug.js:364 Uncaught (in promise) Error: invalid type: JsValue(BigInt), expected any value
...
    at Object.give_value (serde_wasm_bindgen_bug.js:201)
    at main (index.js:7)

Removing the #[serde(tag = "type")] works, changing the BigInt to a regular number works, using a smaller integer type works, and using a struct instead also works. It seems to be a funny intersection of all of these features.

WASM Reference Types

From the README:

and will go away in the future when reference types proposal lands natively in Wasm.

Given that this has now landed, what needs to be changed to take advantage of it?

`#serde(default)`

After some strange results downstream, I discovered that #serde(default) doesn't work. Here are some tests to demonstrate the issue:

#[derive(Deserialize)]
struct Person0 {
    name: String,
    things: Option<Vec<u32>>,
}

#[derive(Deserialize)]
struct Person1 {
    name: String,
    #[serde(default)]
    things: Vec<u32>,
}

#[wasm_bindgen_test]
fn serde_default() {
    let text = "{\"name\": \"Jack\"}";
    let js = JSON::parse(text).unwrap();

    assert!(from_value::<Person0>(js.clone()).is_ok());
    assert!(from_value::<Person1>(js).is_ok());
}

We would expect both Person0 and Person1 to deserialize from the same source String, but Person1 fails with a very strange message that Reflect.get called on a non-object.

Now I see that PR #14 exists... would it fix this issue?

implement i64/u64 using BigInt

I tried to implement this feature myself, but I literally am using rust for the first time today.

Serializing the data is simple, by now it's implemented in serde. Sadly I have no idea how to implement the corresponding deserialization of it. Don't want to create a halfhearted PR tho...

Could `from_value` borrow the `JsValue` instead?

Hi there, I've hit a scenario where I'd like to decode the same JsValue a number of times, but it seems that from_value wants full ownership of its argument. Is there a technical reason why the arg can't be borrowed instead? For instance, the good ol' JsValue::into_serde does borrow its arg.

Cheers and thank you!

Add support for serializing and deserializing JsValue?

Let say we have a struct in Rust as follows:

struct Test {
   v1: JsValue,
   v2: JsValue,
}
let v1 = JsValue::from("hello");
let v2 = JsValue::from("world");
let t = Test { v1, v2 };

It would be nice to be able to pass t back and forth between Rust and Javascript, where during "serialization" v1 and v2 are just replaced with the actual Javascript values they represent, and on deserialization, the Javascript values are pushed onto wasm-bindgen's heap and the struct members are initialized with JsValues that reference those Javascript objects on the heap.

At the moment, this doesn't work out of the box since Serialize is not implemented for JsValue, which makes sense since it's internal representation is just an index to wasm-bindgen's heap, but perhaps there is a way to hack this, possibly with #[serde(serialize_with = "path")] on the JsValue fields?

`serde-wasm-bindgen` doesn't handle `serde(deny_unknown_fields)` properly

An example:

use wasm_bindgen_test::*;
use serde::{Deserialize, Serialize};

wasm_bindgen_test_configure!(run_in_browser);

#[wasm_bindgen_test]
fn test_deny_unknown_fields() {
    #[derive(Debug, Deserialize, Serialize)]
    #[serde(deny_unknown_fields)]
    pub struct RPCErrorExtra {
        pub code: i32,
        pub message: String,
        pub data: Option<serde_json::Value>,
        pub stack: Option<serde_json::Value>,
    }

    let serializer = serde_wasm_bindgen::Serializer::json_compatible();

    let json = r#"{"code": 1, "message": "", "extra": true}"#;
    let json_value = serde_json::from_str::<serde_json::Value>(json).unwrap();
    let js_value = json_value.serialize(&serializer).unwrap();
    // This should fail, but it doesn't.
    serde_wasm_bindgen::from_value::<RPCErrorExtra>(js_value).unwrap_err();
}

Panics with:

panicked at 'called Result::unwrap_err() on an Ok value: RPCErrorExtra { code: 1, message: "", data: None, stack: None }

json_compatible Deserializer

serde_wasm_bindgen::Serializer::json_compatible exists but there is no equivalent for Deserializer.

let date = js_sys::Date::new_0();
let object = js_sys::Object::new();
js_sys::Reflect::set(&object, &"date".into(), &date).unwrap();
gloo::console::log!(&object);
let value: serde_json::Value = serde_wasm_bindgen::from_value(object.into()).unwrap();
gloo::console::log!(format!("{value:?}"));

This logs:
image

I would like there to be a way to deserialize a date somehow.

HashMap is not properly serialized to a JS object.

I don't know if this feature was meant to be suported, but I noticed that a rust struct that contains
a HashMap doesn't get serialized to a

 {"key":"value"}

object but maps the HashMap to an empty object instead.

Example:

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SomeStruct {
    pub name: String,
    pub data: HashMap<String, String>,
}

fn test_serialize() {
    let name = "Foo".to_string();
    let mut data = HashMap::<String, String>::new();
    data.insert("professor".to_string(), "Stephen Hawking".to_string());
    let rust_struct = SomeStruct { name, data };

    let serialized_1 = serde_wasm_bindgen::to_value(&rust_struct).unwrap();

    let serialized_2 = JsValue::from_serde(&rust_struct).unwrap();

    log!("{:?}", serialized_1); // Output: JsValue(Object({"name":"Foo","data":{}}))
    log!("{:?}", serialized_2); // Output: JsValue(Object({"name":"Foo","data":{"professor":"Stephen Hawking"}}))

}

Internally tagged enums are not working

I was trying this library out and I noticed that internal tagging is not deserializing properly. I get runtime errors after simply serializing to a JsValue and deserializing from it.

I looked through your code and found the internally tagged enum test, but it looks like you guys aren't expanding the #[serde(tag = "tag")] attribute in your test_enum! { ... } macro.

I started getting compile errors in the tests/serde.rs file after I added the expansion to the macro_rules block:

macro_rules! test_enum {
    ($(# $attr:tt)* $name:ident) => {{
        #[derive(Debug, PartialEq, Serialize, Deserialize)]
        $(# $attr)*    // RIGHT HERE
        enum $name<A, B> {
            Unit,
            Newtype(A),
            Tuple(A, B),
            Struct { a: A, b: B },
        }

        test_via_json($name::Unit::<(), ()>);
        test_via_json($name::Newtype::<_, ()>("newtype content".to_string()));
        test_via_json($name::Tuple("tuple content".to_string(), 42));
        test_via_json($name::Struct {
            a: "struct content".to_string(),
            b: 42,
        });
    }};
}

Console output:

serde-wasm-bindgen$ wasm-pack test --node
[INFO]: Checking for the Wasm target...
   Compiling serde-wasm-bindgen v0.1.3 (serde-wasm-bindgen)
error: #[serde(tag = "...")] cannot be used with tuple variants

error[E0277]: the trait bound `enums::InternallyTagged<(), ()>: enums::_IMPL_DESERIALIZE_FOR_ExternallyTagged::_serde::Serialize` is not satisfied
   --> tests/serde.rs:89:23
    |
39  |   fn test_via_json<T>(value: T)
    |      -------------
40  |   where
41  |       T: Serialize + DeserializeOwned + PartialEq + Debug,
    |          --------- required by this bound in `test_via_json`
...
89  |           test_via_json($name::Unit::<(), ()>);
    |                         ^^^^^^^^^^^^^^^^^^^^^ the trait `enums::_IMPL_DESERIALIZE_FOR_ExternallyTagged::_serde::Serialize` is not implemented for `enums::InternallyTagged<(), ()>`
...
218 | /     test_enum! {
219 | |         #[serde(tag = "tag")]
220 | |         InternallyTagged
221 | |     }
    | |_____- in this macro invocation

These errors do make sense because internally tagged enums don't work with tuple variants. It's just that they weren't being tested before.

I'm looking at this bug to see if I can fix it, but unfortunately I don't know much about serde. I'm not sure if it's a simple fix or not, but if I figure anything out I'll open a PR.

In the meantime, if you guys have any thoughts or guidance on this, I'd appreciate it!

TypeScript

Not sure quite what the required level of madness would be, but it would be hot to be able to write something like:

#[serde_wasm_bindgen]
pub fn pass_value_to_js() -> Result<MyRustStruct, JsValue> {
    // ...
}

that gets expanded to

#[wasm_bindgen]
pub fn pass_value_to_js() -> Result<JsValue, JsValue> {
    serde_wasm_bindgen::to_value(
        // ...
    ) // plus a type guard
}

PLUS a TypeScript .d.ts definition file...

Suspected low-level issue with `HashMap` -> JS `Map` Conversion

I have stumbled upon a tricky bug that manifests itself differently in different browsers.

To the point, there may be an issue with encoding Rust HashMaps as Javascript Map values (as serde-wasm-bindgen seems to be doing). Without having looked at how it does that, I've boiled the issue down to the following sample. This Rust code:

#[derive(Debug, Serialize, Deserialize)]
struct Foo {
    foo: HashMap<String, bool>,
}

fn foo() {
    let mut foo = HashMap::new();
    foo.insert("Jack".to_string(), true);
    let thing = Foo { foo };
    let js = swb::to_value(&thing).unwrap();
    let st = js_sys::JSON::stringify(&js).unwrap();
    log!("[Rust] ", thing);
    log!("[swb::to_value] ", js);
    log!("[JSON::stringify] ", st);
    log!("[swb::from_value] ", swb::from_value::<Foo>(js).unwrap());
    let sj = serde_json::to_string(&thing).unwrap();
    log!("[serde_json] ", sj);
    log!("[JSON::parse] ", js_sys::JSON::parse(&sj));
}

produces the following console output:

"[Rust] " Foo {
    foo: {
        "Jack": true,
    },
}
"[swb::to_value] " JsValue(Object({"foo":{}}))
"[JSON::stringify] " "{\"foo\":{}}"
"[swb::from_value] " Foo {
    foo: {
        "Jack": true,
    },
}
"[serde_json] " "{\"foo\":{\"Jack\":true}}"
"[JSON::parse] " Ok(
    JsValue(Object({"foo":{"Jack":true}})),
)

Notice that having ran to_value, the internal Map either isn't respecting Debug, or somehow lost the values. The stringify line after that suggests lost values, except that upon decoding (the from_value), the HashMap has miraculously returned. The last two were sanity checks - serde_json chooses to render HashMaps into plain Objects by default, which also seem to print properly via Debug.

Now, this is manifesting an issue in a browser extension I'm building. Data can be transmitted between Extension Components easily via the "ports" API, which blends well with serde-wasm-bindgen since it's all JsValues. I've had no issue sending over serialized structs that contain HashMap fields until last week, when the struct I tried to send over was initially fetched from an Arc<Mutex<...>> wrapping. Since to_value only needs & this shouldn't be an issue, but when I send the data to the other component... poof, the HashMaps deserialize as completely empty. Note that this only occurs in Chrome.

Do you have any idea as why that might be?

For now I'm going to try the serialize_maps_as_objects(true) option listed in the README.

ReferenceType integration?

Hi, now that there's a draft of WebAssembly 2.0, including reference types, are you planning to employ them here? If not yet, is that pending something in Rust or otherwise? I work on a runtime that supports reference types, but not really found serdes that use it..

Structures containing usize seems to create BigInt dependency in generated JavaScript bindings

Describe the Bug

For some reason, having a usize in a structure seems to result in bindings that have a dependency on BigInt, but using u32 instead of the usize does not trigger the same dependency. Because usize is the same thing as u32 on JavaScript platforms, I would expect these to create identical results.

Steps to Reproduce

  1. Unpack wasm-bindgen-bigint-issue.zip (wasm-bindgen-bigint-issue.zip)
  2. wasm-pack build --target nodejs
  3. Scrutinize the generated wasm_bindgen_bigint_issue.js
  4. In src/lib.rs, change pub val: usize to pub val: u32
  5. wasm-pack build --target nodejs
  6. Scrutinize the generated wasm_bindgen_bigint_issue.js

Expected Behavior

I would expect the wasm_bindgen_bigint_issue.js generated by steps 3 and 6 to be identical.

Actual Behavior

The two files are not identical; there are two snippets present when val is a usize:

module.exports.__wbindgen_bigint_from_u64 = function(arg0) {
    const ret = BigInt.asUintN(64, arg0);
    return addHeapObject(ret);
};
module.exports.__wbindgen_error_new = function(arg0, arg1) {
    const ret = new Error(getStringFromWasm0(arg0, arg1));
    return addHeapObject(ret);
};

I first reported this to the wasm-bindgen people but they identified it as pertaining to this project (rustwasm/wasm-bindgen#3564)

as_bytes() does not work if self is an Array

In workers-rs:284 we have a problem converting JSON of a tuple struct's content. The tuple struct has its own deserialize method, which calls deserialize_bytes(). The thing it calls deserialize_bytes() on looks like

JsValue([230, 114, 9, 67, 96, 183, 184, 19, 98, 50, 100, 228, 98, 181, 82, 4])

But as_bytes() handles only Uint8Array and ArrayBuffer, not Array, and so it fails to convert and deserialization fails. If I make the following change, things work:

diff --git a/src/de.rs b/src/de.rs
index 6322045..7a6e7db 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -175,6 +175,9 @@ impl Deserializer {
         } else if let Some(v) = self.value.dyn_ref::<ArrayBuffer>() {
             temp = Uint8Array::new(v);
             &temp
+        } else if let Some(v) = self.value.dyn_ref::<Array>() {
+            temp = Uint8Array::new(v);
+            &temp
         } else {
             return None;
         };

AFAIK there is nothing wrong with what the caller is doing and supporting Array as well seems reasonable, but I am no expert in this area!

Preserved value de-serialization error

Hi,

I'm trying to use the js value preservation, but I'm getting an error. For this code:

#[serde(with = "serde_wasm_bindgen::preserve")]
    pub created: JsValue,

I'm getting the following error:

Error: Error: invalid type: JsValue(Object({"_seconds":1702379961,"_nanoseconds":123000000})), expected tuple struct PreservedValueDeWrapper

Any ideas?

Broken in rustc 1.54.0?

I'm seeing the following error message when compiling with rust v 1.54:

   Compiling serde-wasm-bindgen v0.3.0
error: `impl` item signature doesn't match `trait` item signature
   --> /Users/arv/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-wasm-bindgen-0.3.0/src/error.rs:57:5
    |
57  |     fn from(error: &Error) -> &JsValue {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&error::Error) -> &wasm_bindgen::JsValue`
    |
    = note: expected `fn(&error::Error) -> &wasm_bindgen::JsValue`
               found `fn(&error::Error) -> &wasm_bindgen::JsValue`
    = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`
    = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output

error[E0308]: mismatched types
  --> /Users/arv/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-wasm-bindgen-0.3.0/src/error.rs:58:9
   |
58 |         error.0
   |         ^^^^^^^
   |         |
   |         expected `&wasm_bindgen::JsValue`, found struct `wasm_bindgen::JsValue`
   |         help: consider borrowing here: `&error.0`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
error: could not compile `serde-wasm-bindgen`

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed

~/src/repc  main ✔                                                                                                            18m  ⍉
▶ rustc --version
rustc 1.54.0 (a178d0322 2021-07-26)

Error is not compatible with `Sync`/`Send`/`anyhow::Error`

While trying to move away from wasm-bindgen's deprecated JsValue::from_serde/JsValue::into_serde via serde-wasm-bindgen in an async-heavy codebase I found that the fact that serde_wasm_bindgen::Error stores a JsValue, which in turn stores a marker::PhantomData<*mut u8> (code) effectively prevents the use with anyhow or any other code that constrains its errors by std::error::Error + Send + Sync + 'static. Especially async code tends to require error types to be Send + Sync.

Having had a glance over the implementation I would assume that the internal JsValue field of serde_wasm_bindgen::Error could be replaced with a Send + Sync representation (e.g. a plain old String). The From<Error> for JsValue implementation would then perform the conversion to JsValue dynamically.

The current reliance on JsValue can also be somewhat annoying in situations where you're performing a serde->wasm conversion, but on failure need to present/handle the error in Rust, rather than JS. A JsValue-based error is of little use here. Having a non-JsValue internal representation would also improve this use-case.

Merely the implementation of From<JsValue> for Error (code) would have to either be replaced with TryFrom<JsValue> (since a JsValue could contain a non-string value) or replace the current Error(error) with something like Error(error.as_string().expect("error message")).

Would you be open for a PR that makes these changes?

Can't figure out how to get Option<js_sys::Date> to work when serializing to JS

I have a struct containing a js_sys::Date field, which works perfectly with the preserve module:

pub struct Params {
    #[serde(
        skip_deserializing,
        default = "js_sys::Date::new_0",
        with = "serde_wasm_bindgen::preserve"
    )]
    pub testedTSAsDateTime: js_sys::Date,
}

However, I need to adopt it to hold an "undefined/null" JS value, so I tried this:

pub struct Params {
    #[serde(
        skip_deserializing
    )]
    pub testedTSAsDateTime: Option<js_sys::Date>,
}

It fails to compile:

100 | #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
| ^^^^^^^^^ the trait wasm_bindgen::JsCast is not implemented for std::option::Option<js_sys::Date>

I tried various approaches to no avail. Could you give me any hint on how to approach this issue?

Support partial deserialization

Imagine there's a structure in Js, which I know partly, but some things of it are too dynamic to be mapped to types.
Let's say in js something would look like this:

{
    dynamicData: {  someData: "whatever" },
    staticData: { someOtherData: "any" }
}

From rust perspective I would like to look at it like this:

#[derive(Deserialize)]
struct MyStaticData {
    staticData: String
}

#[derive(Deserialize)]
struct MyData {
    dynamicData: JsValue,
    staticData: MyStaticData,
}

So in this example, structure of MyStaticData is known beforehand, but for field dynamicData we don't know how will it look like at compile time, and so we'd like to just keep there JsValue without changes (and if possible, without copying).

However this will not compile, because Deserialize trait required for from_value cannot be derived for structure with JsValue, because JsValue does not implement it.

I tried to implement it myself by wrapping JsValue inside custom RawJsValue like that :

struct RawJsValue {
    value: JsValue
}

impl<'de> Deserialize<'de> for RawJsValue {
...

However I'm not sure how exactly to implement Deserialize. I figure that if serde::Deserializer would be of type serde_wasm_bindgen::Deserializer , then it's just about rewrapping value, but I don't understand how to cast deserializer to that structure from generic type. Any ideas?

[improvement] Allow overriding default serializer used in to_value

Cloudflare's Queue and Key Value implementations rely on this crate. But they both use the default serializer because of their usage of to_value:

It would be nice if the Serializer::new() used in https://github.com/RReverser/serde-wasm-bindgen/blob/main/src/lib.rs#L77-L80 could return an overridable serializer. This can be done using a mutable static.

Return as Result<JsValue, JsValue>

The docs say that a function can pass a value to javascript by returning as Result<JsValue, JsValue>, however the error type is serde_wasm_bindgen::Error

This can be worked around via map_err(|err| err.into()) but would be nice to have it be JsValue

#[serde(alias = "somename")] stopped working after 0.6

After when going from 0.5 to 0.6 it looks like alias is broken

Using:

wasm-bindgen = "0.2.91"
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
serde-wasm-bindgen = "0.6.3" //0.5 works... fails if i go higher
console_error_panic_hook="0.1.7"
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FilterAction {
    Append,
    Remove,
    Keep,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FilterType {
    Standard,
    Boundingbox,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FilterInput {
    #[serde(alias = "filtertype")]
    pub filter_action: FilterAction,
    #[serde(alias = "type")]
    pub filter_type: FilterType,
    ...

This is my input
image

get error:

panicked at src\lib.rs:228:58:
called `Result::unwrap()` on an `Err` value: Error(JsValue(Error: unknown variant `Append`, expected `Standard` or `Boundingbox`
Error: unknown variant `Append`, expected `Standard` or `Boundingbox`

Its like its setting values from json filtertype to rust filter_type when it should have used json type.

Is it a bug or have I just been lucky it been working ?

`js_sys::Function` is not serialized correctly

For some reason, testFn will always become 137 after serialisation:

use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_wasm_bindgen::Serializer;

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Test {
    #[serde(with = "serde_wasm_bindgen::preserve")]
    test_fn: Function,
}

let lib = json!({
  "org": {
    "test": Test {
      test_fn: Function::new_no_args(format!("function testFn() {{ return 1 }}").as_str())
    }
  }
});

let lib = lib.serialize(&Serializer::json_compatible()).unwrap();

I'm guessing that the crate isn't serialising the function correctly.

Add support for serializing `Closure`s

I've been trying unsuccessfully for a short while to see if I can serialize a wasm_bindgen::closure::Closure (or js_sys::Function?), and I wonder if it is possible?

Essentially I'm trying to expose a Rust struct on which the user can set various parameters - some of which are callback functions - and I would like to be able to be able to serialize that that and pass it to the JavaScript side.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.