reactor rpc (#179)

* reactor rpc generation

* update flag

* update command

* update command

* update unit test

* delete test file

* optimize code

* update doc

* update gen pb

* rename target dir

* update mysql data type convert rule

* add done flag

* optimize req/reply parameter

* optimize req/reply parameter

* remove waste code

* remove duplicate parameter

* format code

* format code

* optimize naming

* reactor rpcv2 to rpc

* remove new line

* format code

* rename underline to snake

* reactor getParentPackage

* remove debug log

* reactor background
This commit is contained in:
Keson
2020-11-05 14:12:47 +08:00
committed by GitHub
parent c9ec22d5f4
commit e76f44a35b
95 changed files with 2708 additions and 3301 deletions

View File

@@ -0,0 +1,75 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: common.proto
package common
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type User struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *User) Reset() { *m = User{} }
func (m *User) String() string { return proto.CompactTextString(m) }
func (*User) ProtoMessage() {}
func (*User) Descriptor() ([]byte, []int) {
return fileDescriptor_555bd8c177793206, []int{0}
}
func (m *User) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_User.Unmarshal(m, b)
}
func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_User.Marshal(b, m, deterministic)
}
func (m *User) XXX_Merge(src proto.Message) {
xxx_messageInfo_User.Merge(m, src)
}
func (m *User) XXX_Size() int {
return xxx_messageInfo_User.Size(m)
}
func (m *User) XXX_DiscardUnknown() {
xxx_messageInfo_User.DiscardUnknown(m)
}
var xxx_messageInfo_User proto.InternalMessageInfo
func (m *User) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func init() {
proto.RegisterType((*User)(nil), "common.User")
}
func init() { proto.RegisterFile("common.proto", fileDescriptor_555bd8c177793206) }
var fileDescriptor_555bd8c177793206 = []byte{
// 72 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0xce, 0xcf, 0xcd,
0xcd, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0x94, 0xa4, 0xb8, 0x58,
0x42, 0x8b, 0x53, 0x8b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18, 0x15, 0x18,
0x35, 0x38, 0x83, 0xc0, 0xec, 0x24, 0x36, 0xb0, 0x52, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff,
0x2c, 0x6d, 0x58, 0x59, 0x3a, 0x00, 0x00, 0x00,
}

View File

@@ -0,0 +1,7 @@
syntax = "proto3";
package common;
message User {
string name = 1;
}

View File

@@ -0,0 +1,33 @@
package generator
import (
"os/exec"
"github.com/tal-tech/go-zero/tools/goctl/util/console"
)
type defaultGenerator struct {
log console.Console
}
func NewDefaultGenerator() *defaultGenerator {
log := console.NewColorConsole()
return &defaultGenerator{
log: log,
}
}
func (g *defaultGenerator) Prepare() error {
_, err := exec.LookPath("go")
if err != nil {
return err
}
_, err = exec.LookPath("protoc")
if err != nil {
return err
}
_, err = exec.LookPath("protoc-gen-go")
return err
}

View File

@@ -0,0 +1,11 @@
package generator
import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func formatFilename(filename string) string {
return strings.ToLower(stringx.From(filename).ToCamel())
}

View File

