denoland / deno_bindgen

Write high-level Deno FFI libraries in Rust.
MIT License
275 stars 28 forks source link

JSON.encode doesn't support Map #54

Closed fourtf closed 2 years ago

fourtf commented 2 years ago

Rust's HashMap type gets translated to the Map type in typescript. It is possible to use a struct containing a map as a parameter. However the generated glue code incorrectly uses JSON.stringify to convert the parameter into a string. This doesn't work on the Map member as the contained values are ignored. The code thus outputs an empty objects which doesn't contain the values of the Map.

Rust code:

use deno_bindgen::deno_bindgen;
use std::collections::HashMap;

#[deno_bindgen]
struct MyStruct {
    my_map: HashMap<String, String>,
}

#[deno_bindgen]
fn my_fn(my_struct: MyStruct) {
    println!("{:?}", my_struct.my_map);
}

Generated binding for struct:

export type MyStruct = {
  my_map: Map<string, string>
}

Faulty binding using JSON.stringify:

export function my_fn(a0: MyStruct) {
  const a0_buf = encode(JSON.stringify(a0))
  let rawResult = _lib.symbols.my_fn(a0_buf, a0_buf.byteLength)
  const result = rawResult
  return result
}

As such the follow code will not output {a:'b'} but instead {}.

import { my_fn } from "./bindings/bindings.ts";

my_fn({ my_map: new Map([["a", "b"]]) });

This could be mitigated by using Record<T, U> instead of Map<T, U> as it refers to a plain JS-Object which can be properly converted by JSON.stringify.

fourtf commented 2 years ago

For the record, I currently simply create a normal JS object and then cast it using {a: 'b'} as unknown as Map<string, string>.