Explorar o código

optimized parse tag (#256)

kingxt %!s(int64=4) %!d(string=hai) anos
pai
achega
67804a6bb2
Modificáronse 4 ficheiros con 108 adicións e 73 borrados
  1. 2 1
      go.mod
  2. 2 2
      go.sum
  3. 37 70
      tools/goctl/api/spec/fn.go
  4. 67 0
      tools/goctl/api/spec/tags.go

+ 2 - 1
go.mod

@@ -11,6 +11,7 @@ require (
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/emicklei/proto v1.9.0
 	github.com/fatih/color v1.9.0 // indirect
+	github.com/fatih/structtag v1.2.0
 	github.com/frankban/quicktest v1.7.2 // indirect
 	github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
 	github.com/go-redis/redis v6.15.7+incompatible
@@ -55,7 +56,7 @@ require (
 	golang.org/x/tools v0.0.0-20200410132612-ae9902aceb98 // indirect
 	google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f // indirect
 	google.golang.org/grpc v1.29.1
-	google.golang.org/protobuf v1.25.0
+	google.golang.org/protobuf v1.25.0 // indirect
 	gopkg.in/cheggaaa/pb.v1 v1.0.28
 	gopkg.in/h2non/gock.v1 v1.0.15
 	gopkg.in/yaml.v2 v2.4.0

+ 2 - 2
go.sum

@@ -63,6 +63,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
+github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
 github.com/frankban/quicktest v1.7.2 h1:2QxQoC1TS09S7fhCPsrvqYdvP1H5M1P1ih5ABm3BTYk=
 github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
@@ -450,8 +452,6 @@ gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 37 - 70
tools/goctl/api/spec/fn.go

@@ -2,30 +2,15 @@ package spec
 
 import (
 	"errors"
-	"regexp"
 	"strings"
 
 	"github.com/tal-tech/go-zero/core/stringx"
 	"github.com/tal-tech/go-zero/tools/goctl/util"
 )
 
-const (
-	TagKey    = "tag"
-	NameKey   = "name"
-	OptionKey = "option"
-	BodyTag   = "json"
-)
-
-var (
-	TagRe       = regexp.MustCompile(`(?P<tag>\w+):"(?P<name>[^,"]+)[,]?(?P<option>[^"]*)"`)
-	TagSubNames = TagRe.SubexpNames()
-	definedTags = []string{TagKey, NameKey, OptionKey}
-)
+const bodyTagKey = "json"
 
-type Attribute struct {
-	Key   string
-	value string
-}
+var definedKeys = []string{"json", "form", "path"}
 
 func (s Service) Routes() []Route {
 	var result []Route
@@ -35,81 +20,62 @@ func (s Service) Routes() []Route {
 	return result
 }
 
-func (m Member) IsOptional() bool {
-	var option string
-
-	matches := TagRe.FindStringSubmatch(m.Tag)
-	for i := range matches {
-		name := TagSubNames[i]
-		if name == OptionKey {
-			option = matches[i]
-		}
+func (m Member) Tags() []*Tag {
+	tags, err := Parse(m.Tag)
+	if err != nil {
+		panic(m.Tag + ", " + err.Error())
 	}
 
-	if len(option) == 0 {
+	return tags.Tags()
+}
+
+func (m Member) IsOptional() bool {
+	if !m.IsBodyMember() {
 		return false
 	}
 
-	fields := strings.Split(option, ",")
-	for _, field := range fields {
-		if field == "optional" || strings.HasPrefix(field, "default=") {
-			return true
+	tag := m.Tags()
+	for _, item := range tag {
+		if item.Key == bodyTagKey {
+			if stringx.Contains(item.Options, "optional") {
+				return true
+			}
 		}
 	}
-
 	return false
 }
 
 func (m Member) IsOmitempty() bool {
-	var option string
-
-	matches := TagRe.FindStringSubmatch(m.Tag)
-	for i := range matches {
-		name := TagSubNames[i]
-		if name == OptionKey {
-			option = matches[i]
-		}
-	}
-
-	if len(option) == 0 {
+	if !m.IsBodyMember() {
 		return false
 	}
 
-	fields := strings.Split(option, ",")
-	for _, field := range fields {
-		if field == "omitempty" {
-			return true
+	tag := m.Tags()
+	for _, item := range tag {
+		if item.Key == bodyTagKey {
+			if stringx.Contains(item.Options, "omitempty") {
+				return true
+			}
 		}
 	}
-
 	return false
 }
 
-func (m Member) GetAttributes() []Attribute {
-	matches := TagRe.FindStringSubmatch(m.Tag)
-	var result []Attribute
-	for i := range matches {
-		name := TagSubNames[i]
-		if stringx.Contains(definedTags, name) {
-			result = append(result, Attribute{
-				Key:   name,
-				value: matches[i],
-			})
-		}
+func (m Member) GetPropertyName() (string, error) {
+	tags := m.Tags()
+	if len(tags) == 0 {
+		return "", errors.New("json property name not exist, member: " + m.Name)
 	}
-	return result
-}
 
-func (m Member) GetPropertyName() (string, error) {
-	attrs := m.GetAttributes()
-	for _, attr := range attrs {
-		if attr.Key == NameKey && len(attr.value) > 0 {
-			if attr.value == "-" {
+	for _, tag := range tags {
+		if stringx.Contains(definedKeys, tag.Key) {
+			if tag.Name == "-" {
 				return util.Untitle(m.Name), nil
 			}
-			return attr.value, nil
+			return tag.Name, nil
 		}
 	}
+
 	return "", errors.New("json property name not exist, member: " + m.Name)
 }
 
@@ -121,9 +87,10 @@ func (m Member) IsBodyMember() bool {
 	if m.IsInline {
 		return true
 	}
-	attrs := m.GetAttributes()
-	for _, attr := range attrs {
-		if attr.value == BodyTag {
+
+	tags := m.Tags()
+	for _, tag := range tags {
+		if tag.Key == bodyTagKey {
 			return true
 		}
 	}

+ 67 - 0
tools/goctl/api/spec/tags.go

@@ -0,0 +1,67 @@
+package spec
+
+import (
+	"errors"
+	"strings"
+
+	"github.com/fatih/structtag"
+)
+
+var errTagNotExist = errors.New("tag does not exist")
+
+type (
+	Tag struct {
+		// Key is the tag key, such as json, xml, etc..
+		// i.e: `json:"foo,omitempty". Here key is: "json"
+		Key string
+
+		// Name is a part of the value
+		// i.e: `json:"foo,omitempty". Here name is: "foo"
+		Name string
+
+		// Options is a part of the value. It contains a slice of tag options i.e:
+		// `json:"foo,omitempty". Here options is: ["omitempty"]
+		Options []string
+	}
+
+	Tags struct {
+		tags []*Tag
+	}
+)
+
+func Parse(tag string) (*Tags, error) {
+	tag = strings.TrimPrefix(tag, "`")
+	tag = strings.TrimSuffix(tag, "`")
+	tags, err := structtag.Parse(tag)
+	if err != nil {
+		return nil, err
+	}
+
+	var result Tags
+	for _, item := range tags.Tags() {
+		result.tags = append(result.tags, &Tag{Key: item.Key, Name: item.Name, Options: item.Options})
+	}
+	return &result, nil
+}
+
+func (t *Tags) Get(key string) (*Tag, error) {
+	for _, tag := range t.tags {
+		if tag.Key == key {
+			return tag, nil
+		}
+	}
+
+	return nil, errTagNotExist
+}
+
+func (t *Tags) Keys() []string {
+	var keys []string
+	for _, tag := range t.tags {
+		keys = append(keys, tag.Key)
+	}
+	return keys
+}
+
+func (t *Tags) Tags() []*Tag {
+	return t.tags
+}