fix(goctl)/new parser (#3834)

Co-authored-by: keson <keson@kesondeMacBook-Pro.local>
master
kesonan 11 months ago committed by GitHub
parent ffd2a78623
commit 7ba8adfc74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -192,7 +192,10 @@ func Test_genPublicModel(t *testing.T) {
code, err := g.genModelCustom(*tables[0], false) code, err := g.genModelCustom(*tables[0], false)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, strings.Contains(code, "package model")) 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, "customTestUserModel struct {\n\t\t*defaultTestUserModel\n\t}\n"))
assert.True(t, strings.Contains(code, "func NewTestUserModel(conn sqlx.SqlConn) TestUserModel {")) assert.True(t, strings.Contains(code, "func NewTestUserModel(conn sqlx.SqlConn) TestUserModel {"))
} }

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

@ -1,6 +1,8 @@
package parser package parser
import ( import (
"fmt"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/ast" "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/ast"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/placeholder" "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 { func (b *filterBuilder) error() error {
return b.errorManager.error() return b.errorManager.error()
} }

@ -385,6 +385,9 @@ func (p *Parser) parsePathExpr() *ast.PathExpr {
} }
values = append(values, p.curTok) 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) { if p.notExpectPeekTokenGotComment(p.curTokenNode().PeekFirstLeadingComment(), token.COLON, token.IDENT, token.INT) {
return nil return nil
} }

@ -521,6 +521,19 @@ func TestParser_Parse_service(t *testing.T) {
LBrace: ast.NewTokenNode(token.Token{Type: token.LBRACE, Text: "{"}), LBrace: ast.NewTokenNode(token.Token{Type: token.LBRACE, Text: "{"}),
RBrace: ast.NewTokenNode(token.Token{Type: token.RBRACE, Text: "}"}), RBrace: ast.NewTokenNode(token.Token{Type: token.RBRACE, Text: "}"}),
Routes: []*ast.ServiceItemStmt{ 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.AtHandlerStmt{
AtHandler: ast.NewTokenNode(token.Token{Type: token.AT_HANDLER, Text: "@handler"}), 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: "{"}), LBrace: ast.NewTokenNode(token.Token{Type: token.LBRACE, Text: "{"}),
RBrace: ast.NewTokenNode(token.Token{Type: token.RBRACE, Text: "}"}), RBrace: ast.NewTokenNode(token.Token{Type: token.RBRACE, Text: "}"}),
Routes: []*ast.ServiceItemStmt{ 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.AtDocLiteralStmt{
AtDoc: ast.NewTokenNode(token.Token{Type: token.AT_DOC, Text: "@doc"}), AtDoc: ast.NewTokenNode(token.Token{Type: token.AT_DOC, Text: "@doc"}),

@ -93,6 +93,12 @@ type (
NestDemoResp2 { NestDemoResp2 {
*Nest `json:"nest"` *Nest `json:"nest"`
} }
RootReq{
}
RootResp{
}
) )
@server ( @server (
@ -124,6 +130,23 @@ service example {
prefix: /v1/v2 prefix: /v1/v2
timeout: 100ms 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 { service example {
@doc ( @doc (
desc: "path demo" desc: "path demo"

@ -1,4 +1,7 @@
service foo { service foo {
@handler root
get /
@handler bar @handler bar
get /ping get /ping
@ -7,6 +10,18 @@ service foo {
} }
service bar { 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" @doc "bar"
@handler foo @handler foo
get /foo/:bar (Foo) get /foo/:bar (Foo)

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

Loading…
Cancel
Save