Expand description

A crate to make working with wasm-bindgen easier.

This crate contains the wasm_bindgen_struct macro which can be used to declare wasm_bindgen types, and implement getters/setter as if they were normal rust structs. It can also be used to implement methods for the type, including inline mapping to overcome wasm-bindgen-futures limitations.

Example

// Or you can use this instead to avoid needing to import
// the macro throughout your crate
// #[macro_use]
// extern crate wasm_bindgen_struct;
use wasm_bindgen_struct::wasm_bindgen_struct;


#[wasm_bindgen_struct]
// `module` here means this type comes from the JS "my-module" module
// with the class name `aAnotherType`
#[opts(module = "my-module", js_name = "AnotherType")]
struct MyJsType {
  // field names are automatically converted to `camelCase`
  field_a: String,
  // ...but can be manually specified if needed
  #[opts(js_name = "field_b")]
  field_b: String,
}

#[wasm_bindgen_struct]
#[opts(module = "my-module")]
impl MyJsType {
  // Automatically gets the `static_method_of = MyJsType` applied,
  // as well as `catch` due to the `Result`
  fn apple() -> Result<String, JsValue>;

  // `async` functions in `wasm-bindgen` can only currently return
  // `()`, `JsValue`, or Result<_, JsValue>`, where `_` is one of
  // the two previously mentioned types. We use `MapValue<T, U>`
  // to tell the macro that the binding method should return `T`,
  // but we'll map the value to `U`
  async fn oranges(&self) -> MapValue<JsValue, String> {
    self
      .oranges_js()
      .await
      .unchecked_into::<js_sys::JsString>()
      .into()
  }
}

The above expands to:


// For getters/setters
#[wasm_bindgen(module = "my-module")]
extern "C" {
  #[wasm_bindgen(js_name = "AnotherType")]
  type MyJsType;

  #[wasm_bindgen(
    method,
    getter,
    js_class = "AnotherType",
    js_name = "fieldA"
  )]
  fn field_a(this: &MyJsType) -> String;

  #[wasm_bindgen(
    method,
    setter,
    js_class = "AnotherType",
    js_name = "fieldA"
  )]
  fn set_field_a(this: &MyJsType, value: String);

  #[wasm_bindgen(
    method,
    getter,
    js_class = "AnotherType",
    js_name = "field_b"
  )]
  fn field_b(this: &MyJsType) -> String;
  #[wasm_bindgen(
    method,
    setter,
    js_class = "AnotherType",
    js_name = "field_b"
  )]
  fn set_field_b(this: &MyJsType, value: String);
}

// And for methods
impl MyJsType {
  fn apple() -> Result<String, JsValue> {
    #[wasm_bindgen(module = "my-module")]
    extern "C" {
      #[wasm_bindgen(static_method_of = MyJsType, js_name = "apple")]
      #[wasm_bindgen(catch)]
      fn apple_js() -> Result<String, JsValue>;
    }
    Self::apple_js()
  }
  async fn oranges(&self) -> String {
    #[wasm_bindgen(module = "my-module")]
    extern "C" {
      #[wasm_bindgen(method, js_name = "oranges")]
      async fn oranges_js(this: &MyJsType) -> JsValue;
    }
    self
      .oranges_js()
      .await
      .unchecked_into::<js_sys::JsString>()
      .into()
  }
}

#[opts(...)] on structs

#[opts(...)] on struct fields

#[opts(...)] on impl

#[opts(...)] on impl methods

Treatment of Special Types

  • Result<T, E>

    Implies catch.

  • MapValue<T, U>

    Allows specifing the types of the generated wasm-bindgen binding, as well as the actual method type. This allows arbitrary transformations between the bound method and the output method.

    Example

     
    #[wasm_bindgen_struct]
    struct JsType;
    
    #[wasm_bindgen_struct]
    impl JsType {
      async fn get_async_string(&self) -> MapValue<JsValue, String> {
        self
          .get_async_string_js()
          .await
          .unchecked_into::<js_sys::JsString>()
          .into()
      }
    }

    Important note

    MapValue is a pseudo-type which only exists within the macro’s scope. i.e., this type does not exist outside of #[wasm_bindgen_struct] impl { /* ... */ } and therefore does not need to be imported.

Attribute Macros