From 56327289cf7eb9884ce515aea4796180b4805f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Tue, 12 Sep 2023 23:46:09 +0200 Subject: [PATCH] Initial tests coverage for golang jenny --- internal/ast/compiler/disjunctions.go | 7 + internal/jennies/all.go | 10 +- internal/jennies/golang/jennies.go | 8 ++ internal/jennies/golang/rawtypes.go | 37 ++--- internal/jennies/golang/rawtypes_test.go | 41 ++++++ .../stringorbool.types.json_marshal.go.tmpl | 28 ++-- .../golang/veneers/types.json_marshal.go.tmpl | 6 - internal/jennies/typescript/jennies.go | 5 + internal/jennies/typescript/rawtypes_test.go | 15 ++ testdata/jennies/rawtypes/arrays.txtar | 15 ++ testdata/jennies/rawtypes/disjunctions.txtar | 131 ++++++++++++++++++ testdata/jennies/rawtypes/enums.txtar | 23 +++ testdata/jennies/rawtypes/maps.txtar | 21 ++- testdata/jennies/rawtypes/refs.txtar | 14 +- testdata/jennies/rawtypes/scalars.txtar | 42 +++++- .../rawtypes/struct_with_complex_fields.txtar | 83 +++++++++++ .../struct_with_optional_fields.txtar | 25 ++++ .../rawtypes/struct_with_scalar_fields.txtar | 28 ++++ 18 files changed, 472 insertions(+), 67 deletions(-) create mode 100644 internal/jennies/golang/rawtypes_test.go delete mode 100644 internal/jennies/golang/veneers/types.json_marshal.go.tmpl diff --git a/internal/ast/compiler/disjunctions.go b/internal/ast/compiler/disjunctions.go index 0dc1a9aaf..4c5cd070d 100644 --- a/internal/ast/compiler/disjunctions.go +++ b/internal/ast/compiler/disjunctions.go @@ -1,6 +1,7 @@ package compiler import ( + "sort" "strings" "github.com/grafana/cog/internal/ast" @@ -41,6 +42,12 @@ func (pass *DisjunctionToType) processFile(file *ast.File) (*ast.File, error) { newObjects = append(newObjects, obj) } + // Since newly created objects are temporarily stored in a map, we need to + // sort them to have a deterministic output. + sort.SliceStable(newObjects, func(i, j int) bool { + return newObjects[i].Name < newObjects[j].Name + }) + return &ast.File{ Package: file.Package, Definitions: append(processedObjects, newObjects...), diff --git a/internal/jennies/all.go b/internal/jennies/all.go index 24d03805a..d2520e0d8 100644 --- a/internal/jennies/all.go +++ b/internal/jennies/all.go @@ -15,17 +15,13 @@ type LanguageTarget struct { func All() map[string]LanguageTarget { targets := map[string]LanguageTarget{ - // Compiler passes should not have side effects, but they do. "go": { - Jennies: golang.Jennies(), - CompilerPasses: []compiler.Pass{ - &compiler.AnonymousEnumToExplicitType{}, - &compiler.DisjunctionToType{}, - }, + Jennies: golang.Jennies(), + CompilerPasses: golang.CompilerPasses(), }, "typescript": { Jennies: typescript.Jennies(), - CompilerPasses: nil, + CompilerPasses: typescript.CompilerPasses(), }, } diff --git a/internal/jennies/golang/jennies.go b/internal/jennies/golang/jennies.go index c4d018b46..c09f30520 100644 --- a/internal/jennies/golang/jennies.go +++ b/internal/jennies/golang/jennies.go @@ -3,6 +3,7 @@ package golang import ( "github.com/grafana/codejen" "github.com/grafana/cog/internal/ast" + "github.com/grafana/cog/internal/ast/compiler" "github.com/grafana/cog/internal/jennies/tools" "github.com/grafana/cog/internal/veneers" ) @@ -29,3 +30,10 @@ func Jennies() *codejen.JennyList[[]*ast.File] { return targets } + +func CompilerPasses() []compiler.Pass { + return []compiler.Pass{ + &compiler.AnonymousEnumToExplicitType{}, + &compiler.DisjunctionToType{}, + } +} diff --git a/internal/jennies/golang/rawtypes.go b/internal/jennies/golang/rawtypes.go index 7fddd0730..4887fbf66 100644 --- a/internal/jennies/golang/rawtypes.go +++ b/internal/jennies/golang/rawtypes.go @@ -65,10 +65,6 @@ func (jenny RawTypes) formatObject(def ast.Object) ([]byte, error) { } switch def.Type.Kind { - case ast.KindStruct: - buffer.WriteString(fmt.Sprintf("type %s ", defName)) - buffer.WriteString(formatStructBody(def.Type.AsStruct(), "")) - buffer.WriteString("\n") case ast.KindEnum: buffer.WriteString(jenny.formatEnumDef(def)) case ast.KindScalar: @@ -77,16 +73,16 @@ func (jenny RawTypes) formatObject(def ast.Object) ([]byte, error) { if scalarType.Value != nil { buffer.WriteString(fmt.Sprintf("const %s = %s", defName, formatScalar(scalarType.Value))) } else { - buffer.WriteString(fmt.Sprintf("type %s %s", defName, scalarType.ScalarKind)) + buffer.WriteString(fmt.Sprintf("type %s %s", defName, formatType(def.Type, true, ""))) } - case ast.KindMap: + case ast.KindMap, ast.KindRef, ast.KindArray, ast.KindStruct: buffer.WriteString(fmt.Sprintf("type %s %s", defName, formatType(def.Type, true, ""))) - case ast.KindRef: - buffer.WriteString(fmt.Sprintf("type %s %s", defName, def.Type.AsRef().ReferredType)) default: return nil, fmt.Errorf("unhandled type def kind: %s", def.Type.Kind) } + buffer.WriteString("\n") + return []byte(buffer.String()), nil } @@ -116,9 +112,9 @@ func (jenny RawTypes) veneer(veneerType string, def ast.Object) (string, error) if tmpl == nil { tmpl = templates.Lookup(fmt.Sprintf("types.%s.go.tmpl", veneerType)) } - // If not, something went wrong. + // If not, then there's nothing to do. if tmpl == nil { - return "", fmt.Errorf("veneer '%s' not found", veneerType) + return "", nil } buf := bytes.Buffer{} @@ -180,10 +176,6 @@ func formatType(def ast.Type, fieldIsRequired bool, typesPkg string) string { return "any" } - if def.Kind == ast.KindDisjunction { - return formatDisjunction(def.AsDisjunction(), typesPkg) - } - if def.Kind == ast.KindArray { return formatArray(def.AsArray(), typesPkg) } @@ -215,16 +207,12 @@ func formatType(def ast.Type, fieldIsRequired bool, typesPkg string) string { return typeName } - if def.Kind == ast.KindEnum { - return "enum here" - } - // anonymous struct if def.Kind == ast.KindStruct { return formatStructBody(def.AsStruct(), typesPkg) } - // FIXME: we shouldn't be here + // FIXME: we should never be here return "unknown" } @@ -241,15 +229,6 @@ func formatMap(def ast.MapType, typesPkg string) string { return fmt.Sprintf("map[%s]%s", keyTypeString, valueTypeString) } -func formatDisjunction(def ast.DisjunctionType, typesPkg string) string { - subTypes := make([]string, 0, len(def.Branches)) - for _, subType := range def.Branches { - subTypes = append(subTypes, formatType(subType, true, typesPkg)) - } - - return fmt.Sprintf("disjunction<%s>", strings.Join(subTypes, " | ")) -} - func formatScalar(val any) string { if list, ok := val.([]any); ok { items := make([]string, 0, len(list)) @@ -258,7 +237,7 @@ func formatScalar(val any) string { items = append(items, formatScalar(item)) } - // TODO: we can't assume a list of strings + // FIXME: this is wrong, we can't just assume a list of strings. return fmt.Sprintf("[]string{%s}", strings.Join(items, ", ")) } diff --git a/internal/jennies/golang/rawtypes_test.go b/internal/jennies/golang/rawtypes_test.go new file mode 100644 index 000000000..2b9a89ab3 --- /dev/null +++ b/internal/jennies/golang/rawtypes_test.go @@ -0,0 +1,41 @@ +package golang + +import ( + "testing" + + "github.com/grafana/cog/internal/ast" + "github.com/grafana/cog/internal/txtartest" + "github.com/stretchr/testify/require" +) + +func TestRawTypes_Generate(t *testing.T) { + test := txtartest.TxTarTest{ + Root: "../../../testdata/jennies/rawtypes", + Name: "jennies/GoRawTypes", + } + + jenny := RawTypes{} + compilerPasses := CompilerPasses() + + test.Run(t, func(tc *txtartest.Test) { + req := require.New(tc) + + var err error + processedAsts := []*ast.File{tc.TypesIR()} + + // We run the compiler passes defined fo Go since without them, we + // might not be able to translate some of the IR's semantics into Go. + // Example: disjunctions. + for _, compilerPass := range compilerPasses { + processedAsts, err = compilerPass.Process(processedAsts) + req.NoError(err) + } + + req.Len(processedAsts, 1, "we somehow got more ast.File than we put in") + + files, err := jenny.Generate(processedAsts[0]) + req.NoError(err) + + tc.WriteFiles(files) + }) +} diff --git a/internal/jennies/golang/veneers/stringorbool.types.json_marshal.go.tmpl b/internal/jennies/golang/veneers/stringorbool.types.json_marshal.go.tmpl index 2cdd3008c..d612013d2 100644 --- a/internal/jennies/golang/veneers/stringorbool.types.json_marshal.go.tmpl +++ b/internal/jennies/golang/veneers/stringorbool.types.json_marshal.go.tmpl @@ -5,21 +5,14 @@ // Grafana. func (resource {{ .def.Name|formatIdentifier }}) MarshalJSON() ([]byte, error) { if resource.ValString != nil { - var buf bytes.Buffer - buf.WriteRune('"') - buf.WriteString(*resource.ValString) - buf.WriteRune('"') - return buf.Bytes(), nil - } - - return strconv.AppendBool([]byte{}, *resource.ValBool), nil -} + var buf bytes.Buffer + buf.WriteRune('"') + buf.WriteString(*resource.ValString) + buf.WriteRune('"') + return buf.Bytes(), nil + } -// MarshalIndentJSON renders the resource as indented JSON -// which your configuration management tool of choice can then feed into -// Grafana. -func (resource {{ .def.Name|formatIdentifier }}) MarshalIndentJSON() ([]byte, error) { - return json.MarshalIndent(resource, "", " ") + return strconv.AppendBool([]byte{}, *resource.ValBool), nil } func (resource {{ .def.Name|formatIdentifier }}) UnmarshalJSON(raw []byte) error { @@ -32,13 +25,13 @@ func (resource {{ .def.Name|formatIdentifier }}) UnmarshalJSON(raw []byte) error ) if raw[0] != '"' { if bytes.Equal(raw, []byte("true")) { - yup := true + yup := true resource.ValBool = &yup return nil } if bytes.Equal(raw, []byte("false")) { - nope := false - resource.ValBool = &nope + nope := false + resource.ValBool = &nope return nil } return errors.New("bad boolean value provided") @@ -49,3 +42,4 @@ func (resource {{ .def.Name|formatIdentifier }}) UnmarshalJSON(raw []byte) error resource.ValString = &tmp return nil } + diff --git a/internal/jennies/golang/veneers/types.json_marshal.go.tmpl b/internal/jennies/golang/veneers/types.json_marshal.go.tmpl deleted file mode 100644 index 262820290..000000000 --- a/internal/jennies/golang/veneers/types.json_marshal.go.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -// MarshalIndentJSON renders the resource as indented JSON -// which your configuration management tool of choice can then feed into -// Grafana. -func (resource {{ .def.Name|formatIdentifier }}) MarshalIndentJSON() ([]byte, error) { - return json.MarshalIndent(resource, "", " ") -} diff --git a/internal/jennies/typescript/jennies.go b/internal/jennies/typescript/jennies.go index 2003b246a..07ff9a1d3 100644 --- a/internal/jennies/typescript/jennies.go +++ b/internal/jennies/typescript/jennies.go @@ -3,6 +3,7 @@ package typescript import ( "github.com/grafana/codejen" "github.com/grafana/cog/internal/ast" + "github.com/grafana/cog/internal/ast/compiler" "github.com/grafana/cog/internal/jennies/tools" "github.com/grafana/cog/internal/veneers" ) @@ -31,3 +32,7 @@ func Jennies() *codejen.JennyList[[]*ast.File] { return targets } + +func CompilerPasses() []compiler.Pass { + return nil +} diff --git a/internal/jennies/typescript/rawtypes_test.go b/internal/jennies/typescript/rawtypes_test.go index 6a7189ddb..40e566541 100644 --- a/internal/jennies/typescript/rawtypes_test.go +++ b/internal/jennies/typescript/rawtypes_test.go @@ -3,6 +3,7 @@ package typescript import ( "testing" + "github.com/grafana/cog/internal/ast" "github.com/grafana/cog/internal/txtartest" "github.com/stretchr/testify/require" ) @@ -14,10 +15,24 @@ func TestRawTypes_Generate(t *testing.T) { } jenny := RawTypes{} + compilerPasses := CompilerPasses() test.Run(t, func(tc *txtartest.Test) { req := require.New(tc) + var err error + processedAsts := []*ast.File{tc.TypesIR()} + + // We run the compiler passes defined fo Go since without them, we + // might not be able to translate some of the IR's semantics into Go. + // Example: disjunctions. + for _, compilerPass := range compilerPasses { + processedAsts, err = compilerPass.Process(processedAsts) + req.NoError(err) + } + + req.Len(processedAsts, 1, "we somehow got more ast.File than we put in") + files, err := jenny.Generate(tc.TypesIR()) req.NoError(err) diff --git a/testdata/jennies/rawtypes/arrays.txtar b/testdata/jennies/rawtypes/arrays.txtar index eb1d7e6ed..2bafaecb5 100644 --- a/testdata/jennies/rawtypes/arrays.txtar +++ b/testdata/jennies/rawtypes/arrays.txtar @@ -80,3 +80,18 @@ export type ArrayOfRefs = SomeStruct[]; export type ArrayOfArrayOfNumbers = number[][]; +-- out/jennies/GoRawTypes -- +== types/arrays/types_gen.go +package types + +// List of tags, maybe? +type ArrayOfStrings []string + +type SomeStruct struct { + FieldAny any `json:"FieldAny"` +} + +type ArrayOfRefs []SomeStruct + +type ArrayOfArrayOfNumbers [][]int64 + diff --git a/testdata/jennies/rawtypes/disjunctions.txtar b/testdata/jennies/rawtypes/disjunctions.txtar index 58191aca9..674ecc01c 100644 --- a/testdata/jennies/rawtypes/disjunctions.txtar +++ b/testdata/jennies/rawtypes/disjunctions.txtar @@ -161,3 +161,134 @@ export interface YetAnotherStruct { export type SeveralRefs = SomeStruct | SomeOtherStruct | YetAnotherStruct; +-- out/jennies/GoRawTypes -- +== types/disjunctions/types_gen.go +package types + +// Refresh rate or disabled. +type StringOrBool StringOrBool + +// MarshalJSON implements the encoding/json.Marshaler interface. +// +// This method can be used to render the resource as JSON +// which your configuration management tool of choice can then feed into +// Grafana. +func (resource StringOrBool) MarshalJSON() ([]byte, error) { + if resource.ValString != nil { + var buf bytes.Buffer + buf.WriteRune('"') + buf.WriteString(*resource.ValString) + buf.WriteRune('"') + return buf.Bytes(), nil + } + + return strconv.AppendBool([]byte{}, *resource.ValBool), nil +} + +func (resource StringOrBool) UnmarshalJSON(raw []byte) error { + if raw == nil || bytes.Equal(raw, []byte(`"null"`)) { + return nil + } + var ( + tmp string + err error + ) + if raw[0] != '"' { + if bytes.Equal(raw, []byte("true")) { + yup := true + resource.ValBool = &yup + return nil + } + if bytes.Equal(raw, []byte("false")) { + nope := false + resource.ValBool = &nope + return nil + } + return errors.New("bad boolean value provided") + } + if err = json.Unmarshal(raw, &tmp); err != nil { + return err + } + resource.ValString = &tmp + return nil +} + +type StringOrNull string + +type SomeStruct struct { + FieldAny any `json:"FieldAny"` +} + +type BoolOrRef BoolOrSomeStruct + +type SomeOtherStruct struct { + FieldAny any `json:"FieldAny"` +} + +type YetAnotherStruct struct { + FieldAny any `json:"FieldAny"` +} + +type SeveralRefs SomeStructOrSomeOtherStructOrYetAnotherStruct + +type BoolOrSomeStruct struct { + ValBool *bool `json:"ValBool,omitempty"` + ValSomeStruct *SomeStruct `json:"ValSomeStruct,omitempty"` +} + +type SomeStructOrSomeOtherStructOrYetAnotherStruct struct { + ValSomeStruct *SomeStruct `json:"ValSomeStruct,omitempty"` + ValSomeOtherStruct *SomeOtherStruct `json:"ValSomeOtherStruct,omitempty"` + ValYetAnotherStruct *YetAnotherStruct `json:"ValYetAnotherStruct,omitempty"` +} + +type StringOrBool struct { + ValString *string `json:"ValString,omitempty"` + ValBool *bool `json:"ValBool,omitempty"` +} + +// MarshalJSON implements the encoding/json.Marshaler interface. +// +// This method can be used to render the resource as JSON +// which your configuration management tool of choice can then feed into +// Grafana. +func (resource StringOrBool) MarshalJSON() ([]byte, error) { + if resource.ValString != nil { + var buf bytes.Buffer + buf.WriteRune('"') + buf.WriteString(*resource.ValString) + buf.WriteRune('"') + return buf.Bytes(), nil + } + + return strconv.AppendBool([]byte{}, *resource.ValBool), nil +} + +func (resource StringOrBool) UnmarshalJSON(raw []byte) error { + if raw == nil || bytes.Equal(raw, []byte(`"null"`)) { + return nil + } + var ( + tmp string + err error + ) + if raw[0] != '"' { + if bytes.Equal(raw, []byte("true")) { + yup := true + resource.ValBool = &yup + return nil + } + if bytes.Equal(raw, []byte("false")) { + nope := false + resource.ValBool = &nope + return nil + } + return errors.New("bad boolean value provided") + } + if err = json.Unmarshal(raw, &tmp); err != nil { + return err + } + resource.ValString = &tmp + return nil +} + diff --git a/testdata/jennies/rawtypes/enums.txtar b/testdata/jennies/rawtypes/enums.txtar index 59e36620d..b145d3171 100644 --- a/testdata/jennies/rawtypes/enums.txtar +++ b/testdata/jennies/rawtypes/enums.txtar @@ -89,3 +89,26 @@ export enum DashboardCursorSync { Tooltip = 2, } +-- out/jennies/GoRawTypes -- +== types/enums/types_gen.go +package types + +// This is a very interesting string enum. +type Operator string +const ( + GreaterThan Operator = ">" + LessThan Operator = "<" +) + + +// 0 for no shared crosshair or tooltip (default). +// 1 for shared crosshair. +// 2 for shared crosshair AND shared tooltip. +type DashboardCursorSync int8 +const ( + Off DashboardCursorSync = 0 + Crosshair DashboardCursorSync = 1 + Tooltip DashboardCursorSync = 2 +) + + diff --git a/testdata/jennies/rawtypes/maps.txtar b/testdata/jennies/rawtypes/maps.txtar index 8089aa6b8..8835517e6 100644 --- a/testdata/jennies/rawtypes/maps.txtar +++ b/testdata/jennies/rawtypes/maps.txtar @@ -67,7 +67,7 @@ }, "ValueType": { "Kind": "ref", - "Ref": {"ReferredType": "MapOfStringToRef"} + "Ref": {"ReferredType": "SomeStruct"} } } } @@ -111,7 +111,24 @@ export interface SomeStruct { fieldAny: any; } -export type MapOfStringToRef = Record; +export type MapOfStringToRef = Record; export type MapOfStringToMapOfStringToBool = Record>; +-- out/jennies/GoRawTypes -- +== types/maps/types_gen.go +package types + +// String to... something. +type MapOfStringToAny map[string]any + +type MapOfStringToString map[string]string + +type SomeStruct struct { + FieldAny any `json:"FieldAny"` +} + +type MapOfStringToRef map[string]SomeStruct + +type MapOfStringToMapOfStringToBool map[string]map[string]bool + diff --git a/testdata/jennies/rawtypes/refs.txtar b/testdata/jennies/rawtypes/refs.txtar index 859d5d49e..1bc2186ff 100644 --- a/testdata/jennies/rawtypes/refs.txtar +++ b/testdata/jennies/rawtypes/refs.txtar @@ -25,7 +25,7 @@ "Name": "RefToSomeStruct", "Type": { "Kind": "ref", - "Ref": {"ReferredType": "MapOfStringToRef"} + "Ref": {"ReferredType": "SomeStruct"} } } ] @@ -36,4 +36,14 @@ export interface SomeStruct { fieldAny: any; } -export type RefToSomeStruct = MapOfStringToRef; +export type RefToSomeStruct = SomeStruct; +-- out/jennies/GoRawTypes -- +== types/refs/types_gen.go +package types + +type SomeStruct struct { + FieldAny any `json:"FieldAny"` +} + +type RefToSomeStruct SomeStruct + diff --git a/testdata/jennies/rawtypes/scalars.txtar b/testdata/jennies/rawtypes/scalars.txtar index f5932fabe..250128ecb 100644 --- a/testdata/jennies/rawtypes/scalars.txtar +++ b/testdata/jennies/rawtypes/scalars.txtar @@ -4,7 +4,7 @@ "Package": "scalars", "Definitions": [ { - "Name": "ConstTypeString", + "Name": "constTypeString", "Type": { "Kind": "scalar", "Scalar": {"ScalarKind": "string", "Value": "foo"} @@ -12,7 +12,7 @@ }, { - "Name": "ScalarTypeAny", + "Name": "scalarTypeAny", "Type": { "Kind": "scalar", "Scalar": {"ScalarKind": "any"} @@ -117,9 +117,9 @@ } -- out/jennies/TypescriptRawTypes -- == types/scalars/types_gen.ts -export const ConstTypeString = "foo"; +export const constTypeString = "foo"; -export type ScalarTypeAny = any; +export type scalarTypeAny = any; export type ScalarTypeBool = boolean; @@ -147,3 +147,37 @@ export type ScalarTypeInt32 = number; export type ScalarTypeInt64 = number; +-- out/jennies/GoRawTypes -- +== types/scalars/types_gen.go +package types + +const ConstTypeString = "foo" + +type ScalarTypeAny any + +type ScalarTypeBool bool + +type ScalarTypeBytes bytes + +type ScalarTypeString string + +type ScalarTypeFloat32 float32 + +type ScalarTypeFloat64 float64 + +type ScalarTypeUint8 uint8 + +type ScalarTypeUint16 uint16 + +type ScalarTypeUint32 uint32 + +type ScalarTypeUint64 uint64 + +type ScalarTypeInt8 int8 + +type ScalarTypeInt16 int16 + +type ScalarTypeInt32 int32 + +type ScalarTypeInt64 int64 + diff --git a/testdata/jennies/rawtypes/struct_with_complex_fields.txtar b/testdata/jennies/rawtypes/struct_with_complex_fields.txtar index ac9ad8cab..73015d887 100644 --- a/testdata/jennies/rawtypes/struct_with_complex_fields.txtar +++ b/testdata/jennies/rawtypes/struct_with_complex_fields.txtar @@ -176,3 +176,86 @@ export interface SomeOtherStruct { fieldAny: any; } +-- out/jennies/GoRawTypes -- +== types/struct_complex_fields/types_gen.go +package types + +// This struct does things. +type SomeStruct struct { + FieldRef SomeOtherStruct `json:"FieldRef"` + FieldDisjunctionOfScalars StringOrBool `json:"FieldDisjunctionOfScalars"` + FieldMixedDisjunction StringOrSomeOtherStruct `json:"FieldMixedDisjunction"` + FieldAnonymousEnum FieldAnonymousEnumEnum `json:"FieldAnonymousEnum"` + FieldArrayOfStrings []string `json:"FieldArrayOfStrings"` + FieldMapOfStringToString map[string]string `json:"FieldMapOfStringToString"` + FieldAnonymousStruct struct { + FieldAny any `json:"FieldAny"` +} `json:"FieldAnonymousStruct"` +} + +type SomeOtherStruct struct { + FieldAny any `json:"FieldAny"` +} + +type FieldAnonymousEnumEnum string +const ( + FieldAnonymousEnumGreaterThan FieldAnonymousEnumEnum = ">" + FieldAnonymousEnumLessThan FieldAnonymousEnumEnum = "<" +) + + +type StringOrBool struct { + ValString *string `json:"ValString,omitempty"` + ValBool *bool `json:"ValBool,omitempty"` +} + +// MarshalJSON implements the encoding/json.Marshaler interface. +// +// This method can be used to render the resource as JSON +// which your configuration management tool of choice can then feed into +// Grafana. +func (resource StringOrBool) MarshalJSON() ([]byte, error) { + if resource.ValString != nil { + var buf bytes.Buffer + buf.WriteRune('"') + buf.WriteString(*resource.ValString) + buf.WriteRune('"') + return buf.Bytes(), nil + } + + return strconv.AppendBool([]byte{}, *resource.ValBool), nil +} + +func (resource StringOrBool) UnmarshalJSON(raw []byte) error { + if raw == nil || bytes.Equal(raw, []byte(`"null"`)) { + return nil + } + var ( + tmp string + err error + ) + if raw[0] != '"' { + if bytes.Equal(raw, []byte("true")) { + yup := true + resource.ValBool = &yup + return nil + } + if bytes.Equal(raw, []byte("false")) { + nope := false + resource.ValBool = &nope + return nil + } + return errors.New("bad boolean value provided") + } + if err = json.Unmarshal(raw, &tmp); err != nil { + return err + } + resource.ValString = &tmp + return nil +} + +type StringOrSomeOtherStruct struct { + ValString *string `json:"ValString,omitempty"` + ValSomeOtherStruct *SomeOtherStruct `json:"ValSomeOtherStruct,omitempty"` +} + diff --git a/testdata/jennies/rawtypes/struct_with_optional_fields.txtar b/testdata/jennies/rawtypes/struct_with_optional_fields.txtar index 79bd6ebb4..30060f451 100644 --- a/testdata/jennies/rawtypes/struct_with_optional_fields.txtar +++ b/testdata/jennies/rawtypes/struct_with_optional_fields.txtar @@ -125,3 +125,28 @@ export interface SomeOtherStruct { fieldAny: any; } +-- out/jennies/GoRawTypes -- +== types/struct_optional_fields/types_gen.go +package types + +type SomeStruct struct { + FieldRef *SomeOtherStruct `json:"FieldRef,omitempty"` + FieldString *string `json:"FieldString,omitempty"` + FieldAnonymousEnum *FieldAnonymousEnumEnum `json:"FieldAnonymousEnum,omitempty"` + FieldArrayOfStrings []string `json:"FieldArrayOfStrings,omitempty"` + FieldAnonymousStruct struct { + FieldAny any `json:"FieldAny"` +} `json:"FieldAnonymousStruct,omitempty"` +} + +type SomeOtherStruct struct { + FieldAny any `json:"FieldAny"` +} + +type FieldAnonymousEnumEnum string +const ( + FieldAnonymousEnumGreaterThan FieldAnonymousEnumEnum = ">" + FieldAnonymousEnumLessThan FieldAnonymousEnumEnum = "<" +) + + diff --git a/testdata/jennies/rawtypes/struct_with_scalar_fields.txtar b/testdata/jennies/rawtypes/struct_with_scalar_fields.txtar index f201486fa..059353579 100644 --- a/testdata/jennies/rawtypes/struct_with_scalar_fields.txtar +++ b/testdata/jennies/rawtypes/struct_with_scalar_fields.txtar @@ -167,3 +167,31 @@ export interface SomeStruct { fieldInt64: number; } +-- out/jennies/GoRawTypes -- +== types/basic/types_gen.go +package types + +// This +// is +// a +// comment +type SomeStruct struct { + // Anything can go in there. +// Really, anything. +FieldAny any `json:"FieldAny"` + FieldBool bool `json:"FieldBool"` + FieldBytes bytes `json:"FieldBytes"` + FieldString string `json:"FieldString"` + FieldStringWithConstantValue string `json:"FieldStringWithConstantValue"` + FieldFloat32 float32 `json:"FieldFloat32"` + FieldFloat64 float64 `json:"FieldFloat64"` + FieldUint8 uint8 `json:"FieldUint8"` + FieldUint16 uint16 `json:"FieldUint16"` + FieldUint32 uint32 `json:"FieldUint32"` + FieldUint64 uint64 `json:"FieldUint64"` + FieldInt8 int8 `json:"FieldInt8"` + FieldInt16 int16 `json:"FieldInt16"` + FieldInt32 int32 `json:"FieldInt32"` + FieldInt64 int64 `json:"FieldInt64"` +} +