Skip to content

Commit a8dbd1b

Browse files
committed
Clean up oneOf interface implementation.
1 parent 168eb3d commit a8dbd1b

File tree

2 files changed

+53
-53
lines changed

2 files changed

+53
-53
lines changed

internal/generate/types.go

Lines changed: 39 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package main
66

77
import (
8-
"encoding/json"
98
"fmt"
109
"maps"
1110
"os"
@@ -37,16 +36,17 @@ type TypeTemplate struct {
3736
// Fields holds the information for the field
3837
Fields []TypeFields
3938

40-
// InterfaceType
41-
InterfaceType string
42-
// DiscriminatorKey
39+
// OneOfInterface is the name of the oneOf interface that this type should implement. By convention, all concrete types that implement a oneOf interface must define a single method, called "is${{.OneOfInterface}}", which accepts no arguments and has no return value.
40+
OneOfInterface string
41+
42+
// DiscriminatorKey is the name of the discriminator key used to determine the type of a complex oneOf field: commonly "type", "kind", etc.
4343
DiscriminatorKey string
44-
// DiscriminatorType
44+
// DiscriminatorType is the generated type name of the discriminator field, and should be constructed as "{{.OneOfName}}Type": MetricType, ValueArrayType, etc.
4545
DiscriminatorType string
46-
// DiscriminatorToType
47-
DiscriminatorToType [][]string
48-
49-
GenericFieldName string
46+
// DiscriminatorMappings maps oneOf enum constant (e.g. DatumTypeBool) to their concrete types (e.g. DatumBool).
47+
DiscriminatorMappings []DiscriminatorMapping
48+
// OneOfField is the name of the field that holds the oneOf variants (e.g., "Datum", "Values")
49+
OneOfField string
5050
}
5151

5252
// TypeFields holds the information for each type field
@@ -65,6 +65,12 @@ type EnumTemplate struct {
6565
Value string
6666
}
6767

68+
// DiscriminatorMapping maps a discriminator enum value to its concrete type
69+
type DiscriminatorMapping struct {
70+
EnumConstant string // The enum constant to match in switch (e.g., "DatumTypeBool")
71+
ConcreteType string // The concrete type to unmarshal into (e.g., "DatumBool")
72+
}
73+
6874
// ValidationTemplate holds information about the fields that
6975
// need to be validated
7076
type ValidationTemplate struct {
@@ -354,33 +360,35 @@ func writeTypes(f *os.File, typeCollection []TypeTemplate, typeValidationCollect
354360
}
355361
fmt.Fprint(f, "}\n")
356362

357-
if tt.DiscriminatorKey != "" && len(tt.DiscriminatorToType) > 0 && tt.GenericFieldName != "" {
363+
if tt.DiscriminatorKey != "" && len(tt.DiscriminatorMappings) > 0 && tt.OneOfField != "" {
358364
fmt.Fprintf(f, "func (v *%s) UnmarshalJSON(data []byte) error {\n", tt.Name)
359365
fmt.Fprintf(f, "\tvar peek struct {\n")
360-
fmt.Fprintf(f, "\t\tdiscriminator %s`json:\"%s\"`\n", tt.DiscriminatorType, tt.DiscriminatorKey)
366+
fmt.Fprintf(f, "\t\tdiscriminator %s `json:\"%s\"`\n", tt.DiscriminatorType, tt.DiscriminatorKey)
361367
fmt.Fprintf(f, "\t}\n")
362368
fmt.Fprintf(f, "\tif err := json.Unmarshal(data, &peek); err != nil {\n")
363369
fmt.Fprintf(f, "\t\treturn err\n")
364370
fmt.Fprintf(f, "\t}\n")
365371
fmt.Fprintf(f, "\tswitch peek.discriminator {\n")
366372

367-
for _, dtt := range tt.DiscriminatorToType {
368-
fmt.Fprintf(f, "\tcase %s:\n", dtt[0])
369-
fmt.Fprintf(f, "\t\tvar val %s\n", dtt[1])
373+
for _, mapping := range tt.DiscriminatorMappings {
374+
fmt.Fprintf(f, "\tcase %s:\n", mapping.EnumConstant)
375+
fmt.Fprintf(f, "\t\tvar val %s\n", mapping.ConcreteType)
370376
fmt.Fprintf(f, "\t\tif err := json.Unmarshal(data, &val); err != nil {\n")
371377
fmt.Fprintf(f, "\t\t\treturn err\n")
372378
fmt.Fprintf(f, "\t\t}\n")
373-
fmt.Fprintf(f, "\tv.%s = val\n", tt.GenericFieldName)
379+
fmt.Fprintf(f, "\tv.%s = val\n", tt.OneOfField)
374380
}
375381

382+
fmt.Fprintf(f, "\tdefault:\n")
383+
fmt.Fprintf(f, "\t\treturn fmt.Errorf(\"unknown %s discriminator value: %%v\", peek.discriminator)\n", tt.DiscriminatorKey)
376384
fmt.Fprintf(f, "\t}\n")
377385
fmt.Fprintf(f, "\treturn nil\n")
378386
fmt.Fprint(f, "}\n")
379387
}
380388
}
381-
if tt.InterfaceType != "" {
389+
if tt.OneOfInterface != "" {
382390
fmt.Fprintf(f, "\n")
383-
fmt.Fprintf(f, "func (%s) is%s() {}\n", tt.Name, tt.InterfaceType)
391+
fmt.Fprintf(f, "func (%s) is%s() {}\n", tt.Name, tt.OneOfInterface)
384392
}
385393
fmt.Fprint(f, "\n")
386394
}
@@ -464,10 +472,8 @@ func populateTypeTemplates(name string, s *openapi3.Schema, enumFieldName string
464472
typeTpl.Type = fmt.Sprintf("[]%s", s.Items.Value.Type)
465473
typeTpl.Name = typeName
466474
case "object":
467-
b, _ := json.Marshal(s)
468-
fmt.Printf("DEBUG IN OBJECT %s %+v %+v\n", typeName, string(b), enumFieldName)
469475
typeTpl = createTypeObject(s, name, typeName, formatTypeDescription(typeName, s))
470-
typeTpl.InterfaceType = interfaceName
476+
typeTpl.OneOfInterface = interfaceName
471477

472478
// Iterate over the properties and append the types, if we need to.
473479
properties := sortedKeys(s.Properties)
@@ -698,7 +704,6 @@ func createAllOf(s *openapi3.Schema, stringEnums map[string][]string, name, type
698704
}
699705

700706
func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []EnumTemplate) {
701-
fmt.Printf("DEBUG IN CREATEONEOF %v\n", typeName)
702707
var parsedProperties []string
703708
var properties []string
704709
var genericTypes []string
@@ -748,10 +753,8 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E
748753
}
749754
}
750755

751-
fmt.Printf("DEBUG GENERIC TYPES %+v\n", genericTypes)
752-
typeInterfaces := map[string]string{}
753756
maybeDiscriminators := map[string]struct{}{}
754-
discriminatorToType := [][]string{}
757+
discriminatorMappings := []DiscriminatorMapping{}
755758
discriminatorToDiscriminatorType := map[string]string{}
756759
interfaceName := ""
757760

@@ -770,14 +773,12 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E
770773
propertyType := convertToValidGoType(prop, typeName, p)
771774

772775
if propertyType == "string" && len(p.Value.Enum) == 1 {
773-
fmt.Printf("DEBUG PROP IS %s\n", prop)
774776
maybeDiscriminators[prop] = struct{}{}
775777
discriminatorToDiscriminatorType[prop] = typeName + strcase.ToCamel(prop)
776-
discriminatorToType = append(discriminatorToType, []string{
777-
fmt.Sprintf("%s%s%s", typeName, propertyName, strcase.ToCamel(p.Value.Enum[0].(string))),
778-
fmt.Sprintf("%s%s", typeName, strcase.ToCamel(p.Value.Enum[0].(string))),
778+
discriminatorMappings = append(discriminatorMappings, DiscriminatorMapping{
779+
EnumConstant: fmt.Sprintf("%s%s%s", typeName, propertyName, strcase.ToCamel(p.Value.Enum[0].(string))),
780+
ConcreteType: fmt.Sprintf("%s%s", typeName, strcase.ToCamel(p.Value.Enum[0].(string))),
779781
})
780-
// discriminatorToType[p.Value.Enum[0].(string)] = fmt.Sprintf("%s%s%s", typeName, propertyName, strcase.ToCamel(p.Value.Enum[0].(string)))
781782
}
782783
// Check if we have an enum in order to use the corresponding type instead of
783784
// "string"
@@ -796,11 +797,8 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E
796797

797798
// We set the type of a field as "any" if every element of the oneOf property isn't the same
798799
if slices.Contains(genericTypes, prop) {
799-
fmt.Printf("DEBUG CONTAINS %+v %+v %s\n", genericTypes, prop, typeName)
800800
interfaceName = fmt.Sprintf("%s%s", typeName, propertyName)
801801
field.Type = fmt.Sprintf("%s%s", typeName, propertyName)
802-
803-
// typeInterfaces[field.Type] = fmt.Sprintf("%s%s", typeName, propertyName)
804802
}
805803

806804
// Check if the field is nullable and use omitzero instead of omitempty.
@@ -836,15 +834,7 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E
836834

837835
// TODO: This is the only place that has an "additional name" at the end
838836
// TODO: This is where the "allOf" is being detected
839-
fmt.Printf("DEBUG ABOUT TO POPULATE %s %+v %s\n", name, enumFieldName, interfaceName)
840837
tt, et := populateTypeTemplates(name, v.Value, enumFieldName, interfaceName)
841-
842-
fmt.Printf("DEBUG TYPE INTERFACES %+v\n", typeInterfaces)
843-
// for idx := range tt {
844-
// if interfaceType, ok := typeInterfaces[tt[idx].Name]; ok {
845-
// tt[idx].InterfaceType = interfaceType
846-
// }
847-
// }
848838
typeTpls = append(typeTpls, tt...)
849839
enumTpls = append(enumTpls, et...)
850840
}
@@ -860,35 +850,31 @@ func createOneOf(s *openapi3.Schema, name, typeName string) ([]TypeTemplate, []E
860850
discriminator := ""
861851
if len(maybeDiscriminators) == 1 {
862852
discriminator = slices.Collect(maps.Keys(maybeDiscriminators))[0]
863-
fmt.Printf("DEBUG DISCRIMINATOR %s\n", discriminator)
864853
}
865854

866855
genericFieldName := ""
867856
if len(genericTypes) == 1 {
868857
genericFieldName = strcase.ToCamel(genericTypes[0])
869858
}
870-
fmt.Printf("DEBUG GENERIC FIELD %+v %d %s\n", genericTypes, len(genericTypes), genericFieldName)
871859

872860
// Make sure to only create structs if the oneOf is not a replacement for enums on the API spec
873861
if len(fields) > 0 {
874862
typeTpl := TypeTemplate{
875-
Description: formatTypeDescription(typeName, s),
876-
Name: typeName,
877-
Type: "struct",
878-
Fields: fields,
879-
DiscriminatorKey: discriminator,
880-
DiscriminatorType: discriminatorToDiscriminatorType[discriminator],
881-
DiscriminatorToType: discriminatorToType,
882-
GenericFieldName: genericFieldName,
883-
}
884-
fmt.Printf("DEBUG TT %+v\n", typeTpl)
863+
Description: formatTypeDescription(typeName, s),
864+
Name: typeName,
865+
Type: "struct",
866+
Fields: fields,
867+
DiscriminatorKey: discriminator,
868+
DiscriminatorType: discriminatorToDiscriminatorType[discriminator],
869+
DiscriminatorMappings: discriminatorMappings,
870+
OneOfField: genericFieldName,
871+
}
885872
typeTpls = append(typeTpls, typeTpl)
886873
}
887874

888875
if interfaceName != "" {
889876
typeTpls = append(typeTpls, TypeTemplate{
890877
Name: interfaceName,
891-
// Name: fmt.Sprintf("%s%s", typeName, interfaceName),
892878
Type: "interface",
893879
})
894880
}

oxide/types.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)