From 12e235efb04be78daa6e768bfa845410b9ae740e Mon Sep 17 00:00:00 2001 From: kingxt Date: Mon, 4 Jan 2021 18:59:48 +0800 Subject: [PATCH] optimized goctl format (#336) * fix format * refactor * refactor * optimized * refactor * refactor * refactor * add js path prefix --- tools/goctl/api/format/format.go | 90 +++++++++++++++++++++++- tools/goctl/api/format/format_test.go | 6 +- tools/goctl/api/javagen/gencomponents.go | 6 ++ tools/goctl/api/spec/fn.go | 4 -- tools/goctl/api/tsgen/genpacket.go | 71 +++++++++++-------- tools/goctl/api/tsgen/vars.go | 1 + 6 files changed, 138 insertions(+), 40 deletions(-) diff --git a/tools/goctl/api/format/format.go b/tools/goctl/api/format/format.go index 0c8940a8..4c6482bb 100644 --- a/tools/goctl/api/format/format.go +++ b/tools/goctl/api/format/format.go @@ -4,6 +4,7 @@ import ( "bufio" "errors" "fmt" + "go/format" "go/scanner" "io/ioutil" "os" @@ -13,6 +14,7 @@ import ( "github.com/tal-tech/go-zero/core/errorx" "github.com/tal-tech/go-zero/tools/goctl/api/parser" "github.com/tal-tech/go-zero/tools/goctl/api/util" + ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" "github.com/urfave/cli" ) @@ -103,24 +105,108 @@ func apiFormat(data string) (string, error) { var builder strings.Builder s := bufio.NewScanner(strings.NewReader(data)) var tapCount = 0 + var newLineCount = 0 + var preLine string for s.Scan() { line := strings.TrimSpace(s.Text()) + if len(line) == 0 { + if newLineCount > 0 { + continue + } + newLineCount++ + } else { + if preLine == rightBrace { + builder.WriteString(ctlutil.NL) + } + newLineCount = 0 + } + + if tapCount == 0 { + format, err := formatGoTypeDef(line, s, &builder) + if err != nil { + return "", err + } + + if format { + continue + } + } + noCommentLine := util.RemoveComment(line) if noCommentLine == rightParenthesis || noCommentLine == rightBrace { tapCount -= 1 } if tapCount < 0 { - line = strings.TrimSuffix(line, rightBrace) + line := strings.TrimSuffix(noCommentLine, rightBrace) line = strings.TrimSpace(line) if strings.HasSuffix(line, leftBrace) { tapCount += 1 } } util.WriteIndent(&builder, tapCount) - builder.WriteString(line + "\n") + builder.WriteString(line + ctlutil.NL) if strings.HasSuffix(noCommentLine, leftParenthesis) || strings.HasSuffix(noCommentLine, leftBrace) { tapCount += 1 } + preLine = line } return strings.TrimSpace(builder.String()), nil } + +func formatGoTypeDef(line string, scanner *bufio.Scanner, builder *strings.Builder) (bool, error) { + noCommentLine := util.RemoveComment(line) + tokenCount := 0 + if strings.HasPrefix(noCommentLine, "type") && (strings.HasSuffix(noCommentLine, leftParenthesis) || + strings.HasSuffix(noCommentLine, leftBrace)) { + var typeBuilder strings.Builder + typeBuilder.WriteString(mayInsertStructKeyword(line, &tokenCount) + ctlutil.NL) + for scanner.Scan() { + noCommentLine := util.RemoveComment(scanner.Text()) + typeBuilder.WriteString(mayInsertStructKeyword(scanner.Text(), &tokenCount) + ctlutil.NL) + if noCommentLine == rightBrace || noCommentLine == rightParenthesis { + tokenCount-- + } + if tokenCount == 0 { + ts, err := format.Source([]byte(typeBuilder.String())) + if err != nil { + return false, errors.New("error format \n" + typeBuilder.String()) + } + + result := strings.ReplaceAll(string(ts), " struct ", " ") + result = strings.ReplaceAll(result, "type ()", "") + builder.WriteString(result) + break + } + } + + return true, nil + } + return false, nil +} + +func mayInsertStructKeyword(line string, token *int) string { + insertStruct := func() string { + if strings.Contains(line, " struct") { + return line + } + index := strings.Index(line, leftBrace) + return line[:index] + " struct " + line[index:] + } + + noCommentLine := util.RemoveComment(line) + if strings.HasSuffix(noCommentLine, leftBrace) { + *token++ + return insertStruct() + } + if strings.HasSuffix(noCommentLine, rightBrace) { + noCommentLine = strings.TrimSuffix(noCommentLine, rightBrace) + noCommentLine = util.RemoveComment(noCommentLine) + if strings.HasSuffix(noCommentLine, leftBrace) { + return insertStruct() + } + } + if strings.HasSuffix(noCommentLine, leftParenthesis) { + *token++ + } + return line +} diff --git a/tools/goctl/api/format/format_test.go b/tools/goctl/api/format/format_test.go index a0f9444a..0d975b5c 100644 --- a/tools/goctl/api/format/format_test.go +++ b/tools/goctl/api/format/format_test.go @@ -24,11 +24,11 @@ handler: GreetHandler } ` - formattedStr = `type Request struct { + formattedStr = `type Request { Name string } -type Response struct { +type Response { Message string } @@ -40,7 +40,7 @@ service A-api { }` ) -func TestInlineTypeNotExist(t *testing.T) { +func TestFormat(t *testing.T) { r, err := apiFormat(notFormattedStr) assert.Nil(t, err) assert.Equal(t, r, formattedStr) diff --git a/tools/goctl/api/javagen/gencomponents.go b/tools/goctl/api/javagen/gencomponents.go index eb53296d..613c7e8e 100644 --- a/tools/goctl/api/javagen/gencomponents.go +++ b/tools/goctl/api/javagen/gencomponents.go @@ -90,6 +90,12 @@ func writeType(writer io.Writer, tp spec.Type, types []spec.Type) error { func writeMembers(writer io.Writer, types []spec.Type, members []spec.Member, allMembers *[]spec.Member, indent int) error { for _, member := range members { + if !member.IsInline { + _, err := member.GetPropertyName() + if err != nil { + return err + } + } if !member.IsBodyMember() { continue } diff --git a/tools/goctl/api/spec/fn.go b/tools/goctl/api/spec/fn.go index 2fbbc2dc..8477b59f 100644 --- a/tools/goctl/api/spec/fn.go +++ b/tools/goctl/api/spec/fn.go @@ -63,10 +63,6 @@ func (m Member) IsOmitempty() bool { func (m Member) GetPropertyName() (string, error) { tags := m.Tags() - if len(tags) == 0 { - return "", errors.New("json property name not exist, member: " + m.Name) - } - for _, tag := range tags { if stringx.Contains(definedKeys, tag.Key) { if tag.Name == "-" { diff --git a/tools/goctl/api/tsgen/genpacket.go b/tools/goctl/api/tsgen/genpacket.go index 7b1e7d01..3dc189ee 100644 --- a/tools/goctl/api/tsgen/genpacket.go +++ b/tools/goctl/api/tsgen/genpacket.go @@ -85,7 +85,7 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e imports += fmt.Sprintf(`import * as components from "%s"`, "./"+outputFile) } - apis, err := genApi(api, localTypes, caller, prefixForType) + apis, err := genApi(api, caller, prefixForType) if err != nil { return err } @@ -119,32 +119,34 @@ func genTypes(localTypes []spec.Type, inlineType func(string) (*spec.Type, error return types, nil } -func genApi(api *spec.ApiSpec, localTypes []spec.Type, caller string, prefixForType func(string) string) (string, error) { +func genApi(api *spec.ApiSpec, caller string, prefixForType func(string) string) (string, error) { var builder strings.Builder - for _, route := range api.Service.Routes() { - handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler") - if !ok { - return "", fmt.Errorf("missing handler annotation for route %q", route.Path) - } - handler = util.Untitle(handler) - handler = strings.Replace(handler, "Handler", "", 1) - comment := commentForRoute(route) - if len(comment) > 0 { - fmt.Fprintf(&builder, "%s\n", comment) - } - fmt.Fprintf(&builder, "export function %s(%s) {\n", handler, paramsForRoute(route, prefixForType)) - writeIndent(&builder, 1) - responseGeneric := "" - if len(route.ResponseType.Name) > 0 { - val, err := goTypeToTs(route.ResponseType.Name, prefixForType) - if err != nil { - return "", err + for _, group := range api.Service.Groups { + for _, route := range group.Routes { + handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler") + if !ok { + return "", fmt.Errorf("missing handler annotation for route %q", route.Path) } - responseGeneric = fmt.Sprintf("<%s>", val) + handler = util.Untitle(handler) + handler = strings.Replace(handler, "Handler", "", 1) + comment := commentForRoute(route) + if len(comment) > 0 { + fmt.Fprintf(&builder, "%s\n", comment) + } + fmt.Fprintf(&builder, "export function %s(%s) {\n", handler, paramsForRoute(route, prefixForType)) + writeIndent(&builder, 1) + responseGeneric := "" + if len(route.ResponseType.Name) > 0 { + val, err := goTypeToTs(route.ResponseType.Name, prefixForType) + if err != nil { + return "", err + } + responseGeneric = fmt.Sprintf("<%s>", val) + } + fmt.Fprintf(&builder, `return %s.%s%s(%s)`, caller, strings.ToLower(route.Method), + util.Title(responseGeneric), callParamsForRoute(route, group)) + builder.WriteString("\n}\n\n") } - fmt.Fprintf(&builder, `return %s.%s%s(%s)`, caller, strings.ToLower(route.Method), - util.Title(responseGeneric), callParamsForRoute(route)) - builder.WriteString("\n}\n\n") } apis := builder.String() @@ -188,21 +190,28 @@ func commentForRoute(route spec.Route) string { return builder.String() } -func callParamsForRoute(route spec.Route) string { +func callParamsForRoute(route spec.Route, group spec.Group) string { hasParams := pathHasParams(route) hasBody := hasRequestBody(route) if hasParams && hasBody { - return fmt.Sprintf("%s, %s, %s", pathForRoute(route), "params", "req") + return fmt.Sprintf("%s, %s, %s", pathForRoute(route, group), "params", "req") } else if hasParams { - return fmt.Sprintf("%s, %s", pathForRoute(route), "params") + return fmt.Sprintf("%s, %s", pathForRoute(route, group), "params") } else if hasBody { - return fmt.Sprintf("%s, %s", pathForRoute(route), "req") + return fmt.Sprintf("%s, %s", pathForRoute(route, group), "req") } - return pathForRoute(route) + return pathForRoute(route, group) } -func pathForRoute(route spec.Route) string { - return "\"" + route.Path + "\"" +func pathForRoute(route spec.Route, group spec.Group) string { + value, ok := apiutil.GetAnnotationValue(group.Annotations, "server", pathPrefix) + if !ok { + return "\"" + route.Path + "\"" + } else { + value = strings.TrimPrefix(value, `"`) + value = strings.TrimSuffix(value, `"`) + return fmt.Sprintf(`"%s/%s"`, value, strings.TrimPrefix(route.Path, "/")) + } } func pathHasParams(route spec.Route) bool { diff --git a/tools/goctl/api/tsgen/vars.go b/tools/goctl/api/tsgen/vars.go index 0c027bfe..309301f1 100644 --- a/tools/goctl/api/tsgen/vars.go +++ b/tools/goctl/api/tsgen/vars.go @@ -2,4 +2,5 @@ package tsgen const ( packagePrefix = "components." + pathPrefix = "pathPrefix" )