From 5423dc5193e24dcaf1ab5ffe1608b1673531fc1c Mon Sep 17 00:00:00 2001 From: diamondburned Date: Mon, 13 Feb 2023 04:03:53 -0800 Subject: [PATCH 1/2] Generate Go interfaces for discriminators This commit implements jsontypedef/json-typedef-codegen/issues/67. --- crates/target_go/src/lib.rs | 117 ++++++++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 25 deletions(-) diff --git a/crates/target_go/src/lib.rs b/crates/target_go/src/lib.rs index 3c934b86..829796bd 100644 --- a/crates/target_go/src/lib.rs +++ b/crates/target_go/src/lib.rs @@ -259,27 +259,12 @@ impl jtd_codegen::target::Target for Target { writeln!(out)?; write!(out, "{}", description(&metadata, 0))?; writeln!(out, "type {} struct {{", name)?; - writeln!(out, "\t{} string", tag_field_name)?; - for variant in &variants { - writeln!(out)?; - writeln!(out, "\t{} {}", &variant.field_name, &variant.type_name)?; - } + writeln!(out, "\tValue I{} `json:\"-\"`", name)?; writeln!(out, "}}")?; writeln!(out)?; writeln!(out, "func (v {}) MarshalJSON() ([]byte, error) {{", name)?; - writeln!(out, "\tswitch v.{} {{", tag_field_name)?; - for variant in &variants { - writeln!(out, "\tcase {:?}:", variant.tag_value)?; - writeln!(out, "\t\treturn json.Marshal(struct {{ T string `json:\"{}\"`; {} }}{{ v.{}, v.{} }})", tag_json_name, variant.type_name, tag_field_name, variant.field_name)?; - } - writeln!(out, "\t}}")?; - writeln!(out)?; - writeln!( - out, - "\treturn nil, fmt.Errorf(\"bad {0} value: %s\", v.{0})", - tag_field_name - )?; + writeln!(out, "\treturn json.Marshal(v.Value)")?; writeln!(out, "}}")?; writeln!(out)?; @@ -293,21 +278,21 @@ impl jtd_codegen::target::Target for Target { writeln!(out, "\t\treturn err")?; writeln!(out, "\t}}")?; writeln!(out)?; + writeln!(out, "\tvar value I{}", name)?; writeln!(out, "\tvar err error")?; + writeln!(out)?; writeln!(out, "\tswitch t.T {{")?; for variant in &variants { writeln!(out, "\tcase {:?}:", variant.tag_value)?; - writeln!( - out, - "\t\terr = json.Unmarshal(b, &v.{})", - variant.field_name - )?; + writeln!(out, "\t\tvar v {}", variant.type_name)?; + writeln!(out, "\t\terr = json.Unmarshal(b, &v)")?; + writeln!(out, "\t\tvalue = v")?; } writeln!(out, "\tdefault:")?; writeln!( out, - "\t\terr = fmt.Errorf(\"bad {} value: %s\", t.T)", - tag_field_name + "\t\terr = fmt.Errorf(\"{}: bad {} value: %q\", t.T)", + name, tag_json_name )?; writeln!(out, "\t}}")?; writeln!(out)?; @@ -315,10 +300,92 @@ impl jtd_codegen::target::Target for Target { writeln!(out, "\t\treturn err")?; writeln!(out, "\t}}")?; writeln!(out)?; - writeln!(out, "\tv.{} = t.T", tag_field_name)?; + writeln!(out, "\tv.Value = value")?; writeln!(out, "\treturn nil")?; writeln!(out, "}}")?; + writeln!(out)?; + writeln!( + out, + "// I{} is an interface type that {} types implement.", + name, name + )?; + writeln!(out, "// It can be the following types:")?; + writeln!(out, "//")?; + for variant in &variants { + writeln!( + out, + "// - [{}] ({})", + &variant.type_name, &variant.tag_value + )?; + } + writeln!(out, "//")?; + writeln!(out, "type I{} interface {{", name)?; + writeln!(out, "\t{}() string", tag_field_name)?; + writeln!(out, "\tis{}()", name)?; + writeln!(out, "}}")?; + writeln!(out)?; + + for variant in &variants { + writeln!( + out, + "func ({}) {}() string {{ return {:?} }}", + variant.type_name, tag_field_name, variant.tag_value + )?; + } + writeln!(out)?; + + for variant in &variants { + writeln!(out, "func ({}) is{}() {{}}", variant.type_name, name)?; + } + writeln!(out)?; + + for variant in &variants { + writeln!( + out, + "func (v {}) MarshalJSON() ([]byte, error) {{", + variant.type_name + )?; + writeln!(out, "\ttype Alias {}", variant.type_name)?; + writeln!(out, "\treturn json.Marshal(struct {{")?; + writeln!(out, "\t\tT string `json:\"{}\"`", tag_json_name)?; + writeln!(out, "\t\tAlias")?; + writeln!(out, "\t}}{{")?; + writeln!(out, "\t\tv.{}(),", tag_field_name)?; + writeln!(out, "\t\tAlias(v),")?; + writeln!(out, "\t}})")?; + writeln!(out, "}}")?; + writeln!(out)?; + + writeln!( + out, + "func (v *{}) UnmarshalJSON(b []byte) error {{", + variant.type_name + )?; + writeln!(out, "\ttype Alias {}", variant.type_name)?; + writeln!(out, "\tvar a struct {{")?; + writeln!(out, "\t\tT string `json:\"{}\"`", tag_json_name)?; + writeln!(out, "\t\tAlias")?; + writeln!(out, "\t}}")?; + writeln!(out)?; + writeln!(out, "\tif err := json.Unmarshal(b, &a); err != nil {{")?; + writeln!(out, "\t\treturn err")?; + writeln!(out, "\t}}")?; + writeln!(out)?; + writeln!(out, "\tif a.T != {:?} {{", variant.tag_value)?; + writeln!( + out, + "\t\treturn fmt.Errorf(\"{}: bad {} value: %q\", a.T)", + variant.type_name, tag_json_name + )?; + writeln!(out, "\t}}")?; + writeln!(out)?; + writeln!(out, "\t*v = {}(a.Alias)", variant.type_name)?; + writeln!(out, "\treturn nil")?; + writeln!(out, "}}")?; + writeln!(out)?; + } + None } From 4e5ee4c02b8f18bd5e15ebcc04a1f31842c24403 Mon Sep 17 00:00:00 2001 From: Ryan Atallah Date: Fri, 19 May 2023 19:20:59 -0400 Subject: [PATCH 2/2] Regenerate tests --- .../basic_discriminator/jtd_codegen_e2e.go | 107 ++++- .../output/description/jtd_codegen_e2e.go | 66 ++- .../jtd_codegen_e2e.go | 66 ++- .../output/geojson/jtd_codegen_e2e.go | 394 +++++++++++++++--- .../nullable_discriminator/jtd_codegen_e2e.go | 107 ++++- 5 files changed, 630 insertions(+), 110 deletions(-) diff --git a/crates/target_go/output/basic_discriminator/jtd_codegen_e2e.go b/crates/target_go/output/basic_discriminator/jtd_codegen_e2e.go index fe8bc71b..7b69b1f6 100644 --- a/crates/target_go/output/basic_discriminator/jtd_codegen_e2e.go +++ b/crates/target_go/output/basic_discriminator/jtd_codegen_e2e.go @@ -8,22 +8,11 @@ import ( ) type Root struct { - Foo string - - BarBaz RootBarBaz - - Quux RootQuux + Value IRoot `json:"-"` } func (v Root) MarshalJSON() ([]byte, error) { - switch v.Foo { - case "BAR_BAZ": - return json.Marshal(struct { T string `json:"foo"`; RootBarBaz }{ v.Foo, v.BarBaz }) - case "QUUX": - return json.Marshal(struct { T string `json:"foo"`; RootQuux }{ v.Foo, v.Quux }) - } - - return nil, fmt.Errorf("bad Foo value: %s", v.Foo) + return json.Marshal(v.Value) } func (v *Root) UnmarshalJSON(b []byte) error { @@ -32,24 +21,108 @@ func (v *Root) UnmarshalJSON(b []byte) error { return err } + var value IRoot var err error + switch t.T { case "BAR_BAZ": - err = json.Unmarshal(b, &v.BarBaz) + var v RootBarBaz + err = json.Unmarshal(b, &v) + value = v case "QUUX": - err = json.Unmarshal(b, &v.Quux) + var v RootQuux + err = json.Unmarshal(b, &v) + value = v default: - err = fmt.Errorf("bad Foo value: %s", t.T) + err = fmt.Errorf("Root: bad foo value: %q", t.T) } if err != nil { return err } - v.Foo = t.T + v.Value = value + return nil +} + +// IRoot is an interface type that Root types implement. +// It can be the following types: +// +// - [RootBarBaz] (BAR_BAZ) +// - [RootQuux] (QUUX) +// +type IRoot interface { + Foo() string + isRoot() +} + +func (RootBarBaz) Foo() string { return "BAR_BAZ" } +func (RootQuux) Foo() string { return "QUUX" } + +func (RootBarBaz) isRoot() {} +func (RootQuux) isRoot() {} + +func (v RootBarBaz) MarshalJSON() ([]byte, error) { + type Alias RootBarBaz + return json.Marshal(struct { + T string `json:"foo"` + Alias + }{ + v.Foo(), + Alias(v), + }) +} + +func (v *RootBarBaz) UnmarshalJSON(b []byte) error { + type Alias RootBarBaz + var a struct { + T string `json:"foo"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "BAR_BAZ" { + return fmt.Errorf("RootBarBaz: bad foo value: %q", a.T) + } + + *v = RootBarBaz(a.Alias) + return nil +} + +func (v RootQuux) MarshalJSON() ([]byte, error) { + type Alias RootQuux + return json.Marshal(struct { + T string `json:"foo"` + Alias + }{ + v.Foo(), + Alias(v), + }) +} + +func (v *RootQuux) UnmarshalJSON(b []byte) error { + type Alias RootQuux + var a struct { + T string `json:"foo"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "QUUX" { + return fmt.Errorf("RootQuux: bad foo value: %q", a.T) + } + + *v = RootQuux(a.Alias) return nil } + type RootBarBaz struct { Baz string `json:"baz"` } diff --git a/crates/target_go/output/description/jtd_codegen_e2e.go b/crates/target_go/output/description/jtd_codegen_e2e.go index f0117b26..7b6a33b1 100644 --- a/crates/target_go/output/description/jtd_codegen_e2e.go +++ b/crates/target_go/output/description/jtd_codegen_e2e.go @@ -9,18 +9,11 @@ import ( // A description for discriminator type RootDiscriminatorWithDescription struct { - Foo string - - Bar RootDiscriminatorWithDescriptionBar + Value IRootDiscriminatorWithDescription `json:"-"` } func (v RootDiscriminatorWithDescription) MarshalJSON() ([]byte, error) { - switch v.Foo { - case "bar": - return json.Marshal(struct { T string `json:"foo"`; RootDiscriminatorWithDescriptionBar }{ v.Foo, v.Bar }) - } - - return nil, fmt.Errorf("bad Foo value: %s", v.Foo) + return json.Marshal(v.Value) } func (v *RootDiscriminatorWithDescription) UnmarshalJSON(b []byte) error { @@ -29,22 +22,71 @@ func (v *RootDiscriminatorWithDescription) UnmarshalJSON(b []byte) error { return err } + var value IRootDiscriminatorWithDescription var err error + switch t.T { case "bar": - err = json.Unmarshal(b, &v.Bar) + var v RootDiscriminatorWithDescriptionBar + err = json.Unmarshal(b, &v) + value = v default: - err = fmt.Errorf("bad Foo value: %s", t.T) + err = fmt.Errorf("RootDiscriminatorWithDescription: bad foo value: %q", t.T) } if err != nil { return err } - v.Foo = t.T + v.Value = value + return nil +} + +// IRootDiscriminatorWithDescription is an interface type that RootDiscriminatorWithDescription types implement. +// It can be the following types: +// +// - [RootDiscriminatorWithDescriptionBar] (bar) +// +type IRootDiscriminatorWithDescription interface { + Foo() string + isRootDiscriminatorWithDescription() +} + +func (RootDiscriminatorWithDescriptionBar) Foo() string { return "bar" } + +func (RootDiscriminatorWithDescriptionBar) isRootDiscriminatorWithDescription() {} + +func (v RootDiscriminatorWithDescriptionBar) MarshalJSON() ([]byte, error) { + type Alias RootDiscriminatorWithDescriptionBar + return json.Marshal(struct { + T string `json:"foo"` + Alias + }{ + v.Foo(), + Alias(v), + }) +} + +func (v *RootDiscriminatorWithDescriptionBar) UnmarshalJSON(b []byte) error { + type Alias RootDiscriminatorWithDescriptionBar + var a struct { + T string `json:"foo"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "bar" { + return fmt.Errorf("RootDiscriminatorWithDescriptionBar: bad foo value: %q", a.T) + } + + *v = RootDiscriminatorWithDescriptionBar(a.Alias) return nil } + // A description for discriminator variant type RootDiscriminatorWithDescriptionBar struct { } diff --git a/crates/target_go/output/discriminator_optional_properties/jtd_codegen_e2e.go b/crates/target_go/output/discriminator_optional_properties/jtd_codegen_e2e.go index 2ff99da9..1e1d64f7 100644 --- a/crates/target_go/output/discriminator_optional_properties/jtd_codegen_e2e.go +++ b/crates/target_go/output/discriminator_optional_properties/jtd_codegen_e2e.go @@ -8,18 +8,11 @@ import ( ) type Root struct { - Foo string - - Bar RootBar + Value IRoot `json:"-"` } func (v Root) MarshalJSON() ([]byte, error) { - switch v.Foo { - case "bar": - return json.Marshal(struct { T string `json:"foo"`; RootBar }{ v.Foo, v.Bar }) - } - - return nil, fmt.Errorf("bad Foo value: %s", v.Foo) + return json.Marshal(v.Value) } func (v *Root) UnmarshalJSON(b []byte) error { @@ -28,22 +21,71 @@ func (v *Root) UnmarshalJSON(b []byte) error { return err } + var value IRoot var err error + switch t.T { case "bar": - err = json.Unmarshal(b, &v.Bar) + var v RootBar + err = json.Unmarshal(b, &v) + value = v default: - err = fmt.Errorf("bad Foo value: %s", t.T) + err = fmt.Errorf("Root: bad foo value: %q", t.T) } if err != nil { return err } - v.Foo = t.T + v.Value = value + return nil +} + +// IRoot is an interface type that Root types implement. +// It can be the following types: +// +// - [RootBar] (bar) +// +type IRoot interface { + Foo() string + isRoot() +} + +func (RootBar) Foo() string { return "bar" } + +func (RootBar) isRoot() {} + +func (v RootBar) MarshalJSON() ([]byte, error) { + type Alias RootBar + return json.Marshal(struct { + T string `json:"foo"` + Alias + }{ + v.Foo(), + Alias(v), + }) +} + +func (v *RootBar) UnmarshalJSON(b []byte) error { + type Alias RootBar + var a struct { + T string `json:"foo"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "bar" { + return fmt.Errorf("RootBar: bad foo value: %q", a.T) + } + + *v = RootBar(a.Alias) return nil } + type RootBar struct { Baz []string `json:"baz,omitempty"` diff --git a/crates/target_go/output/geojson/jtd_codegen_e2e.go b/crates/target_go/output/geojson/jtd_codegen_e2e.go index dc8ebee2..924ad978 100644 --- a/crates/target_go/output/geojson/jtd_codegen_e2e.go +++ b/crates/target_go/output/geojson/jtd_codegen_e2e.go @@ -96,50 +96,11 @@ type BoundingBox = []float64 // GeoJSON processors MAY interpret Geometry objects with empty // "coordinates" arrays as null objects. type GeojsonObject struct { - Type string - - Feature GeojsonObjectFeature - - FeatureCollection GeojsonObjectFeatureCollection - - GeometryCollection GeojsonObjectGeometryCollection - - LineString GeojsonObjectLineString - - MultiLineString GeojsonObjectMultiLineString - - MultiPoint GeojsonObjectMultiPoint - - MultiPolygon GeojsonObjectMultiPolygon - - Point GeojsonObjectPoint - - Polygon GeojsonObjectPolygon + Value IGeojsonObject `json:"-"` } func (v GeojsonObject) MarshalJSON() ([]byte, error) { - switch v.Type { - case "Feature": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectFeature }{ v.Type, v.Feature }) - case "FeatureCollection": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectFeatureCollection }{ v.Type, v.FeatureCollection }) - case "GeometryCollection": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectGeometryCollection }{ v.Type, v.GeometryCollection }) - case "LineString": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectLineString }{ v.Type, v.LineString }) - case "MultiLineString": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectMultiLineString }{ v.Type, v.MultiLineString }) - case "MultiPoint": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectMultiPoint }{ v.Type, v.MultiPoint }) - case "MultiPolygon": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectMultiPolygon }{ v.Type, v.MultiPolygon }) - case "Point": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectPoint }{ v.Type, v.Point }) - case "Polygon": - return json.Marshal(struct { T string `json:"type"`; GeojsonObjectPolygon }{ v.Type, v.Polygon }) - } - - return nil, fmt.Errorf("bad Type value: %s", v.Type) + return json.Marshal(v.Value) } func (v *GeojsonObject) UnmarshalJSON(b []byte) error { @@ -148,38 +109,367 @@ func (v *GeojsonObject) UnmarshalJSON(b []byte) error { return err } + var value IGeojsonObject var err error + switch t.T { case "Feature": - err = json.Unmarshal(b, &v.Feature) + var v GeojsonObjectFeature + err = json.Unmarshal(b, &v) + value = v case "FeatureCollection": - err = json.Unmarshal(b, &v.FeatureCollection) + var v GeojsonObjectFeatureCollection + err = json.Unmarshal(b, &v) + value = v case "GeometryCollection": - err = json.Unmarshal(b, &v.GeometryCollection) + var v GeojsonObjectGeometryCollection + err = json.Unmarshal(b, &v) + value = v case "LineString": - err = json.Unmarshal(b, &v.LineString) + var v GeojsonObjectLineString + err = json.Unmarshal(b, &v) + value = v case "MultiLineString": - err = json.Unmarshal(b, &v.MultiLineString) + var v GeojsonObjectMultiLineString + err = json.Unmarshal(b, &v) + value = v case "MultiPoint": - err = json.Unmarshal(b, &v.MultiPoint) + var v GeojsonObjectMultiPoint + err = json.Unmarshal(b, &v) + value = v case "MultiPolygon": - err = json.Unmarshal(b, &v.MultiPolygon) + var v GeojsonObjectMultiPolygon + err = json.Unmarshal(b, &v) + value = v case "Point": - err = json.Unmarshal(b, &v.Point) + var v GeojsonObjectPoint + err = json.Unmarshal(b, &v) + value = v case "Polygon": - err = json.Unmarshal(b, &v.Polygon) + var v GeojsonObjectPolygon + err = json.Unmarshal(b, &v) + value = v default: - err = fmt.Errorf("bad Type value: %s", t.T) + err = fmt.Errorf("GeojsonObject: bad type value: %q", t.T) } if err != nil { return err } - v.Type = t.T + v.Value = value + return nil +} + +// IGeojsonObject is an interface type that GeojsonObject types implement. +// It can be the following types: +// +// - [GeojsonObjectFeature] (Feature) +// - [GeojsonObjectFeatureCollection] (FeatureCollection) +// - [GeojsonObjectGeometryCollection] (GeometryCollection) +// - [GeojsonObjectLineString] (LineString) +// - [GeojsonObjectMultiLineString] (MultiLineString) +// - [GeojsonObjectMultiPoint] (MultiPoint) +// - [GeojsonObjectMultiPolygon] (MultiPolygon) +// - [GeojsonObjectPoint] (Point) +// - [GeojsonObjectPolygon] (Polygon) +// +type IGeojsonObject interface { + Type() string + isGeojsonObject() +} + +func (GeojsonObjectFeature) Type() string { return "Feature" } +func (GeojsonObjectFeatureCollection) Type() string { return "FeatureCollection" } +func (GeojsonObjectGeometryCollection) Type() string { return "GeometryCollection" } +func (GeojsonObjectLineString) Type() string { return "LineString" } +func (GeojsonObjectMultiLineString) Type() string { return "MultiLineString" } +func (GeojsonObjectMultiPoint) Type() string { return "MultiPoint" } +func (GeojsonObjectMultiPolygon) Type() string { return "MultiPolygon" } +func (GeojsonObjectPoint) Type() string { return "Point" } +func (GeojsonObjectPolygon) Type() string { return "Polygon" } + +func (GeojsonObjectFeature) isGeojsonObject() {} +func (GeojsonObjectFeatureCollection) isGeojsonObject() {} +func (GeojsonObjectGeometryCollection) isGeojsonObject() {} +func (GeojsonObjectLineString) isGeojsonObject() {} +func (GeojsonObjectMultiLineString) isGeojsonObject() {} +func (GeojsonObjectMultiPoint) isGeojsonObject() {} +func (GeojsonObjectMultiPolygon) isGeojsonObject() {} +func (GeojsonObjectPoint) isGeojsonObject() {} +func (GeojsonObjectPolygon) isGeojsonObject() {} + +func (v GeojsonObjectFeature) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectFeature + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectFeature) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectFeature + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "Feature" { + return fmt.Errorf("GeojsonObjectFeature: bad type value: %q", a.T) + } + + *v = GeojsonObjectFeature(a.Alias) + return nil +} + +func (v GeojsonObjectFeatureCollection) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectFeatureCollection + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectFeatureCollection) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectFeatureCollection + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "FeatureCollection" { + return fmt.Errorf("GeojsonObjectFeatureCollection: bad type value: %q", a.T) + } + + *v = GeojsonObjectFeatureCollection(a.Alias) + return nil +} + +func (v GeojsonObjectGeometryCollection) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectGeometryCollection + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectGeometryCollection) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectGeometryCollection + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "GeometryCollection" { + return fmt.Errorf("GeojsonObjectGeometryCollection: bad type value: %q", a.T) + } + + *v = GeojsonObjectGeometryCollection(a.Alias) + return nil +} + +func (v GeojsonObjectLineString) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectLineString + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectLineString) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectLineString + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "LineString" { + return fmt.Errorf("GeojsonObjectLineString: bad type value: %q", a.T) + } + + *v = GeojsonObjectLineString(a.Alias) + return nil +} + +func (v GeojsonObjectMultiLineString) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectMultiLineString + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectMultiLineString) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectMultiLineString + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "MultiLineString" { + return fmt.Errorf("GeojsonObjectMultiLineString: bad type value: %q", a.T) + } + + *v = GeojsonObjectMultiLineString(a.Alias) + return nil +} + +func (v GeojsonObjectMultiPoint) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectMultiPoint + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectMultiPoint) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectMultiPoint + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "MultiPoint" { + return fmt.Errorf("GeojsonObjectMultiPoint: bad type value: %q", a.T) + } + + *v = GeojsonObjectMultiPoint(a.Alias) + return nil +} + +func (v GeojsonObjectMultiPolygon) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectMultiPolygon + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectMultiPolygon) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectMultiPolygon + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "MultiPolygon" { + return fmt.Errorf("GeojsonObjectMultiPolygon: bad type value: %q", a.T) + } + + *v = GeojsonObjectMultiPolygon(a.Alias) return nil } +func (v GeojsonObjectPoint) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectPoint + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectPoint) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectPoint + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "Point" { + return fmt.Errorf("GeojsonObjectPoint: bad type value: %q", a.T) + } + + *v = GeojsonObjectPoint(a.Alias) + return nil +} + +func (v GeojsonObjectPolygon) MarshalJSON() ([]byte, error) { + type Alias GeojsonObjectPolygon + return json.Marshal(struct { + T string `json:"type"` + Alias + }{ + v.Type(), + Alias(v), + }) +} + +func (v *GeojsonObjectPolygon) UnmarshalJSON(b []byte) error { + type Alias GeojsonObjectPolygon + var a struct { + T string `json:"type"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "Polygon" { + return fmt.Errorf("GeojsonObjectPolygon: bad type value: %q", a.T) + } + + *v = GeojsonObjectPolygon(a.Alias) + return nil +} + + // A Feature object represents a spatially bounded thing. Every // Feature object is a GeoJSON object no matter where it occurs in a // GeoJSON text. diff --git a/crates/target_go/output/nullable_discriminator/jtd_codegen_e2e.go b/crates/target_go/output/nullable_discriminator/jtd_codegen_e2e.go index 5f44a94f..f68bdca8 100644 --- a/crates/target_go/output/nullable_discriminator/jtd_codegen_e2e.go +++ b/crates/target_go/output/nullable_discriminator/jtd_codegen_e2e.go @@ -8,22 +8,11 @@ import ( ) type Root0 struct { - Foo string - - Bar RootBar - - Quux RootQuux + Value IRoot0 `json:"-"` } func (v Root0) MarshalJSON() ([]byte, error) { - switch v.Foo { - case "bar": - return json.Marshal(struct { T string `json:"foo"`; RootBar }{ v.Foo, v.Bar }) - case "quux": - return json.Marshal(struct { T string `json:"foo"`; RootQuux }{ v.Foo, v.Quux }) - } - - return nil, fmt.Errorf("bad Foo value: %s", v.Foo) + return json.Marshal(v.Value) } func (v *Root0) UnmarshalJSON(b []byte) error { @@ -32,24 +21,108 @@ func (v *Root0) UnmarshalJSON(b []byte) error { return err } + var value IRoot0 var err error + switch t.T { case "bar": - err = json.Unmarshal(b, &v.Bar) + var v RootBar + err = json.Unmarshal(b, &v) + value = v case "quux": - err = json.Unmarshal(b, &v.Quux) + var v RootQuux + err = json.Unmarshal(b, &v) + value = v default: - err = fmt.Errorf("bad Foo value: %s", t.T) + err = fmt.Errorf("Root0: bad foo value: %q", t.T) } if err != nil { return err } - v.Foo = t.T + v.Value = value + return nil +} + +// IRoot0 is an interface type that Root0 types implement. +// It can be the following types: +// +// - [RootBar] (bar) +// - [RootQuux] (quux) +// +type IRoot0 interface { + Foo() string + isRoot0() +} + +func (RootBar) Foo() string { return "bar" } +func (RootQuux) Foo() string { return "quux" } + +func (RootBar) isRoot0() {} +func (RootQuux) isRoot0() {} + +func (v RootBar) MarshalJSON() ([]byte, error) { + type Alias RootBar + return json.Marshal(struct { + T string `json:"foo"` + Alias + }{ + v.Foo(), + Alias(v), + }) +} + +func (v *RootBar) UnmarshalJSON(b []byte) error { + type Alias RootBar + var a struct { + T string `json:"foo"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "bar" { + return fmt.Errorf("RootBar: bad foo value: %q", a.T) + } + + *v = RootBar(a.Alias) + return nil +} + +func (v RootQuux) MarshalJSON() ([]byte, error) { + type Alias RootQuux + return json.Marshal(struct { + T string `json:"foo"` + Alias + }{ + v.Foo(), + Alias(v), + }) +} + +func (v *RootQuux) UnmarshalJSON(b []byte) error { + type Alias RootQuux + var a struct { + T string `json:"foo"` + Alias + } + + if err := json.Unmarshal(b, &a); err != nil { + return err + } + + if a.T != "quux" { + return fmt.Errorf("RootQuux: bad foo value: %q", a.T) + } + + *v = RootQuux(a.Alias) return nil } + type RootBar struct { Baz string `json:"baz"` }