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", "number", "array", "map", "slice", "struct", "MarshalJSON", "MarshalText", "recursive", "intString", "uintString", "intPtr", "uintPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr", "numberPtr", "arrayPtr", "mapPtr", "slicePtr", "marshalJSONPtr", "marshalTextPtr", "interfacePtr", "recursivePtr", } 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("SliceElem", "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) } }