go-json/cmd/generator/main.go

536 lines
13 KiB
Go

package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"path/filepath"
"runtime"
"strings"
"text/template"
)
type opType struct {
Op string
Code string
HeadToPtrHead func() string
HeadToNPtrHead func() string
HeadToAnonymousHead func() string
HeadToOmitEmptyHead func() string
HeadToStringTagHead func() string
HeadToOnlyHead func() string
PtrHeadToHead func() string
FieldToEnd func() string
FieldToOmitEmptyField func() string
FieldToStringTagField func() string
}
func (t opType) IsHeadToPtrHead() bool {
return t.Op != t.HeadToPtrHead()
}
func (t opType) IsHeadToNPtrHead() bool {
return t.Op != t.HeadToNPtrHead()
}
func (t opType) IsHeadToAnonymousHead() bool {
return t.Op != t.HeadToAnonymousHead()
}
func (t opType) IsHeadToOmitEmptyHead() bool {
return t.Op != t.HeadToOmitEmptyHead()
}
func (t opType) IsHeadToStringTagHead() bool {
return t.Op != t.HeadToStringTagHead()
}
func (t opType) IsPtrHeadToHead() bool {
return t.Op != t.PtrHeadToHead()
}
func (t opType) IsHeadToOnlyHead() bool {
return t.Op != t.HeadToOnlyHead()
}
func (t opType) IsFieldToEnd() bool {
return t.Op != t.FieldToEnd()
}
func (t opType) IsFieldToOmitEmptyField() bool {
return t.Op != t.FieldToOmitEmptyField()
}
func (t opType) IsFieldToStringTagField() bool {
return t.Op != t.FieldToStringTagField()
}
func createOpType(op, code string) opType {
return opType{
Op: op,
Code: code,
HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op },
HeadToOnlyHead: func() string { return op },
PtrHeadToHead: func() string { return op },
FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op },
FieldToStringTagField: func() string { return op },
}
}
func _main() error {
tmpl, err := template.New("").Parse(`// Code generated by cmd/generator. DO NOT EDIT!
package json
import (
"strings"
)
type codeType int
const (
{{- range $index, $type := .CodeTypes }}
code{{ $type }} codeType = {{ $index }}
{{- end }}
)
var opTypeStrings = [{{ .OpLen }}]string{
{{- range $type := .OpTypes }}
"{{ $type.Op }}",
{{- end }}
}
type opType int
const (
{{- range $index, $type := .OpTypes }}
op{{ $type.Op }} opType = {{ $index }}
{{- end }}
)
func (t opType) String() string {
if int(t) >= {{ .OpLen }} {
return ""
}
return opTypeStrings[int(t)]
}
func (t opType) codeType() codeType {
if strings.Contains(t.String(), "Struct") {
if strings.Contains(t.String(), "End") {
return codeStructEnd
}
return codeStructField
}
if strings.Contains(t.String(), "ArrayHead") {
return codeArrayHead
}
if strings.Contains(t.String(), "ArrayElem") {
return codeArrayElem
}
if strings.Contains(t.String(), "SliceHead") {
return codeSliceHead
}
if strings.Contains(t.String(), "SliceElem") {
return codeSliceElem
}
if strings.Contains(t.String(), "MapHead") {
return codeMapHead
}
if strings.Contains(t.String(), "MapKey") {
return codeMapKey
}
if strings.Contains(t.String(), "MapValue") {
return codeMapValue
}
if strings.Contains(t.String(), "MapEnd") {
return codeMapEnd
}
return codeOp
}
func (t opType) headToPtrHead() opType {
if strings.Index(t.String(), "PtrHead") > 0 {
return t
}
if strings.Index(t.String(), "PtrAnonymousHead") > 0 {
return t
}
idx := strings.Index(t.String(), "Field")
if idx == -1 {
return t
}
suffix := "Ptr"+t.String()[idx+len("Field"):]
const toPtrOffset = 12
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
return opType(int(t) + toPtrOffset)
}
return t
}
func (t opType) headToNPtrHead() opType {
if strings.Index(t.String(), "PtrHead") > 0 {
return t
}
if strings.Index(t.String(), "PtrAnonymousHead") > 0 {
return t
}
idx := strings.Index(t.String(), "Field")
if idx == -1 {
return t
}
suffix := "NPtr"+t.String()[idx+len("Field"):]
const toPtrOffset = 24
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
return opType(int(t) + toPtrOffset)
}
return t
}
func (t opType) headToAnonymousHead() opType {
const toAnonymousOffset = 6
if strings.Contains(opType(int(t) + toAnonymousOffset).String(), "Anonymous") {
return opType(int(t) + toAnonymousOffset)
}
return t
}
func (t opType) headToOmitEmptyHead() opType {
const toOmitEmptyOffset = 2
if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
return opType(int(t) + toOmitEmptyOffset)
}
return t
}
func (t opType) headToStringTagHead() opType {
const toStringTagOffset = 4
if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
return opType(int(t) + toStringTagOffset)
}
return t
}
func (t opType) headToOnlyHead() opType {
if strings.HasSuffix(t.String(), "Head") || strings.HasSuffix(t.String(), "HeadOmitEmpty") || strings.HasSuffix(t.String(), "HeadStringTag") {
return t
}
const toOnlyOffset = 1
if opType(int(t) + toOnlyOffset).String() == t.String() + "Only" {
return opType(int(t) + toOnlyOffset)
}
return t
}
func (t opType) ptrHeadToHead() opType {
idx := strings.Index(t.String(), "Ptr")
if idx == -1 {
return t
}
suffix := t.String()[idx+len("Ptr"):]
const toPtrOffset = 12
if strings.Contains(opType(int(t) - toPtrOffset).String(), suffix) {
return opType(int(t) - toPtrOffset)
}
return t
}
func (t opType) fieldToEnd() opType {
switch t {
{{- range $type := .OpTypes }}
{{- if $type.IsFieldToEnd }}
case op{{ $type.Op }}:
return op{{ call $type.FieldToEnd }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) fieldToOmitEmptyField() opType {
const toOmitEmptyOffset = 1
if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
return opType(int(t) + toOmitEmptyOffset)
}
return t
}
func (t opType) fieldToStringTagField() opType {
const toStringTagOffset = 2
if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
return opType(int(t) + toStringTagOffset)
}
return t
}
`)
if err != nil {
return err
}
codeTypes := []string{
"Op",
"ArrayHead",
"ArrayElem",
"SliceHead",
"SliceElem",
"MapHead",
"MapKey",
"MapValue",
"MapEnd",
"StructFieldRecursive",
"StructField",
"StructEnd",
}
primitiveTypes := []string{
"int", "uint", "float32", "float64", "bool", "string", "bytes",
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
"intString", "uintString",
"intPtr", "uintPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr",
"intNPtr", "uintNPtr", "float32NPtr", "float64NPtr", "boolNPtr", "stringNPtr", "bytesNPtr",
}
primitiveTypesUpper := []string{}
for _, typ := range primitiveTypes {
primitiveTypesUpper = append(primitiveTypesUpper, strings.ToUpper(string(typ[0]))+typ[1:])
}
opTypes := []opType{
createOpType("End", "Op"),
createOpType("Interface", "Op"),
createOpType("Ptr", "Op"),
createOpType("NPtr", "Op"),
createOpType("SliceHead", "SliceHead"),
createOpType("RootSliceHead", "SliceHead"),
createOpType("SliceElem", "SliceElem"),
createOpType("RootSliceElem", "SliceElem"),
createOpType("SliceEnd", "Op"),
createOpType("ArrayHead", "ArrayHead"),
createOpType("ArrayElem", "ArrayElem"),
createOpType("ArrayEnd", "Op"),
createOpType("MapHead", "MapHead"),
createOpType("MapHeadLoad", "MapHead"),
createOpType("MapKey", "MapKey"),
createOpType("MapValue", "MapValue"),
createOpType("MapEnd", "Op"),
createOpType("StructFieldRecursiveEnd", "Op"),
createOpType("StructAnonymousEnd", "StructEnd"),
}
for _, typ := range primitiveTypesUpper {
typ := typ
opTypes = append(opTypes, createOpType(typ, "Op"))
}
for _, typ := range append(primitiveTypesUpper, "") {
for _, ptrOrNot := range []string{"", "Ptr", "NPtr"} {
for _, headType := range []string{"", "Anonymous"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
for _, onlyOrNot := range []string{"", "Only"} {
ptrOrNot := ptrOrNot
headType := headType
opt := opt
typ := typ
onlyOrNot := onlyOrNot
op := fmt.Sprintf(
"StructField%s%sHead%s%s%s",
ptrOrNot,
headType,
opt,
typ,
onlyOrNot,
)
opTypes = append(opTypes, opType{
Op: op,
Code: "StructField",
HeadToPtrHead: func() string {
return fmt.Sprintf(
"StructFieldPtr%sHead%s%s%s",
headType,
opt,
typ,
onlyOrNot,
)
},
HeadToNPtrHead: func() string {
return fmt.Sprintf(
"StructFieldNPtr%sHead%s%s%s",
headType,
opt,
typ,
onlyOrNot,
)
},
HeadToAnonymousHead: func() string {
return fmt.Sprintf(
"StructField%sAnonymousHead%s%s%s",
ptrOrNot,
opt,
typ,
onlyOrNot,
)
},
HeadToOmitEmptyHead: func() string {
return fmt.Sprintf(
"StructField%s%sHeadOmitEmpty%s%s",
ptrOrNot,
headType,
typ,
onlyOrNot,
)
},
HeadToStringTagHead: func() string {
return fmt.Sprintf(
"StructField%s%sHeadStringTag%s%s",
ptrOrNot,
headType,
typ,
onlyOrNot,
)
},
HeadToOnlyHead: func() string {
switch typ {
case "", "Array", "Map", "MapLoad", "Slice",
"Struct", "Recursive", "MarshalJSON", "MarshalText",
"IntString", "UintString":
return op
}
return fmt.Sprintf(
"StructField%s%sHead%s%sOnly",
ptrOrNot,
headType,
opt,
typ,
)
},
PtrHeadToHead: func() string {
return fmt.Sprintf(
"StructField%sHead%s%s%s",
headType,
opt,
typ,
onlyOrNot,
)
},
FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op },
FieldToStringTagField: func() string { return op },
})
}
}
}
}
}
for _, typ := range append(primitiveTypesUpper, "") {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
opt := opt
typ := typ
op := fmt.Sprintf(
"StructField%s%s",
opt,
typ,
)
opTypes = append(opTypes, opType{
Op: op,
Code: "StructField",
HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op },
HeadToOnlyHead: func() string { return op },
PtrHeadToHead: func() string { return op },
FieldToEnd: func() string {
switch typ {
case "", "Array", "Map", "MapLoad", "Slice", "Struct", "Recursive":
return op
}
return fmt.Sprintf(
"StructEnd%s%s",
opt,
typ,
)
},
FieldToOmitEmptyField: func() string {
return fmt.Sprintf(
"StructFieldOmitEmpty%s",
typ,
)
},
FieldToStringTagField: func() string {
return fmt.Sprintf(
"StructFieldStringTag%s",
typ,
)
},
})
}
}
for _, typ := range append(primitiveTypesUpper, "") {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
opt := opt
typ := typ
op := fmt.Sprintf(
"StructEnd%s%s",
opt,
typ,
)
opTypes = append(opTypes, opType{
Op: op,
Code: "StructEnd",
HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op },
HeadToOnlyHead: func() string { return op },
PtrHeadToHead: func() string { return op },
FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op },
FieldToStringTagField: func() string { return op },
})
}
}
var b bytes.Buffer
if err := tmpl.Execute(&b, struct {
CodeTypes []string
OpTypes []opType
OpLen int
}{
CodeTypes: codeTypes,
OpTypes: opTypes,
OpLen: len(opTypes),
}); err != nil {
return err
}
path := filepath.Join(repoRoot(), "encode_optype.go")
buf, err := format.Source(b.Bytes())
if err != nil {
return err
}
return ioutil.WriteFile(path, buf, 0644)
}
func repoRoot() string {
_, file, _, _ := runtime.Caller(0)
relativePathFromRepoRoot := filepath.Join("cmd", "generator")
return strings.TrimSuffix(filepath.Dir(file), relativePathFromRepoRoot)
}
func main() {
if err := _main(); err != nil {
panic(err)
}
}