go-json/cmd/generator/main.go

414 lines
9.6 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
HeadToOmitEmptyHead func() string
HeadToStringTagHead 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) 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) 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 },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: 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 t.String() == "Array" || t.String() == "ArrayPtr" {
return codeArrayHead
}
if strings.Contains(t.String(), "ArrayElem") {
return codeArrayElem
}
if t.String() == "Slice" || t.String() == "SlicePtr" {
return codeSliceHead
}
if strings.Contains(t.String(), "SliceElem") {
return codeSliceElem
}
if t.String() == "Map" || t.String() == "MapPtr" {
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
}
idx := strings.Index(t.String(), "Field")
if idx == -1 {
return t
}
suffix := "Ptr"+t.String()[idx+len("Field"):]
const toPtrOffset = 3
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
return opType(int(t) + toPtrOffset)
}
return t
}
func (t opType) headToOmitEmptyHead() opType {
const toOmitEmptyOffset = 1
if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
return opType(int(t) + toOmitEmptyOffset)
}
return t
}
func (t opType) headToStringTagHead() opType {
const toStringTagOffset = 2
if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
return opType(int(t) + toStringTagOffset)
}
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 = 3
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", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
"intString", "uintString",
"intPtr", "uintPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr",
"arrayPtr", "slicePtr", "mapPtr", "marshalJSONPtr", "marshalTextPtr", "interfacePtr",
}
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("RootSliceHead", "SliceHead"),
createOpType("SliceElem", "SliceElem"),
createOpType("RootSliceElem", "SliceElem"),
createOpType("SliceEnd", "Op"),
createOpType("ArrayElem", "ArrayElem"),
createOpType("ArrayEnd", "Op"),
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"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
ptrOrNot := ptrOrNot
opt := opt
typ := typ
op := fmt.Sprintf(
"StructField%sHead%s%s",
ptrOrNot,
opt,
typ,
)
opTypes = append(opTypes, opType{
Op: op,
Code: "StructField",
HeadToPtrHead: func() string {
return fmt.Sprintf(
"StructFieldPtrHead%s%s",
opt,
typ,
)
},
HeadToOmitEmptyHead: func() string {
return fmt.Sprintf(
"StructField%sHeadOmitEmpty%s",
ptrOrNot,
typ,
)
},
HeadToStringTagHead: func() string {
return fmt.Sprintf(
"StructField%sHeadStringTag%s",
ptrOrNot,
typ,
)
},
PtrHeadToHead: func() string {
return fmt.Sprintf(
"StructFieldHead%s%s",
opt,
typ,
)
},
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 },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op },
PtrHeadToHead: func() string { return op },
FieldToEnd: func() string {
switch typ {
case "", "Array", "Map", "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 },
HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: 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)
}
}