@@ -0,0 +1,98 @@
package generator
import (
"path/filepath"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/console"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
type RpcGenerator struct {
g Generator
}
func NewDefaultRpcGenerator() *RpcGenerator {
return NewRpcGenerator(NewDefaultGenerator())
}
func NewRpcGenerator(g Generator) *RpcGenerator {
return &RpcGenerator{
g: g,
}
}
func (g *RpcGenerator) Generate(src, target string, protoImportPath []string) error {
abs, err := filepath.Abs(target)
if err != nil {
return err
}
err = util.MkdirIfNotExist(abs)
if err != nil {
return err
}
err = g.g.Prepare()
if err != nil {
return err
}
projectCtx, err := ctx.Prepare(abs)
if err != nil {
return err
}
p := parser.NewDefaultProtoParser()
proto, err := p.Parse(src)
if err != nil {
return err
}
dirCtx, err := mkdir(projectCtx, proto)
if err != nil {
return err
}
err = g.g.GenEtc(dirCtx, proto)
if err != nil {
return err
}
err = g.g.GenPb(dirCtx, protoImportPath, proto)
if err != nil {
return err
}
err = g.g.GenConfig(dirCtx, proto)
if err != nil {
return err
}
err = g.g.GenSvc(dirCtx, proto)
if err != nil {
return err
}
err = g.g.GenLogic(dirCtx, proto)
if err != nil {
return err
}
err = g.g.GenServer(dirCtx, proto)
if err != nil {
return err
}
err = g.g.GenMain(dirCtx, proto)
if err != nil {
return err
}
err = g.g.GenCall(dirCtx, proto)
console.NewColorConsole().MarkDone()
return err
}

View File

@@ -0,0 +1,104 @@
package generator
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
)
func TestRpcGenerateCaseNilImport(t *testing.T) {
dispatcher := NewDefaultGenerator()
if err := dispatcher.Prepare(); err == nil {
g := NewRpcGenerator(dispatcher)
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
err = g.Generate("./test_stream.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
}
}
func TestRpcGenerateCaseOption(t *testing.T) {
dispatcher := NewDefaultGenerator()
if err := dispatcher.Prepare(); err == nil {
g := NewRpcGenerator(dispatcher)
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
err = g.Generate("./test_option.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
}
}
func TestRpcGenerateCaseWordOption(t *testing.T) {
dispatcher := NewDefaultGenerator()
if err := dispatcher.Prepare(); err == nil {
g := NewRpcGenerator(dispatcher)
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
err = g.Generate("./test_word_option.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
}
}
// test keyword go
func TestRpcGenerateCaseGoOption(t *testing.T) {
dispatcher := NewDefaultGenerator()
if err := dispatcher.Prepare(); err == nil {
g := NewRpcGenerator(dispatcher)
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
err = g.Generate("./test_go_option.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
}
}
func TestRpcGenerateCaseImport(t *testing.T) {
dispatcher := NewDefaultGenerator()
if err := dispatcher.Prepare(); err == nil {
g := NewRpcGenerator(dispatcher)
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
err = g.Generate("./test_import.proto", abs, []string{"./base"})
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.True(t, func() bool {
return strings.Contains(err.Error(), "package base is not in GOROOT")
}())
}
}

View File

@@ -0,0 +1,154 @@
package generator
import (
"fmt"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
const (
callTemplateText = `{{.head}}
//go:generate mockgen -destination ./{{.name}}_mock.go -package {{.filePackage}} -source $GOFILE
package {{.filePackage}}
import (
"context"
{{.package}}
"github.com/tal-tech/go-zero/zrpc"
)
type (
{{.alias}}
{{.serviceName}} interface {
{{.interface}}
}
default{{.serviceName}} struct {
cli zrpc.Client
}
)
func New{{.serviceName}}(cli zrpc.Client) {{.serviceName}} {
return &default{{.serviceName}}{
cli: cli,
}
}
{{.functions}}
`
callInterfaceFunctionTemplate = `{{if .hasComment}}{{.comment}}
{{end}}{{.method}}(ctx context.Context,in *{{.pbRequest}}) (*{{.pbResponse}},error)`
callFunctionTemplate = `
{{if .hasComment}}{{.comment}}{{end}}
func (m *default{{.rpcServiceName}}) {{.method}}(ctx context.Context,in *{{.pbRequest}}) (*{{.pbResponse}}, error) {
client := {{.package}}.New{{.rpcServiceName}}Client(m.cli.Conn())
return client.{{.method}}(ctx, in)
}
`
)
func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto) error {
dir := ctx.GetCall()
service := proto.Service
head := util.GetHead(proto.Name)
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", formatFilename(service.Name)))
functions, err := g.genFunction(proto.PbPackage, service)
if err != nil {
return err
}
iFunctions, err := g.getInterfaceFuncs(service)
if err != nil {
return err
}
text, err := util.LoadTemplate(category, callTemplateFile, callTemplateText)
if err != nil {
return err
}
var alias []string
for _, item := range service.RPC {
alias = append(alias, fmt.Sprintf("%s = %s", parser.CamelCase(item.RequestType), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(item.RequestType))))
alias = append(alias, fmt.Sprintf("%s = %s", parser.CamelCase(item.ReturnsType), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(item.ReturnsType))))
}
err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"name": formatFilename(service.Name),
"alias": strings.Join(alias, util.NL),
"head": head,
"filePackage": formatFilename(service.Name),
"package": fmt.Sprintf(`"%s"`, ctx.GetPb().Package),
"serviceName": parser.CamelCase(service.Name),
"functions": strings.Join(functions, util.NL),
"interface": strings.Join(iFunctions, util.NL),
}, filename, true)
return err
}
func (g *defaultGenerator) genFunction(goPackage string, service parser.Service) ([]string, error) {
functions := make([]string, 0)
for _, rpc := range service.RPC {
text, err := util.LoadTemplate(category, callFunctionTemplateFile, callFunctionTemplate)
if err != nil {
return nil, err
}
comment := parser.GetComment(rpc.Doc())
buffer, err := util.With("sharedFn").Parse(text).Execute(map[string]interface{}{
"rpcServiceName": stringx.From(service.Name).Title(),
"method": stringx.From(rpc.Name).Title(),
"package": goPackage,
"pbRequest": parser.CamelCase(rpc.RequestType),
"pbResponse": parser.CamelCase(rpc.ReturnsType),
"hasComment": len(comment) > 0,
"comment": comment,
})
if err != nil {
return nil, err
}
functions = append(functions, buffer.String())
}
return functions, nil
}
func (g *defaultGenerator) getInterfaceFuncs(service parser.Service) ([]string, error) {
functions := make([]string, 0)
for _, rpc := range service.RPC {
text, err := util.LoadTemplate(category, callInterfaceFunctionTemplateFile, callInterfaceFunctionTemplate)
if err != nil {
return nil, err
}
comment := parser.GetComment(rpc.Doc())
buffer, err := util.With("interfaceFn").Parse(text).Execute(
map[string]interface{}{
"hasComment": len(comment) > 0,
"comment": comment,
"method": stringx.From(rpc.Name).Title(),
"pbRequest": parser.CamelCase(rpc.RequestType),
"pbResponse": parser.CamelCase(rpc.ReturnsType),
})
if err != nil {
return nil, err
}
functions = append(functions, buffer.String())
}
return functions, nil
}

