forked from mirror/go-json
492 lines
12 KiB
Go
492 lines
12 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
|
|
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) 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 },
|
|
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 = 6
|
|
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 = 12
|
|
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
|
|
return opType(int(t) + toPtrOffset)
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (t opType) headToAnonymousHead() opType {
|
|
const toAnonymousOffset = 3
|
|
if strings.Contains(opType(int(t) + toAnonymousOffset).String(), "Anonymous") {
|
|
return opType(int(t) + toAnonymousOffset)
|
|
}
|
|
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 = 6
|
|
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",
|
|
"arrayPtr", "slicePtr", "mapPtr", "marshalJSONPtr", "marshalTextPtr",
|
|
"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"} {
|
|
ptrOrNot := ptrOrNot
|
|
headType := headType
|
|
opt := opt
|
|
typ := typ
|
|
|
|
op := fmt.Sprintf(
|
|
"StructField%s%sHead%s%s",
|
|
ptrOrNot,
|
|
headType,
|
|
opt,
|
|
typ,
|
|
)
|
|
opTypes = append(opTypes, opType{
|
|
Op: op,
|
|
Code: "StructField",
|
|
HeadToPtrHead: func() string {
|
|
return fmt.Sprintf(
|
|
"StructFieldPtr%sHead%s%s",
|
|
headType,
|
|
opt,
|
|
typ,
|
|
)
|
|
},
|
|
HeadToNPtrHead: func() string {
|
|
return fmt.Sprintf(
|
|
"StructFieldNPtr%sHead%s%s",
|
|
headType,
|
|
opt,
|
|
typ,
|
|
)
|
|
},
|
|
HeadToAnonymousHead: func() string {
|
|
return fmt.Sprintf(
|
|
"StructField%sAnonymousHead%s%s",
|
|
ptrOrNot,
|
|
opt,
|
|
typ,
|
|
)
|
|
},
|
|
HeadToOmitEmptyHead: func() string {
|
|
return fmt.Sprintf(
|
|
"StructField%s%sHeadOmitEmpty%s",
|
|
ptrOrNot,
|
|
headType,
|
|
typ,
|
|
)
|
|
},
|
|
HeadToStringTagHead: func() string {
|
|
return fmt.Sprintf(
|
|
"StructField%s%sHeadStringTag%s",
|
|
ptrOrNot,
|
|
headType,
|
|
typ,
|
|
)
|
|
},
|
|
PtrHeadToHead: func() string {
|
|
return fmt.Sprintf(
|
|
"StructField%sHead%s%s",
|
|
headType,
|
|
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 },
|
|
HeadToNPtrHead: func() string { return op },
|
|
HeadToAnonymousHead: 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", "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 },
|
|
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)
|
|
}
|
|
}
|