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 internal/cmd/generator. DO NOT EDIT! package encoder 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 } switch t { case OpArray, OpArrayPtr: return CodeArrayHead case OpArrayElem: return CodeArrayElem case OpSlice, OpSlicePtr: return CodeSliceHead case OpSliceElem: return CodeSliceElem case OpMap, OpMapPtr: return CodeMapHead case OpMapKey: return CodeMapKey case OpMapValue: return CodeMapValue case OpMapEnd: return CodeMapEnd } return CodeOp } func (t OpType) HeadToPtrHead() OpType { if strings.Index(t.String(), "PtrHead") > 0 { return t } idx := strings.Index(t.String(), "Head") if idx == -1 { return t } suffix := "Ptr"+t.String()[idx+len("Head"):] 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 { idx := strings.Index(t.String(), "Field") if idx == -1 { return t } suffix := t.String()[idx+len("Field"):] const toEndOffset = 3 if strings.Contains(OpType(int(t) + toEndOffset).String(), "End"+suffix) { return OpType(int(t) + toEndOffset) } 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( "Struct%sHead%s%s", ptrOrNot, opt, typ, ) opTypes = append(opTypes, opType{ Op: op, Code: "StructField", HeadToPtrHead: func() string { return fmt.Sprintf( "StructPtrHead%s%s", opt, typ, ) }, HeadToOmitEmptyHead: func() string { return fmt.Sprintf( "Struct%sHeadOmitEmpty%s", ptrOrNot, typ, ) }, HeadToStringTagHead: func() string { return fmt.Sprintf( "Struct%sHeadStringTag%s", ptrOrNot, typ, ) }, PtrHeadToHead: func() string { return fmt.Sprintf( "StructHead%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 _, 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(), "internal", "encoder", "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("internal", "cmd", "generator") return strings.TrimSuffix(filepath.Dir(file), relativePathFromRepoRoot) } //go:generate go run main.go func main() { if err := _main(); err != nil { panic(err) } }