View File

@@ -0,0 +1,44 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateCall(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenCall(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,34 @@
package generator
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
const configTemplate = `package config
import "github.com/tal-tech/go-zero/zrpc"
type Config struct {
zrpc.RpcServerConf
}
`
func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto) error {
dir := ctx.GetConfig()
fileName := filepath.Join(dir.Filename, formatFilename("config")+".go")
if util.FileExists(fileName) {
return nil
}
text, err := util.LoadTemplate(category, configTemplateFileFile, configTemplate)
if err != nil {
return err
}
return ioutil.WriteFile(fileName, []byte(text), os.ModePerm)
}

View File

@@ -0,0 +1,48 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateConfig(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenConfig(dirCtx, proto)
assert.Nil(t, err)
// test file exists
err = g.GenConfig(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,15 @@
package generator
import "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
type Generator interface {
Prepare() error
GenMain(ctx DirContext, proto parser.Proto) error
GenCall(ctx DirContext, proto parser.Proto) error
GenEtc(ctx DirContext, proto parser.Proto) error
GenConfig(ctx DirContext, proto parser.Proto) error
GenLogic(ctx DirContext, proto parser.Proto) error
GenServer(ctx DirContext, proto parser.Proto) error
GenSvc(ctx DirContext, proto parser.Proto) error
GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto) error
}

View File

@@ -0,0 +1,32 @@
package generator
import (
"fmt"
"path/filepath"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
const etcTemplate = `Name: {{.serviceName}}.rpc
ListenOn: 127.0.0.1:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: {{.serviceName}}.rpc
`
func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto) error {
dir := ctx.GetEtc()
serviceNameLower := formatFilename(ctx.GetMain().Base)
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.yaml", serviceNameLower))
text, err := util.LoadTemplate(category, etcTemplateFileFile, etcTemplate)
if err != nil {
return err
}
return util.With("etc").Parse(text).SaveTo(map[string]interface{}{
"serviceName": serviceNameLower,
}, fileName, false)
}

View File

@@ -0,0 +1,45 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateEtc(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenEtc(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,101 @@
package generator
import (
"fmt"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
const (
logicTemplate = `package logic
import (
"context"
{{.imports}}
"github.com/tal-tech/go-zero/core/logx"
)
type {{.logicName}} struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func New{{.logicName}}(ctx context.Context,svcCtx *svc.ServiceContext) *{{.logicName}} {
return &{{.logicName}}{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
{{.functions}}
`
logicFunctionTemplate = `{{if .hasComment}}{{.comment}}{{end}}
func (l *{{.logicName}}) {{.method}} (in {{.request}}) ({{.response}}, error) {
// todo: add your logic here and delete this line
return &{{.responseType}}{}, nil
}
`
)
func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto) error {
dir := ctx.GetLogic()
for _, rpc := range proto.Service.RPC {
filename := filepath.Join(dir.Filename, formatFilename(rpc.Name+"_logic")+".go")
functions, err := g.genLogicFunction(proto.PbPackage, rpc)
if err != nil {
return err
}
imports := collection.NewSet()
imports.AddStr(fmt.Sprintf(`"%v"`, ctx.GetSvc().Package))
imports.AddStr(fmt.Sprintf(`"%v"`, ctx.GetPb().Package))
text, err := util.LoadTemplate(category, logicTemplateFileFile, logicTemplate)
if err != nil {
return err
}
err = util.With("logic").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"logicName": fmt.Sprintf("%sLogic", stringx.From(rpc.Name).Title()),
"functions": functions,
"imports": strings.Join(imports.KeysStr(), util.NL),
}, filename, false)
if err != nil {
return err
}
}
return nil
}
func (g *defaultGenerator) genLogicFunction(goPackage string, rpc *parser.RPC) (string, error) {
var functions = make([]string, 0)
text, err := util.LoadTemplate(category, logicFuncTemplateFileFile, logicFunctionTemplate)
if err != nil {
return "", err
}
logicName := stringx.From(rpc.Name + "_logic").ToCamel()
comment := parser.GetComment(rpc.Doc())
buffer, err := util.With("fun").Parse(text).Execute(map[string]interface{}{
"logicName": logicName,
"method": parser.CamelCase(rpc.Name),
"request": fmt.Sprintf("*%s.%s", goPackage, parser.CamelCase(rpc.RequestType)),
"response": fmt.Sprintf("*%s.%s", goPackage, parser.CamelCase(rpc.ReturnsType)),
"responseType": fmt.Sprintf("%s.%s", goPackage, parser.CamelCase(rpc.ReturnsType)),
"hasComment": len(comment) > 0,
"comment": comment,
})
if err != nil {
return "", err
}
functions = append(functions, buffer.String())
return strings.Join(functions, util.NL), nil
}

View File

@@ -0,0 +1,44 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateLogic(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenLogic(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,70 @@
package generator
import (
"fmt"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
const mainTemplate = `{{.head}}
package main
import (
"flag"
"fmt"
{{.imports}}
"github.com/tal-tech/go-zero/core/conf"
"github.com/tal-tech/go-zero/zrpc"
"google.golang.org/grpc"
)
var configFile = flag.String("f", "etc/{{.serviceName}}.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
srv := server.New{{.service}}Server(ctx)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
{{.pkg}}.Register{{.service}}Server(grpcServer, srv)
})
defer s.Stop()
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
`
func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto) error {
dir := ctx.GetMain()
serviceNameLower := formatFilename(ctx.GetMain().Base)
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.go", serviceNameLower))
imports := make([]string, 0)
pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
remoteImport := fmt.Sprintf(`"%v"`, ctx.GetServer().Package)
configImport := fmt.Sprintf(`"%v"`, ctx.GetConfig().Package)
imports = append(imports, configImport, pbImport, remoteImport, svcImport)
head := util.GetHead(proto.Name)
text, err := util.LoadTemplate(category, mainTemplateFile, mainTemplate)
if err != nil {
return err
}
return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head,
"serviceName": serviceNameLower,
"imports": strings.Join(imports, util.NL),
"pkg": proto.PbPackage,
"service": parser.CamelCase(proto.Service.Name),
}, fileName, false)
}

View File

@@ -0,0 +1,45 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateMain(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenMain(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,31 @@
package generator
import (
"bytes"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto) error {
dir := ctx.GetPb()
cw := new(bytes.Buffer)
base := filepath.Dir(proto.Src)
cw.WriteString("protoc ")
for _, ip := range protoImportPath {
cw.WriteString(" -I=" + ip)
}
cw.WriteString(" -I=" + base)
cw.WriteString(" " + proto.Name)
if strings.Contains(proto.GoPackage, "/") {
cw.WriteString(" --go_out=plugins=grpc:" + ctx.GetInternal().Filename)
} else {
cw.WriteString(" --go_out=plugins=grpc:" + dir.Filename)
}
command := cw.String()
g.log.Debug(command)
_, err := execx.Run(command, "")
return err
}

View File

@@ -0,0 +1,184 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateCaseNilImport(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
//_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_stream.pb.go")
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
func TestGenerateCaseImport(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_stream.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
func TestGenerateCasePathOption(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_option.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_option.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
func TestGenerateCaseWordOption(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_word_option.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_word_option.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
// test keyword go
func TestGenerateCaseGoOption(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_go_option.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_go_option.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}

View File

@@ -0,0 +1,102 @@
package generator
import (
"fmt"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
const (
serverTemplate = `{{.head}}
package server
import (
"context"
{{.imports}}
)
type {{.server}}Server struct {
svcCtx *svc.ServiceContext
}
func New{{.server}}Server(svcCtx *svc.ServiceContext) *{{.server}}Server {
return &{{.server}}Server{
svcCtx: svcCtx,
}
}
{{.funcs}}
`
functionTemplate = `
{{if .hasComment}}{{.comment}}{{end}}
func (s *{{.server}}Server) {{.method}} (ctx context.Context, in {{.request}}) ({{.response}}, error) {
l := logic.New{{.logicName}}(ctx,s.svcCtx)
return l.{{.method}}(in)
}
`
)
func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto) error {
dir := ctx.GetServer()
logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package)
imports := collection.NewSet()
imports.AddStr(logicImport, svcImport, pbImport)
head := util.GetHead(proto.Name)
service := proto.Service
serverFile := filepath.Join(dir.Filename, formatFilename(service.Name+"_server")+".go")
funcList, err := g.genFunctions(proto.PbPackage, service)
if err != nil {
return err
}
text, err := util.LoadTemplate(category, serverTemplateFile, serverTemplate)
if err != nil {
return err
}
err = util.With("server").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head,
"server": stringx.From(service.Name).Title(),
"imports": strings.Join(imports.KeysStr(), util.NL),
"funcs": strings.Join(funcList, util.NL),
}, serverFile, true)
return err
}
func (g *defaultGenerator) genFunctions(goPackage string, service parser.Service) ([]string, error) {
var functionList []string
for _, rpc := range service.RPC {
text, err := util.LoadTemplate(category, serverFuncTemplateFile, functionTemplate)
if err != nil {
return nil, err
}
comment := parser.GetComment(rpc.Doc())
buffer, err := util.With("func").Parse(text).Execute(map[string]interface{}{
"server": stringx.From(service.Name).Title(),
"logicName": fmt.Sprintf("%sLogic", stringx.From(rpc.Name).Title()),
"method": parser.CamelCase(rpc.Name),
"request": fmt.Sprintf("*%s.%s", goPackage, parser.CamelCase(rpc.RequestType)),
"response": fmt.Sprintf("*%s.%s", goPackage, parser.CamelCase(rpc.ReturnsType)),
"hasComment": len(comment) > 0,
"comment": comment,
})
if err != nil {
return nil, err
}
functionList = append(functionList, buffer.String())
}
return functionList, nil
}

View File

@@ -0,0 +1,45 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateServer(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenServer(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,37 @@
package generator
import (
"fmt"
"path/filepath"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
const svcTemplate = `package svc
import {{.imports}}
type ServiceContext struct {
c config.Config
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
c:c,
}
}
`
func (g *defaultGenerator) GenSvc(ctx DirContext, _ parser.Proto) error {
dir := ctx.GetSvc()
fileName := filepath.Join(dir.Filename, formatFilename("service_context")+".go")
text, err := util.LoadTemplate(category, svcTemplateFile, svcTemplate)
if err != nil {
return err
}
return util.With("svc").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"imports": fmt.Sprintf(`"%v"`, ctx.GetConfig().Package),
}, fileName, false)
}

View File

@@ -0,0 +1,40 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateSvc(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.GenSvc(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,152 @@
package generator
import (
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
const (
wd = "wd"
etc = "etc"
internal = "internal"
config = "config"
logic = "logic"
server = "server"
svc = "svc"
pb = "pb"
call = "call"
)
type (
DirContext interface {
GetCall() Dir
GetEtc() Dir
GetInternal() Dir
GetConfig() Dir
GetLogic() Dir
GetServer() Dir
GetSvc() Dir
GetPb() Dir
GetMain() Dir
}
Dir struct {
Base string
Filename string
Package string
}
defaultDirContext struct {
inner map[string]Dir
}
)
func mkdir(ctx *ctx.ProjectContext, proto parser.Proto) (DirContext, error) {
inner := make(map[string]Dir)
etcDir := filepath.Join(ctx.WorkDir, "etc")
internalDir := filepath.Join(ctx.WorkDir, "internal")
configDir := filepath.Join(internalDir, "config")
logicDir := filepath.Join(internalDir, "logic")
serverDir := filepath.Join(internalDir, "server")
svcDir := filepath.Join(internalDir, "svc")
pbDir := filepath.Join(internalDir, proto.GoPackage)
callDir := filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name).ToCamel()))
inner[wd] = Dir{
Filename: ctx.WorkDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(ctx.WorkDir, ctx.Dir))),
Base: filepath.Base(ctx.WorkDir),
}
inner[etc] = Dir{
Filename: etcDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(etcDir, ctx.Dir))),
Base: filepath.Base(etcDir),
}
inner[internal] = Dir{
Filename: internalDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(internalDir, ctx.Dir))),
Base: filepath.Base(internalDir),
}
inner[config] = Dir{
Filename: configDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(configDir, ctx.Dir))),
Base: filepath.Base(configDir),
}
inner[logic] = Dir{
Filename: logicDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(logicDir, ctx.Dir))),
Base: filepath.Base(logicDir),
}
inner[server] = Dir{
Filename: serverDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(serverDir, ctx.Dir))),
Base: filepath.Base(serverDir),
}
inner[svc] = Dir{
Filename: svcDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(svcDir, ctx.Dir))),
Base: filepath.Base(svcDir),
}
inner[pb] = Dir{
Filename: pbDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(pbDir, ctx.Dir))),
Base: filepath.Base(pbDir),
}
inner[call] = Dir{
Filename: callDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(callDir, ctx.Dir))),
Base: filepath.Base(callDir),
}
for _, v := range inner {
err := util.MkdirIfNotExist(v.Filename)
if err != nil {
return nil, err
}
}
return &defaultDirContext{
inner: inner,
}, nil
}
func (d *defaultDirContext) GetCall() Dir {
return d.inner[call]
}
func (d *defaultDirContext) GetEtc() Dir {
return d.inner[etc]
}
func (d *defaultDirContext) GetInternal() Dir {
return d.inner[internal]
}
func (d *defaultDirContext) GetConfig() Dir {
return d.inner[config]
}
func (d *defaultDirContext) GetLogic() Dir {
return d.inner[logic]
}
func (d *defaultDirContext) GetServer() Dir {
return d.inner[server]
}
func (d *defaultDirContext) GetSvc() Dir {
return d.inner[svc]
}
func (d *defaultDirContext) GetPb() Dir {
return d.inner[pb]
}
func (d *defaultDirContext) GetMain() Dir {
return d.inner[wd]
}
func (d *Dir) Valid() bool {
return len(d.Filename) > 0 && len(d.Package) > 0
}

