diff --git a/.gitignore b/.gitignore index 698e891b6..e6155ae25 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ # For files created by specific development environment (e.g. editor), # use alternative ways to exclude files from git. # For example, set up .git/info/exclude or use a global .gitignore. +.idea diff --git a/cmd/protoc-gen-go/internal_gengo/gotags.go b/cmd/protoc-gen-go/internal_gengo/gotags.go new file mode 100644 index 000000000..27a137e67 --- /dev/null +++ b/cmd/protoc-gen-go/internal_gengo/gotags.go @@ -0,0 +1,137 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// Added by Hao Luo +// Source: https://github.com/hacksomecn/protobuf-go.git +// Branch: feature/tags + +// Package internal_gengo Support add custom struct field tags +// Protoc parse field tags from field's tailing comment, declare extra tags like: +// message Example { +// ... +// string name = 1; // @go_tags(`bson:"name" yaml:"name"`) FORM 1 support comment tail +// ... +// } +// +// FORM 1: Go tags regexp: `(\s?)@go_tags\(` + "(`.*`)" + `\)\s` +package internal_gengo + +import ( + "google.golang.org/protobuf/compiler/protogen" + "log" + "regexp" + "strings" +) + +var tailingGoTagsExcludeKeys = map[string]bool{ + "protobuf": true, + "protobuf_key": true, + "protobuf_val": true, +} + +var commentGoTagsRe *regexp.Regexp + +func init() { + var err error + commentGoTagsRe, err = regexp.Compile(`(\s?)@go_tags\(` + "(`.*`)" + `\)\s`) + if err != nil { + log.Fatalf("compile comment go tags regexp failed. %s", err) + return + } +} + +// AppendGoTagsFromFieldComment append extra tags parsed from tailing comment +// tag with same name will be replaced except protobuf tags like "protobuf"、"protobuf_key"、"protobuf_val" +func AppendGoTagsFromFieldComment( + existsTags structTags, + tailComment protogen.Comments, +) ( + newTags structTags, + newTailing protogen.Comments, +) { + newTags = existsTags + newTailing = tailComment + + tagsMap := map[string]string{} // key -> value + seqKeys := make([]string, 0) + for _, existTags := range existsTags { + key := existTags[0] + value := existTags[1] + tagsMap[key] = value + seqKeys = append(seqKeys, key) + } + + tailTags, newTailing := ParseGoTagsFromTailingComment(tailComment) + for _, tailTag := range tailTags { + key := tailTag.Key + value := tailTag.Value + if tailingGoTagsExcludeKeys[key] { + continue + } + + _, exists := tagsMap[key] + if !exists { // keep sequence + seqKeys = append(seqKeys, key) + } + + tagsMap[key] = value + } + + newTags = make([][2]string, 0) + for _, key := range seqKeys { + tag := tagsMap[key] + newTags = append(newTags, [2]string{key, tag}) + } + + return +} + +type GoTag struct { + Key string + Value string +} + +// ParseGoTagsFromTailingComment parse go tags from comment +func ParseGoTagsFromTailingComment(tailing protogen.Comments) ( + tags []GoTag, + newTailing protogen.Comments, +) { + newTailing = tailing + + matched := commentGoTagsRe.FindStringSubmatch(string(tailing)) + if len(matched) != 3 { + return + } + + strMatched := matched[0] + strStart := matched[1] + strTagsReplacer := strings.Replace(strMatched, strStart, "", 1) + newTailing = protogen.Comments(strings.Replace(string(tailing), strTagsReplacer, "", 1)) + + strTags := matched[2] + strTags = strings.Trim(strTags, "`") + + strPairs := strings.Split(strTags, " ") + for _, pair := range strPairs { + pair = strings.TrimSpace(pair) + if pair == "" { + continue + } + + separateIndex := strings.Index(pair, ":") + if separateIndex < 0 || separateIndex == len(pair)-1 { + continue + } + + key := pair[:separateIndex] + value := pair[separateIndex+1:] + value = strings.Trim(value, "\"") + + tags = append(tags, GoTag{ + Key: key, + Value: value, + }) + } + + return +} diff --git a/cmd/protoc-gen-go/internal_gengo/gotags_test.go b/cmd/protoc-gen-go/internal_gengo/gotags_test.go new file mode 100644 index 000000000..719ed2c2a --- /dev/null +++ b/cmd/protoc-gen-go/internal_gengo/gotags_test.go @@ -0,0 +1,42 @@ +package internal_gengo + +import ( + "fmt" + "google.golang.org/protobuf/compiler/protogen" + "regexp" + "testing" +) + +func TestCommentTagsReg(t *testing.T) { + re, err := regexp.Compile(`(\s?)@go_tags\(` + "(`.*`)" + `\)\s`) + if err != nil { + t.Error(err) + return + } + + str := " @go_tags(`json:\"name\"`) abc" + matched := re.FindStringSubmatch(str) + fmt.Println(len(matched), matched) +} + +func TestParseGoTagsFromTailingComment(t *testing.T) { + str := " @go_tags(`json:\"name,omitempty\"`) abc" + tags, newTailing := ParseGoTagsFromTailingComment(protogen.Comments(str)) + for key, value := range tags { + fmt.Println(key, value) + } + fmt.Println(newTailing) +} + +func TestAppendGoTagsFromTailingComment(t *testing.T) { + tags := structTags{ + {"protobuf", "abc"}, + {"json", "efg"}, + {"protobuf_key", "string"}, + {"protobuf_val", "string"}, + } + str := " @go_tags(`json:\"name,omitempty\" bson:\"name\"`) abc" + newTags, newTailing := AppendGoTagsFromFieldComment(tags, protogen.Comments(str)) + fmt.Println(newTailing) + fmt.Println(newTags) +} diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go index 8cae43201..f34f47bba 100644 --- a/cmd/protoc-gen-go/internal_gengo/main.go +++ b/cmd/protoc-gen-go/internal_gengo/main.go @@ -418,17 +418,23 @@ func genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, fie tags = append(tags, gotrackTags...) } + var leftLeading protogen.Comments + var leftTailing protogen.Comments + tags, leftLeading = AppendGoTagsFromFieldComment(tags, field.Comments.Leading) + tags, leftTailing = AppendGoTagsFromFieldComment(tags, field.Comments.Trailing) + name := field.GoName if field.Desc.IsWeak() { name = genid.WeakFieldPrefix_goname + name } g.Annotate(m.GoIdent.GoName+"."+name, field.Location) - leadingComments := appendDeprecationSuffix(field.Comments.Leading, + leadingComments := appendDeprecationSuffix(leftLeading, field.Desc.ParentFile(), field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated()) g.P(leadingComments, name, " ", goType, tags, - trailingComment(field.Comments.Trailing)) + //trailingComment(field.Comments.Trailing)) + trailingComment(leftTailing)) sf.append(field.GoName) }