Browse Source

goctl support import api file (#94)

* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

Co-authored-by: kingxt <dream4kingxt@163.com>
kingxt 4 years ago
parent
commit
b628bc0086
3 changed files with 105 additions and 44 deletions
  1. 18 6
      tools/goctl/api/format/format.go
  2. 33 8
      tools/goctl/api/parser/parser.go
  3. 54 30
      tools/goctl/api/parser/util.go

+ 18 - 6
tools/goctl/api/format/format.go

@@ -65,16 +65,16 @@ func ApiFormat(path string, printToConsole bool) error {
 		return m
 	})
 
-	info, st, service, err := parser.MatchStruct(r)
+	apiStruct, err := parser.MatchStruct(r)
 	if err != nil {
 		return err
 	}
-	info = strings.TrimSpace(info)
-	if len(service) == 0 || len(st) == 0 {
+	info := strings.TrimSpace(apiStruct.Info)
+	if len(apiStruct.Service) == 0 {
 		return nil
 	}
 
-	fs, err := format.Source([]byte(strings.TrimSpace(st)))
+	fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.StructBody)))
 	if err != nil {
 		str := err.Error()
 		lineNumber := strings.Index(str, ":")
@@ -93,12 +93,24 @@ func ApiFormat(path string, printToConsole bool) error {
 		return err
 	}
 
-	result := strings.Join([]string{info, string(fs), service}, "\n\n")
+	var result string
+	if len(strings.TrimSpace(info)) > 0 {
+		result += strings.TrimSpace(info) + "\n\n"
+	}
+	if len(strings.TrimSpace(apiStruct.Imports)) > 0 {
+		result += strings.TrimSpace(apiStruct.Imports) + "\n\n"
+	}
+	if len(strings.TrimSpace(string(fs))) > 0 {
+		result += strings.TrimSpace(string(fs)) + "\n\n"
+	}
+	if len(strings.TrimSpace(apiStruct.Service)) > 0 {
+		result += strings.TrimSpace(apiStruct.Service) + "\n\n"
+	}
+
 	if printToConsole {
 		_, err := fmt.Print(result)
 		return err
 	}
-	result = strings.TrimSpace(result)
 	return ioutil.WriteFile(path, []byte(result), os.ModePerm)
 }
 

+ 33 - 8
tools/goctl/api/parser/parser.go

@@ -6,36 +6,61 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"path/filepath"
+	"strings"
 
 	"github.com/tal-tech/go-zero/tools/goctl/api/spec"