View File

@@ -0,0 +1,130 @@
package generator
import (
"go/build"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestMkDirInGoPath(t *testing.T) {
dft := build.Default
gp := dft.GOPATH
if len(gp) == 0 {
return
}
projectName := stringx.Rand()
dir := filepath.Join(gp, "src", projectName)
err := util.MkdirIfNotExist(dir)
if err != nil {
return
}
defer func() {
_ = os.RemoveAll(dir)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
internal := filepath.Join(dir, "internal")
assert.True(t, true, func() bool {
return filepath.Join(dir, strings.ToLower(projectName)) == dirCtx.GetCall().Filename && projectName == dirCtx.GetCall().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(dir, "etc") == dirCtx.GetEtc().Filename && filepath.Join(projectName, "etc") == dirCtx.GetEtc().Package
}())
assert.True(t, true, func() bool {
return internal == dirCtx.GetInternal().Filename && filepath.Join(projectName, "internal") == dirCtx.GetInternal().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "config") == dirCtx.GetConfig().Filename && filepath.Join(projectName, "internal", "config") == dirCtx.GetConfig().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "logic") == dirCtx.GetLogic().Filename && filepath.Join(projectName, "internal", "logic") == dirCtx.GetLogic().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "server") == dirCtx.GetServer().Filename && filepath.Join(projectName, "internal", "server") == dirCtx.GetServer().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "svc") == dirCtx.GetSvc().Filename && filepath.Join(projectName, "internal", "svc") == dirCtx.GetSvc().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, strings.ToLower(proto.Service.Name)) == dirCtx.GetPb().Filename && filepath.Join(projectName, "internal", strings.ToLower(proto.Service.Name)) == dirCtx.GetPb().Package
}())
assert.True(t, true, func() bool {
return dir == dirCtx.GetMain().Filename && projectName == dirCtx.GetMain().Package
}())
}
func TestMkDirInGoMod(t *testing.T) {
dft := build.Default
gp := dft.GOPATH
if len(gp) == 0 {
return
}
projectName := stringx.Rand()
dir := filepath.Join(gp, "src", projectName)
err := util.MkdirIfNotExist(dir)
if err != nil {
return
}
_, err = execx.Run("go mod init "+projectName, dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(dir)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
internal := filepath.Join(dir, "internal")
assert.True(t, true, func() bool {
return filepath.Join(dir, strings.ToLower(projectName)) == dirCtx.GetCall().Filename && projectName == dirCtx.GetCall().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(dir, "etc") == dirCtx.GetEtc().Filename && filepath.Join(projectName, "etc") == dirCtx.GetEtc().Package
}())
assert.True(t, true, func() bool {
return internal == dirCtx.GetInternal().Filename && filepath.Join(projectName, "internal") == dirCtx.GetInternal().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "config") == dirCtx.GetConfig().Filename && filepath.Join(projectName, "internal", "config") == dirCtx.GetConfig().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "logic") == dirCtx.GetLogic().Filename && filepath.Join(projectName, "internal", "logic") == dirCtx.GetLogic().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "server") == dirCtx.GetServer().Filename && filepath.Join(projectName, "internal", "server") == dirCtx.GetServer().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, "svc") == dirCtx.GetSvc().Filename && filepath.Join(projectName, "internal", "svc") == dirCtx.GetSvc().Package
}())
assert.True(t, true, func() bool {
return filepath.Join(internal, strings.ToLower(proto.Service.Name)) == dirCtx.GetPb().Filename && filepath.Join(projectName, "internal", strings.ToLower(proto.Service.Name)) == dirCtx.GetPb().Package
}())
assert.True(t, true, func() bool {
return dir == dirCtx.GetMain().Filename && projectName == dirCtx.GetMain().Package
}())
}

