bytecodealliance / go-modules

WebAssembly, WASI, and Component Model tools for Go
https://go.bytecodealliance.org
Apache License 2.0
78 stars 13 forks source link

wit/bindgen: JSON serialization support for record types #239

Open lxfontes opened 2 weeks ago

lxfontes commented 2 weeks ago

This started in https://github.com/bytecodealliance/go-modules/pull/225

Goal: JSON Serialize / Deserialize WIT Records based on WIT field names.

Progress

Approach

Centralize JSON serialization in the cm package, keeping codegen changes to a minimum.

Given a complex record type:

  record response {
      headers: list<tuple<string, list<string>>>,
      http-code: u16,
      body: response-body
  }
  variant response-body {
    ok(list<list-element>),
    err(function-error),
    platform-err(platform-error)
  }
  record list-element {
      optional-int: option<u64>,
      optional-bool: option<bool>,
  }
  record function-error {
      error: string
  }
  record platform-error {
    code: string,
    message: string
  }

and filling it up:

    hdrVals := cm.ToList([]string{"value1", "value2"})
    hdrTuple := cm.Tuple[string, cm.List[string]]{
        F0: "header-name",
        F1: hdrVals,
    }
    headers := cm.ToList([]cm.Tuple[string, cm.List[string]]{hdrTuple})
    v := somefunctioninterface.Response{
        Headers:  headers,
        HTTPCode: 200,
        Body:     somefunctioninterface.ResponseBodyErr(somefunctioninterface.FunctionError{Error: "failed"}),
    }

We should serialize it to JSON as:

{"headers":[["header-name",["value1","value2"]]],"http-code":200,"body":{"err":{"error":"failed"}}}

For comparison, this is what is produced today:

{"Headers":{},"Body":{"RequiredParam":"required","OptionalParam":{}}}

Type encoding

Whenever possible, reuse standard mappings. string -> string, u32 -> uint32, etc.

Tuple handling

Tuples are encoded as json arrays with explicit nulls.

Tuple[string,int] -> [ "some string", 42 ]
Tuple3[string, *string, int] -> [ "some string", null, 42 ]

Variant handling

Variants are encoded as json dictionaries, so they can carry the variant data.

 record somerecord {
   myvariant: somevar,
 }
  variant somevar {
    ok,
    err(string),
  }

// JSON
{ "myvariant": { "ok": true }}
{ "myvariant": { "error": "error value" }}

Option handling

Options rely on explicit null for the "None" case.

{ "myoptional": null } // cm.None
{ "myoptional": "this" } // cm.Option[string]

Questions

Zero Value Variants

Today they end up with tag = 0, this impacts de-serialization of variants. We need the ability to distinct between:

atm only the Some() path de-serializes correctly ( pointers ).

ydnar commented 2 weeks ago

Can we move the design and task list for this to an issue?

Then we can have discrete PRs that implement JSON support for different types?