jsontypedef / json-typedef-codegen

A CLI tool that generates code from JSON Typedef schemas
https://jsontypedef.com/docs/tools/jtd-codegen
MIT License
160 stars 31 forks source link

Go: Use private interfaces instead of generating structs for discriminating types #67

Open diamondburned opened 1 year ago

diamondburned commented 1 year ago

Proposal

This issue proposes that for the following JSON type definition:

{
  "Thing": {
    "discriminator": "type",
    "mapping": {
      "a": {
        "properties": {}
      },
      "b": {
        "properties": {}
      },
      "c": {
        "properties": {}
      }
    }
  }
}

jtd-codegen should generate the following Go code:

type Thing struct {
    Type string `json:"type"`

    // Value can be the following types:
    //
    //    - [A]
    //    - [B]
    //    - [C]
    //
    Value thingValue `json:"-"`
}

func (t *Thing) UnmarshalJSON(data []byte) error
func (t *Thing) MarshalJSON() ([]byte, error)

type thingValue interface {
    isThing()
}

type ThingA struct{}
type ThingB struct{}
type ThingC struct{}

func (ThingA) isThing() {}
func (ThingB) isThing() {}
func (ThingC) isThing() {}

The user would consume the API like so:

var t Thing

switch v := t.Value.(type) {
case ThingA:
    log.Println("thing contains type A")
case ThingB:
    log.Println("thing contains type B")
case ThingC:
    log.Println("thing contains type C")
default:
    log.Println("thing contains unknown type")
}

If the user makes a mistake in the type-switch, the compiler will complain:

var t Thing

switch v := t.Value.(type) {
case ThingA:
    log.Println("thing contains type A")
case string:
    // does not even compile
}

This issue is an alternative to issue #49.

Rationale

Pros:

Cons:

diamondburned commented 1 year ago

If I can get this project to build locally in Nix, I will probably file a PR for this.