fix(goctl)/new parser (#3834)

Co-authored-by: keson <keson@kesondeMacBook-Pro.local>
This commit is contained in:
kesonan
2024-01-11 23:50:53 +08:00
committed by GitHub
parent ffd2a78623
commit 7ba8adfc74
8 changed files with 171 additions and 5 deletions

View File

@@ -192,7 +192,10 @@ func Test_genPublicModel(t *testing.T) {
code, err := g.genModelCustom(*tables[0], false)
assert.NoError(t, err)
assert.True(t, strings.Contains(code, "package model"))
assert.True(t, strings.Contains(code, "TestUserModel interface {\n\t\ttestUserModel\n\t}\n"))
assert.True(t, strings.Contains(code, ` TestUserModel interface {
testUserModel
withSession(session sqlx.Session) TestUserModel
}`))
assert.True(t, strings.Contains(code, "customTestUserModel struct {\n\t\t*defaultTestUserModel\n\t}\n"))
assert.True(t, strings.Contains(code, "func NewTestUserModel(conn sqlx.SqlConn) TestUserModel {"))
}

View File

@@ -12,6 +12,11 @@ import (
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
)
const (
atServerGroupKey = "group:"
atServerPrefixKey = "prefix:"
)
// API is the parsed api file.
type API struct {
Filename string
@@ -127,10 +132,13 @@ func (api *API) checkServiceStmt() error {
} else {
serviceName[name] = name
}
var group = api.getAtServerValue(v.AtServerStmt, "prefix")
var (
prefix = api.getAtServerValue(v.AtServerStmt, atServerPrefixKey)
group = api.getAtServerValue(v.AtServerStmt, atServerGroupKey)
)
for _, item := range v.Routes {
handlerChecker.check(item.AtHandler.Name)
path := fmt.Sprintf("[%s]:%s", group, item.Route.Format(""))
handlerChecker.checkNodeWithPrefix(group, item.AtHandler.Name)
path := fmt.Sprintf("[%s]:%s", prefix, item.Route.Format(""))
pathChecker.check(ast.NewTokenNode(token.Token{
Text: path,
Position: item.Route.Pos(),

View File

@@ -1,6 +1,8 @@
package parser
import (
"fmt"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/ast"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/placeholder"
)
@@ -21,6 +23,17 @@ func (b *filterBuilder) check(nodes ...*ast.TokenNode) {
}
}
func (b *filterBuilder) checkNodeWithPrefix(prefix string, nodes ...*ast.TokenNode) {
for _, node := range nodes {
joinText:=fmt.Sprintf("%s/%s",prefix,node.Token.Text)
if _, ok := b.m[joinText]; ok {
b.errorManager.add(ast.DuplicateStmtError(node.Pos(), "duplicate "+b.checkExprName))
} else {
b.m[joinText] = placeholder.PlaceHolder
}
}
}
func (b *filterBuilder) error() error {
return b.errorManager.error()
}

View File

@@ -385,6 +385,9 @@ func (p *Parser) parsePathExpr() *ast.PathExpr {
}
values = append(values, p.curTok)
if p.peekTokenIs(token.LPAREN, token.Returns, token.AT_DOC, token.AT_HANDLER, token.SEMICOLON, token.RBRACE){
break
}
if p.notExpectPeekTokenGotComment(p.curTokenNode().PeekFirstLeadingComment(), token.COLON, token.IDENT, token.INT) {
return nil
}

View File

@@ -521,6 +521,19 @@ func TestParser_Parse_service(t *testing.T) {
LBrace: ast.NewTokenNode(token.Token{Type: token.LBRACE, Text: "{"}),
RBrace: ast.NewTokenNode(token.Token{Type: token.RBRACE, Text: "}"}),
Routes: []*ast.ServiceItemStmt{
{
AtHandler: &ast.AtHandlerStmt{
AtHandler: ast.NewTokenNode(token.Token{Type: token.AT_HANDLER, Text: "@handler"}),
Name: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "root"}),
},
Route: &ast.RouteStmt{
Method: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "get"}),
Path: &ast.PathExpr{Value: ast.NewTokenNode(token.Token{
Type: token.PATH,
Text: "/",
})},
},
},
{
AtHandler: &ast.AtHandlerStmt{
AtHandler: ast.NewTokenNode(token.Token{Type: token.AT_HANDLER, Text: "@handler"}),
@@ -557,6 +570,93 @@ func TestParser_Parse_service(t *testing.T) {
LBrace: ast.NewTokenNode(token.Token{Type: token.LBRACE, Text: "{"}),
RBrace: ast.NewTokenNode(token.Token{Type: token.RBRACE, Text: "}"}),
Routes: []*ast.ServiceItemStmt{
{
AtDoc: &ast.AtDocLiteralStmt{
AtDoc: ast.NewTokenNode(token.Token{Type: token.AT_DOC, Text: "@doc"}),
Value: ast.NewTokenNode(token.Token{Type: token.STRING, Text: `"bar"`}),
},
AtHandler: &ast.AtHandlerStmt{
AtHandler: ast.NewTokenNode(token.Token{Type: token.AT_HANDLER, Text: "@handler"}),
Name: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "root"}),
},
Route: &ast.RouteStmt{
Method: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "get"}),
Path: &ast.PathExpr{
Value: ast.NewTokenNode(token.Token{
Type: token.PATH,
Text: "/",
}),
},
Request: &ast.BodyStmt{
LParen: ast.NewTokenNode(token.Token{Type: token.LPAREN, Text: "("}),
Body: &ast.BodyExpr{
Value: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "Foo"}),
},
RParen: ast.NewTokenNode(token.Token{Type: token.RPAREN, Text: ")"}),
},
},
},
{
AtDoc: &ast.AtDocLiteralStmt{
AtDoc: ast.NewTokenNode(token.Token{Type: token.AT_DOC, Text: "@doc"}),
Value: ast.NewTokenNode(token.Token{Type: token.STRING, Text: `"bar"`}),
},
AtHandler: &ast.AtHandlerStmt{
AtHandler: ast.NewTokenNode(token.Token{Type: token.AT_HANDLER, Text: "@handler"}),
Name: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "root2"}),
},
Route: &ast.RouteStmt{
Method: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "get"}),
Path: &ast.PathExpr{
Value: ast.NewTokenNode(token.Token{
Type: token.PATH,
Text: "/",
}),
},
Returns: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "returns"}),
Response: &ast.BodyStmt{
LParen: ast.NewTokenNode(token.Token{Type: token.LPAREN, Text: "("}),
Body: &ast.BodyExpr{
Value: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "Foo"}),
},
RParen: ast.NewTokenNode(token.Token{Type: token.RPAREN, Text: ")"}),
},
},
},
{
AtDoc: &ast.AtDocLiteralStmt{
AtDoc: ast.NewTokenNode(token.Token{Type: token.AT_DOC, Text: "@doc"}),
Value: ast.NewTokenNode(token.Token{Type: token.STRING, Text: `"bar"`}),
},
AtHandler: &ast.AtHandlerStmt{
AtHandler: ast.NewTokenNode(token.Token{Type: token.AT_HANDLER, Text: "@handler"}),
Name: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "root3"}),
},
Route: &ast.RouteStmt{
Method: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "get"}),
Path: &ast.PathExpr{
Value: ast.NewTokenNode(token.Token{
Type: token.PATH,
Text: "/",
}),
},
Request: &ast.BodyStmt{
LParen: ast.NewTokenNode(token.Token{Type: token.LPAREN, Text: "("}),
Body: &ast.BodyExpr{
Value: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "Foo"}),
},
RParen: ast.NewTokenNode(token.Token{Type: token.RPAREN, Text: ")"}),
},
Returns: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "returns"}),
Response: &ast.BodyStmt{
LParen: ast.NewTokenNode(token.Token{Type: token.LPAREN, Text: "("}),
Body: &ast.BodyExpr{
Value: ast.NewTokenNode(token.Token{Type: token.IDENT, Text: "Bar"}),
},
RParen: ast.NewTokenNode(token.Token{Type: token.RPAREN, Text: ")"}),
},
},
},
{
AtDoc: &ast.AtDocLiteralStmt{
AtDoc: ast.NewTokenNode(token.Token{Type: token.AT_DOC, Text: "@doc"}),

View File

@@ -93,6 +93,12 @@ type (
NestDemoResp2 {
*Nest `json:"nest"`
}
RootReq{
}
RootResp{
}
)
@server (
@@ -124,6 +130,23 @@ service example {
prefix: /v1/v2
timeout: 100ms
)
service example {
@doc (
desc: "path demo"
)
@handler postPath
post /example/path (PostPathReq) returns (PostPathResp)
@handler root
post / (RootReq) returns (RootResp)
}
@server (
group: path2
middleware: Path
prefix: /v1/v3
timeout: 100ms
)
service example {
@doc (
desc: "path demo"

View File

@@ -1,4 +1,7 @@
service foo {
@handler root
get /
@handler bar
get /ping
@@ -7,6 +10,18 @@ service foo {
}
service bar {
@doc "bar"
@handler root
get / (Foo)
@doc "bar"
@handler root2
get / returns (Foo)
@doc "bar"
@handler root3
get / (Foo) returns (Bar)
@doc "bar"
@handler foo
get /foo/:bar (Foo)

View File

@@ -10,6 +10,7 @@ import (
"strings"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)
const (
@@ -650,7 +651,7 @@ func NewScanner(filename string, src interface{}) (*Scanner, error) {
}
func readData(filename string, src interface{}) ([]byte, error) {
if strings.HasSuffix(filename, ".api") {
if strings.HasSuffix(filename, ".api") &&pathx.FileExists(filename){
data, err := os.ReadFile(filename)
if err != nil {
return nil, err