fix(goctl)/new parser (#3834)
Co-authored-by: keson <keson@kesondeMacBook-Pro.local>
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user