View File

@@ -0,0 +1,47 @@
package generator
import (
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
const rpcTemplateText = `syntax = "proto3";
package {{.package}};
message Request {
string ping = 1;
}
message Response {
string pong = 1;
}
service {{.serviceName}} {
rpc Ping(Request) returns(Response);
}
`
func ProtoTmpl(out string) error {
protoFilename := filepath.Base(out)
serviceName := stringx.From(strings.TrimSuffix(protoFilename, filepath.Ext(protoFilename)))
text, err := util.LoadTemplate(category, rpcTemplateFile, rpcTemplateText)
if err != nil {
return err
}
dir := filepath.Dir(out)
err = util.MkdirIfNotExist(dir)
if err != nil {
return err
}
err = util.With("t").Parse(text).SaveTo(map[string]string{
"package": serviceName.UnTitle(),
"serviceName": serviceName.Title(),
}, out, false)
return err
}

View File

@@ -0,0 +1,21 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestProtoTmpl(t *testing.T) {
out, err := filepath.Abs("./test/test.proto")
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(filepath.Dir(out))
}()
err = ProtoTmpl(out)
assert.Nil(t, err)
_, err = os.Stat(out)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,67 @@
package generator
import (
"fmt"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
)
const (
category = "rpc"
callTemplateFile = "call.tpl"
callInterfaceFunctionTemplateFile = "call-interface-func.tpl"
callFunctionTemplateFile = "call-func.tpl"
configTemplateFileFile = "config.tpl"
etcTemplateFileFile = "etc.tpl"
logicTemplateFileFile = "logic.tpl"
logicFuncTemplateFileFile = "logic-func.tpl"
mainTemplateFile = "main.tpl"
serverTemplateFile = "server.tpl"
serverFuncTemplateFile = "server-func.tpl"
svcTemplateFile = "svc.tpl"
rpcTemplateFile = "template.tpl"
)
var templates = map[string]string{
callTemplateFile: callTemplateText,
callInterfaceFunctionTemplateFile: callInterfaceFunctionTemplate,
callFunctionTemplateFile: callFunctionTemplate,
configTemplateFileFile: configTemplate,
etcTemplateFileFile: etcTemplate,
logicTemplateFileFile: logicTemplate,
logicFuncTemplateFileFile: logicFunctionTemplate,
mainTemplateFile: mainTemplate,
serverTemplateFile: serverTemplate,
serverFuncTemplateFile: functionTemplate,
svcTemplateFile: svcTemplate,
rpcTemplateFile: rpcTemplateText,
}
func GenTemplates(_ *cli.Context) error {
return util.InitTemplates(category, templates)
}
func RevertTemplate(name string) error {
content, ok := templates[name]
if !ok {
return fmt.Errorf("%s: no such file name", name)
}
return util.CreateTemplate(category, name, content)
}
func Clean() error {
return util.Clean(category)
}
func Update(category string) error {
err := Clean()
if err != nil {
return err
}
return util.InitTemplates(category, templates)
}
func GetCategory() string {
return category
}

View File

@@ -0,0 +1,96 @@
package generator
import (
"io/ioutil"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
func TestGenTemplates(t *testing.T) {
err := util.InitTemplates(category, templates)
assert.Nil(t, err)
dir, err := util.GetTemplateDir(category)
assert.Nil(t, err)
file := filepath.Join(dir, "main.tpl")
data, err := ioutil.ReadFile(file)
assert.Nil(t, err)
assert.Equal(t, string(data), mainTemplate)
}
func TestRevertTemplate(t *testing.T) {
name := "main.tpl"
err := util.InitTemplates(category, templates)
assert.Nil(t, err)
dir, err := util.GetTemplateDir(category)
assert.Nil(t, err)
file := filepath.Join(dir, name)
data, err := ioutil.ReadFile(file)
assert.Nil(t, err)
modifyData := string(data) + "modify"
err = util.CreateTemplate(category, name, modifyData)
assert.Nil(t, err)
data, err = ioutil.ReadFile(file)
assert.Nil(t, err)
assert.Equal(t, string(data), modifyData)
assert.Nil(t, RevertTemplate(name))
data, err = ioutil.ReadFile(file)
assert.Nil(t, err)
assert.Equal(t, mainTemplate, string(data))
}
func TestClean(t *testing.T) {
name := "main.tpl"
err := util.InitTemplates(category, templates)
assert.Nil(t, err)
assert.Nil(t, Clean())
dir, err := util.GetTemplateDir(category)
assert.Nil(t, err)
file := filepath.Join(dir, name)
_, err = ioutil.ReadFile(file)
assert.NotNil(t, err)
}
func TestUpdate(t *testing.T) {
name := "main.tpl"
err := util.InitTemplates(category, templates)
assert.Nil(t, err)
dir, err := util.GetTemplateDir(category)
assert.Nil(t, err)
file := filepath.Join(dir, name)
data, err := ioutil.ReadFile(file)
assert.Nil(t, err)
modifyData := string(data) + "modify"
err = util.CreateTemplate(category, name, modifyData)
assert.Nil(t, err)
data, err = ioutil.ReadFile(file)
assert.Nil(t, err)
assert.Equal(t, string(data), modifyData)
assert.Nil(t, Update(category))
data, err = ioutil.ReadFile(file)
assert.Nil(t, err)
assert.Equal(t, mainTemplate, string(data))
}
func TestGetCategory(t *testing.T) {
assert.Equal(t, category, GetCategory())
}

View File

@@ -0,0 +1,25 @@
// test proto
syntax = "proto3";
package test;
option go_package = "go";
import "test_base.proto";
message TestMessage{
base.CommonReq req = 1;
}
message TestReq{}
message TestReply{
base.CommonReply reply = 2;
}
enum TestEnum {
unknown = 0;
male = 1;
female = 2;
}
service TestService{
rpc TestRpc (TestReq)returns(TestReply);
}

View File

@@ -0,0 +1,12 @@
// test proto
syntax = "proto3";
package base;
message CommonReq {
string in = 1;
}
message CommonReply {
string out = 1;
}

View File

@@ -0,0 +1,18 @@
// test proto
syntax = "proto3";
package stream;
option go_package="go";
message StreamReq {
string name = 1;
}
message StreamResp {
string greet = 1;
}
service StreamGreeter {
rpc greet(StreamReq) returns (StreamResp);
}

View File

@@ -0,0 +1,18 @@
// test proto
syntax = "proto3";
package greet;
import "base/common.proto";
message In {
string name = 1;
common.User user = 2;
}
message Out {
string greet = 1;
}
service StreamGreeter {
rpc greet(In) returns (Out);
}

View File

@@ -0,0 +1,18 @@
// test proto
syntax = "proto3";
package stream;
option go_package="github.com/tal-tech/go-zero";
message StreamReq {
string name = 1;
}
message StreamResp {
string greet = 1;
}
service StreamGreeter {
rpc greet(StreamReq) returns (StreamResp);
}

View File

@@ -0,0 +1,16 @@
// test proto
syntax = "proto3";
package stream;
message StreamReq {
string name = 1;
}
message StreamResp {
string greet = 1;
}
service StreamGreeter {
rpc greet(StreamReq) returns (StreamResp);
}

View File

@@ -0,0 +1,18 @@
// test proto
syntax = "proto3";
package stream;
option go_package="user";
message StreamReq {
string name = 1;
}
message StreamResp {
string greet = 1;
}
service StreamGreeter {
rpc greet(StreamReq) returns (StreamResp);
}