+	"github.com/tal-tech/go-zero/tools/goctl/util"
 )
 
 type Parser struct {
-	r  *bufio.Reader
-	st string
+	r       *bufio.Reader
+	typeDef string
 }
 
 func NewParser(filename string) (*Parser, error) {
+	apiAbsPath, err := filepath.Abs(filename)
+	if err != nil {
+		return nil, err
+	}
+
 	api, err := ioutil.ReadFile(filename)
 	if err != nil {
 		return nil, err
 	}
-	info, body, service, err := MatchStruct(string(api))
+
+	apiStruct, err := MatchStruct(string(api))
 	if err != nil {
 		return nil, err
 	}
+	for _, item := range strings.Split(apiStruct.Imports, "\n") {
+		ip := strings.TrimSpace(item)
+		if len(ip) > 0 {
+			item := strings.TrimPrefix(item, "import")
+			item = strings.TrimSpace(item)
+			var path = item
+			if !util.FileExists(item) {
+				path = filepath.Join(filepath.Dir(apiAbsPath), item)
+			}
+			content, err := ioutil.ReadFile(path)
+			if err != nil {
+				return nil, err
+			}
+			apiStruct.StructBody += "\n" + string(content)
+		}
+	}
+
 	var buffer = new(bytes.Buffer)
-	buffer.WriteString(info)
-	buffer.WriteString(service)
+	buffer.WriteString(apiStruct.Service)
 	return &Parser{
-		r:  bufio.NewReader(buffer),
-		st: body,
+		r:       bufio.NewReader(buffer),
+		typeDef: apiStruct.StructBody,
 	}, nil
 }
 
 func (p *Parser) Parse() (api *spec.ApiSpec, err error) {
 	api = new(spec.ApiSpec)
-	types, err := parseStructAst(p.st)
+	types, err := parseStructAst(p.typeDef)
 	if err != nil {
 		return nil, err
 	}

+ 54 - 30
tools/goctl/api/parser/util.go

@@ -3,19 +3,19 @@ package parser
 import (
 	"bufio"
 	"errors"
-	"regexp"
 	"strings"
 
 	"github.com/tal-tech/go-zero/tools/goctl/api/spec"
 )
 
-// struct match
-const typeRegex = `(?m)(?m)(^ *type\s+[a-zA-Z][a-zA-Z0-9_-]+\s+(((struct)\s*?\{[\w\W]*?[^\{]\})|([a-zA-Z][a-zA-Z0-9_-]+)))|(^ *type\s*?\([\w\W]+\}\s*\))`
+var emptyType spec.Type
 
-var (
-	emptyStrcut = errors.New("struct body not found")
-	emptyType   spec.Type
-)
+type ApiStruct struct {
+	Info       string
+	StructBody string
+	Service    string
+	Imports    string
+}
 
 func GetType(api *spec.ApiSpec, t string) spec.Type {
 	for _, tp := range api.Types {
@@ -69,32 +69,56 @@ func unread(r *bufio.Reader) error {
 	return r.UnreadRune()
 }
 
-func MatchStruct(api string) (info, structBody, service string, err error) {
-	r := regexp.MustCompile(typeRegex)
-	indexes := r.FindAllStringIndex(api, -1)
-	if len(indexes) == 0 {
-		return "", "", "", emptyStrcut
-	}
-	startIndexes := indexes[0]
-	endIndexes := indexes[len(indexes)-1]
+func MatchStruct(api string) (*ApiStruct, error) {
+	var result ApiStruct
+	scanner := bufio.NewScanner(strings.NewReader(api))
+	var parseInfo = false
+	var parseImport = false
+	var parseType = false
+	var parseSevice = false
+	var segment string
+	for scanner.Scan() {
+		line := strings.TrimSpace(scanner.Text())
+
+		if line == "@doc(" {
+			parseInfo = true
+		}
+		if line == ")" && parseInfo {
+			parseInfo = false
+			result.Info = segment + ")"
+			segment = ""
+			continue
+		}
 
-	info = api[:startIndexes[0]]
-	structBody = api[startIndexes[0]:endIndexes[len(endIndexes)-1]]
-	service = api[endIndexes[len(endIndexes)-1]:]
+		if strings.HasPrefix(line, "import") {
+			parseImport = true
+		}
+		if parseImport && (strings.HasPrefix(line, "type") || strings.HasPrefix(line, "@server") ||
+			strings.HasPrefix(line, "service")) {
+			parseImport = false
+			result.Imports = segment
+			segment = line + "\n"
+			continue
+		}
 
-	firstIIndex := strings.Index(info, "i")
-	if firstIIndex > 0 {
-		info = info[firstIIndex:]
+		if strings.HasPrefix(line, "type") {
+			parseType = true
+		}
+		if strings.HasPrefix(line, "@server") || strings.HasPrefix(line, "service") {
+			if parseType {
+				parseType = false
+				result.StructBody = segment
+				segment = line + "\n"
+				continue
+			}
+			parseSevice = true
+		}
+		segment += scanner.Text() + "\n"
 	}
 
-	lastServiceRightBraceIndex := strings.LastIndex(service, "}") + 1
-	var firstServiceIndex int
-	for index, char := range service {
-		if !isSpace(char) && !isNewline(char) {
-			firstServiceIndex = index
-			break
-		}
+	if !parseSevice {
+		return nil, errors.New("no service defined")
 	}
-	service = service[firstServiceIndex:lastServiceRightBraceIndex]
-	return
+	result.Service = segment
+	return &result, nil
 }