rpc service generation (#26)
* add execute files * add protoc-osx * add rpc generation * add rpc generation * add: rpc template generation * update usage * fixed env prepare for project in go path * optimize gomod cache * add README.md * format error * reactor templatex.go * remove waste codemaster
parent
71bbf91a63
commit
db16115037
@ -0,0 +1,6 @@
|
|||||||
|
# Change log
|
||||||
|
|
||||||
|
# 2020-08-27
|
||||||
|
* 新增支持rpc模板生成
|
||||||
|
* 新增支持rpc服务生成
|
||||||
|
|
@ -0,0 +1,23 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/ctx"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/goen"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Rpc(c *cli.Context) error {
|
||||||
|
rpcCtx := ctx.MustCreateRpcContextFromCli(c)
|
||||||
|
generator := gogen.NewDefaultRpcGenerator(rpcCtx)
|
||||||
|
rpcCtx.Must(generator.Generate())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RpcTemplate(c *cli.Context) error {
|
||||||
|
out := c.String("out")
|
||||||
|
idea := c.Bool("idea")
|
||||||
|
generator := gogen.NewRpcTemplate(out, idea)
|
||||||
|
generator.MustGenerate()
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package ctx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
|
"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/stringx"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagSrc = "src"
|
||||||
|
flagDir = "dir"
|
||||||
|
flagShared = "shared"
|
||||||
|
flagService = "service"
|
||||||
|
flagIdea = "idea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
RpcContext struct {
|
||||||
|
ProjectPath string
|
||||||
|
ProjectName stringx.String
|
||||||
|
ServiceName stringx.String
|
||||||
|
CurrentPath string
|
||||||
|
Module string
|
||||||
|
ProtoFileSrc string
|
||||||
|
ProtoSource string
|
||||||
|
TargetDir string
|
||||||
|
SharedDir string
|
||||||
|
GoPath string
|
||||||
|
console.Console
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func MustCreateRpcContext(protoSrc, targetDir, sharedDir, serviceName string, idea bool) *RpcContext {
|
||||||
|
log := console.NewConsole(idea)
|
||||||
|
info, err := prepare(log)
|
||||||
|
log.Must(err)
|
||||||
|
|
||||||
|
if stringx.From(protoSrc).IsEmptyOrSpace() {
|
||||||
|
log.Fatalln("expected proto source, but nothing found")
|
||||||
|
}
|
||||||
|
srcFp, err := filepath.Abs(protoSrc)
|
||||||
|
log.Must(err)
|
||||||
|
|
||||||
|
if !util.FileExists(srcFp) {
|
||||||
|
log.Fatalln("%s is not exists", srcFp)
|
||||||
|
}
|
||||||
|
current := filepath.Dir(srcFp)
|
||||||
|
if stringx.From(targetDir).IsEmptyOrSpace() {
|
||||||
|
targetDir = current
|
||||||
|
}
|
||||||
|
if stringx.From(sharedDir).IsEmptyOrSpace() {
|
||||||
|
sharedDir = filepath.Join(current, "shared")
|
||||||
|
}
|
||||||
|
targetDirFp, err := filepath.Abs(targetDir)
|
||||||
|
log.Must(err)
|
||||||
|
|
||||||
|
sharedFp, err := filepath.Abs(sharedDir)
|
||||||
|
log.Must(err)
|
||||||
|
|
||||||
|
if stringx.From(serviceName).IsEmptyOrSpace() {
|
||||||
|
serviceName = getServiceFromRpcStructure(targetDirFp)
|
||||||
|
}
|
||||||
|
serviceNameString := stringx.From(serviceName)
|
||||||
|
if serviceNameString.IsEmptyOrSpace() {
|
||||||
|
log.Fatalln("service name is not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RpcContext{
|
||||||
|
ProjectPath: info.Path,
|
||||||
|
ProjectName: stringx.From(info.Name),
|
||||||
|
ServiceName: serviceNameString,
|
||||||
|
CurrentPath: current,
|
||||||
|
Module: info.GoMod.Module,
|
||||||
|
ProtoFileSrc: srcFp,
|
||||||
|
ProtoSource: filepath.Base(srcFp),
|
||||||
|
TargetDir: targetDirFp,
|
||||||
|
SharedDir: sharedFp,
|
||||||
|
GoPath: info.GoPath,
|
||||||
|
Console: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func MustCreateRpcContextFromCli(ctx *cli.Context) *RpcContext {
|
||||||
|
os := runtime.GOOS
|
||||||
|
switch os {
|
||||||
|
case "darwin":
|
||||||
|
case "windows":
|
||||||
|
logx.Must(fmt.Errorf("windows will support soon"))
|
||||||
|
default:
|
||||||
|
logx.Must(fmt.Errorf("unexpected os: %s", os))
|
||||||
|
}
|
||||||
|
protoSrc := ctx.String(flagSrc)
|
||||||
|
targetDir := ctx.String(flagDir)
|
||||||
|
sharedDir := ctx.String(flagShared)
|
||||||
|
serviceName := ctx.String(flagService)
|
||||||
|
idea := ctx.Bool(flagIdea)
|
||||||
|
return MustCreateRpcContext(protoSrc, targetDir, sharedDir, serviceName, idea)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServiceFromRpcStructure(targetDir string) string {
|
||||||
|
targetDir = filepath.Clean(targetDir)
|
||||||
|
suffix := filepath.Join("cmd", "rpc")
|
||||||
|
return filepath.Base(strings.TrimSuffix(targetDir, suffix))
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
package ctx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util/console"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errProtobufNotFound = errors.New("github.com/golang/protobuf is not found,please ensure you has already [go get github.com/golang/protobuf]")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
constGo = "go"
|
||||||
|
constProtoC = "protoc"
|
||||||
|
constGoModOn = "go env GO111MODULE"
|
||||||
|
constGoMod = "go env GOMOD"
|
||||||
|
constGoModCache = "go env GOMODCACHE"
|
||||||
|
constGoPath = "go env GOPATH"
|
||||||
|
constProtoCGenGo = "protoc-gen-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Project struct {
|
||||||
|
Path string
|
||||||
|
Name string
|
||||||
|
GoPath string
|
||||||
|
Protobuf Protobuf
|
||||||
|
GoMod GoMod
|
||||||
|
}
|
||||||
|
|
||||||
|
GoMod struct {
|
||||||
|
ModOn bool
|
||||||
|
GoModCache string
|
||||||
|
GoMod string
|
||||||
|
Module string
|
||||||
|
}
|
||||||
|
Protobuf struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func prepare(log console.Console) (*Project, error) {
|
||||||
|
log.Info("check go env ...")
|
||||||
|
_, err := exec.LookPath(constGo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = exec.LookPath(constProtoC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
goModOn bool
|
||||||
|
goMod, goModCache, module string
|
||||||
|
goPath string
|
||||||
|
name, path string
|
||||||
|
protobufModule string
|
||||||
|
)
|
||||||
|
ret, err := execx.Run(constGoModOn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
goModOn = strings.TrimSpace(ret) == "on"
|
||||||
|
ret, err = execx.Run(constGoMod)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
goMod = strings.TrimSpace(ret)
|
||||||
|
ret, err = execx.Run(constGoModCache)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
goModCache = strings.TrimSpace(ret)
|
||||||
|
ret, err = execx.Run(constGoPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
goPath = strings.TrimSpace(ret)
|
||||||
|
src := filepath.Join(goPath, "src")
|
||||||
|
if len(goMod) > 0 {
|
||||||
|
if goModCache == "" {
|
||||||
|
goModCache = filepath.Join(goPath, "pkg", "mod")
|
||||||
|
}
|
||||||
|
path = filepath.Dir(goMod)
|
||||||
|
name = filepath.Base(path)
|
||||||
|
data, err := ioutil.ReadFile(goMod)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
module, err = matchModule(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
protobufModule, err = matchProtoBuf(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if goModCache == "" {
|
||||||
|
goModCache = src
|
||||||
|
}
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(pwd, src) {
|
||||||
|
return nil, fmt.Errorf("%s: project is not in go mod and go path", pwd)
|
||||||
|
}
|
||||||
|
r := strings.TrimPrefix(pwd, src+string(filepath.Separator))
|
||||||
|
name = filepath.Dir(r)
|
||||||
|
if name == "." {
|
||||||
|
name = r
|
||||||
|
}
|
||||||
|
path = filepath.Join(src, name)
|
||||||
|
module = name
|
||||||
|
}
|
||||||
|
|
||||||
|
protobuf := filepath.Join(goModCache, protobufModule)
|
||||||
|
if !util.FileExists(protobuf) {
|
||||||
|
return nil, fmt.Errorf("expected protobuf module in path: %s,please ensure you has already [go get github.com/golang/protobuf]", protobuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var protoCGenGoFilename string
|
||||||
|
os := runtime.GOOS
|
||||||
|
switch os {
|
||||||
|
case "darwin":
|
||||||
|
protoCGenGoFilename = filepath.Join(goPath, "bin", "protoc-gen-go")
|
||||||
|
case "windows":
|
||||||
|
protoCGenGoFilename = filepath.Join(goPath, "bin", "protoc-gen-go.exe")
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpeted os: %s", os)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !util.FileExists(protoCGenGoFilename) {
|
||||||
|
sh := "go install " + filepath.Join(protobuf, constProtoCGenGo)
|
||||||
|
log.Warning(sh)
|
||||||
|
stdout, err := execx.Run(sh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(stdout)
|
||||||
|
}
|
||||||
|
if !util.FileExists(protoCGenGoFilename) {
|
||||||
|
return nil, fmt.Errorf("protoc-gen-go is not found")
|
||||||
|
}
|
||||||
|
return &Project{
|
||||||
|
Name: name,
|
||||||
|
Path: path,
|
||||||
|
GoPath: goPath,
|
||||||
|
Protobuf: Protobuf{
|
||||||
|
Path: protobuf,
|
||||||
|
},
|
||||||
|
GoMod: GoMod{
|
||||||
|
ModOn: goModOn,
|
||||||
|
GoModCache: goModCache,
|
||||||
|
GoMod: goMod,
|
||||||
|
Module: module,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// github.com/golang/protobuf@{version}
|
||||||
|
func matchProtoBuf(data []byte) (string, error) {
|
||||||
|
text := string(data)
|
||||||
|
re := regexp.MustCompile(`(?m)(github.com/golang/protobuf)\s+(v[0-9.]+)`)
|
||||||
|
matches := re.FindAllStringSubmatch(text, -1)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return "", errProtobufNotFound
|
||||||
|
}
|
||||||
|
groups := matches[0]
|
||||||
|
if len(groups) < 3 {
|
||||||
|
return "", errProtobufNotFound
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s@%s", groups[1], groups[2]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchModule(data []byte) (string, error) {
|
||||||
|
text := string(data)
|
||||||
|
re := regexp.MustCompile(`(?m)^\s*module\s+[a-z0-9/\-.]+$`)
|
||||||
|
matches := re.FindAllString(text, -1)
|
||||||
|
if len(matches) == 1 {
|
||||||
|
target := matches[0]
|
||||||
|
index := strings.Index(target, "module")
|
||||||
|
return strings.TrimSpace(target[index+6:]), nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package execx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Run(arg string) (string, error) {
|
||||||
|
goos := runtime.GOOS
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
switch goos {
|
||||||
|
case "darwin":
|
||||||
|
cmd = exec.Command("sh", "-c", arg)
|
||||||
|
case "windows":
|
||||||
|
cmd = exec.Command("cmd.exe", "/c", arg)
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unexpected os: %v", goos)
|
||||||
|
}
|
||||||
|
dtsout := new(bytes.Buffer)
|
||||||
|
stderr := new(bytes.Buffer)
|
||||||
|
cmd.Stdout = dtsout
|
||||||
|
cmd.Stderr = stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
if stderr.Len() > 0 {
|
||||||
|
return "", errors.New(stderr.String())
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return dtsout.String(), nil
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/ctx"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dirTarget = "dirTarget"
|
||||||
|
dirConfig = "config"
|
||||||
|
dirEtc = "etc"
|
||||||
|
dirSvc = "svc"
|
||||||
|
dirShared = "shared"
|
||||||
|
dirHandler = "handler"
|
||||||
|
dirLogic = "logic"
|
||||||
|
dirPb = "pb"
|
||||||
|
dirInternal = "internal"
|
||||||
|
fileConfig = "config.go"
|
||||||
|
fileServiceContext = "servicecontext.go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
defaultRpcGenerator struct {
|
||||||
|
dirM map[string]string
|
||||||
|
Ctx *ctx.RpcContext
|
||||||
|
ast *parser.PbAst
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDefaultRpcGenerator(ctx *ctx.RpcContext) *defaultRpcGenerator {
|
||||||
|
return &defaultRpcGenerator{
|
||||||
|
Ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) Generate() (err error) {
|
||||||
|
g.Ctx.Info("code generating...")
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
g.Ctx.Success("Done.")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err = g.createDir()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genEtc()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genPb()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genConfig()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genSvc()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genLogic()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genRemoteHandler()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genMain()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.genShared()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var configTemplate = `package config
|
||||||
|
|
||||||
|
import "github.com/tal-tech/go-zero/rpcx"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Config struct {
|
||||||
|
rpcx.RpcServerConf
|
||||||
|
}
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genConfig() error {
|
||||||
|
configPath := g.dirM[dirConfig]
|
||||||
|
fileName := filepath.Join(configPath, fileConfig)
|
||||||
|
if util.FileExists(fileName) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(fileName, []byte(configTemplate), os.ModePerm)
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// target
|
||||||
|
// ├── etc
|
||||||
|
// ├── internal
|
||||||
|
// │ ├── config
|
||||||
|
// │ ├── handler
|
||||||
|
// │ ├── logic
|
||||||
|
// │ ├── pb
|
||||||
|
// │ └── svc
|
||||||
|
func (g *defaultRpcGenerator) createDir() error {
|
||||||
|
ctx := g.Ctx
|
||||||
|
m := make(map[string]string)
|
||||||
|
m[dirTarget] = ctx.TargetDir
|
||||||
|
m[dirEtc] = filepath.Join(ctx.TargetDir, dirEtc)
|
||||||
|
m[dirInternal] = filepath.Join(ctx.TargetDir, dirInternal)
|
||||||
|
m[dirConfig] = filepath.Join(ctx.TargetDir, dirInternal, dirConfig)
|
||||||
|
m[dirHandler] = filepath.Join(ctx.TargetDir, dirInternal, dirHandler)
|
||||||
|
m[dirLogic] = filepath.Join(ctx.TargetDir, dirInternal, dirLogic)
|
||||||
|
m[dirPb] = filepath.Join(ctx.TargetDir, dirPb)
|
||||||
|
m[dirSvc] = filepath.Join(ctx.TargetDir, dirInternal, dirSvc)
|
||||||
|
m[dirShared] = g.Ctx.SharedDir
|
||||||
|
for _, d := range m {
|
||||||
|
err := util.MkdirIfNotExist(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.dirM = m
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) mustGetPackage(dir string) string {
|
||||||
|
target := g.dirM[dir]
|
||||||
|
projectPath := g.Ctx.ProjectPath
|
||||||
|
relativePath := strings.TrimPrefix(target, projectPath)
|
||||||
|
return g.Ctx.Module + relativePath
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var etcTemplate = `{
|
||||||
|
"Name": "{{.serviceName}}.rpc",
|
||||||
|
"Log": {
|
||||||
|
"Mode": "console"
|
||||||
|
},
|
||||||
|
"ListenOn": "127.0.0.1:8080",
|
||||||
|
"Etcd": {
|
||||||
|
"Hosts": ["127.0.0.1:6379"],
|
||||||
|
"Key": "{{.serviceName}}.rpc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genEtc() error {
|
||||||
|
etdDir := g.dirM[dirEtc]
|
||||||
|
fileName := filepath.Join(etdDir, fmt.Sprintf("%v.json", g.Ctx.ServiceName.Lower()))
|
||||||
|
if util.FileExists(fileName) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return util.With("etc").
|
||||||
|
Parse(etcTemplate).
|
||||||
|
SaveTo(map[string]interface{}{
|
||||||
|
"serviceName": g.Ctx.ServiceName.Lower(),
|
||||||
|
}, fileName, false)
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
remoteTemplate = `{{.head}}
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
{{.imports}}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
{{.types}}
|
||||||
|
)
|
||||||
|
|
||||||
|
{{.newFuncs}}
|
||||||
|
`
|
||||||
|
functionTemplate = `{{.head}}
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
{{.imports}}
|
||||||
|
)
|
||||||
|
|
||||||
|
{{if .hasComment}}{{.comment}}{{end}}
|
||||||
|
func (s *{{.server}}Server) {{.method}} (ctx context.Context, in *{{.package}}.{{.request}}) (*{{.package}}.{{.response}}, error) {
|
||||||
|
l:=logic.New{{.logicName}}(ctx,s.svcCtx)
|
||||||
|
return l.{{.method}}(in)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
typeFmt = `%sServer struct {
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}`
|
||||||
|
newFuncFmt = `func New%sServer(svcCtx *svc.ServiceContext) *%sServer {
|
||||||
|
return &%sServer{
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genRemoteHandler() error {
|
||||||
|
handlerPath := g.dirM[dirHandler]
|
||||||
|
serverGo := fmt.Sprintf("%vhandler.go", g.Ctx.ServiceName.Lower())
|
||||||
|
fileName := filepath.Join(handlerPath, serverGo)
|
||||||
|
file := g.ast
|
||||||
|
svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
|
||||||
|
types := make([]string, 0)
|
||||||
|
newFuncs := make([]string, 0)
|
||||||
|
head := util.GetHead(g.Ctx.ProtoSource)
|
||||||
|
for _, service := range file.Service {
|
||||||
|
types = append(types, fmt.Sprintf(typeFmt, service.Name.Title()))
|
||||||
|
newFuncs = append(newFuncs, fmt.Sprintf(newFuncFmt, service.Name.Title(), service.Name.Title(), service.Name.Title()))
|
||||||
|
}
|
||||||
|
err := util.With("server").GoFmt(true).Parse(remoteTemplate).SaveTo(map[string]interface{}{
|
||||||
|
"head": head,
|
||||||
|
"types": strings.Join(types, "\n"),
|
||||||
|
"newFuncs": strings.Join(newFuncs, "\n"),
|
||||||
|
"imports": svcImport,
|
||||||
|
}, fileName, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return g.genFunctions()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genFunctions() error {
|
||||||
|
handlerPath := g.dirM[dirHandler]
|
||||||
|
file := g.ast
|
||||||
|
pkg := file.Package
|
||||||
|
|
||||||
|
head := util.GetHead(g.Ctx.ProtoSource)
|
||||||
|
handlerImports := make([]string, 0)
|
||||||
|
pbImport := fmt.Sprintf(`%v "%v"`, pkg, g.mustGetPackage(dirPb))
|
||||||
|
handlerImports = append(handlerImports, pbImport, fmt.Sprintf(`"%v"`, g.mustGetPackage(dirLogic)))
|
||||||
|
for _, service := range file.Service {
|
||||||
|
for _, method := range service.Funcs {
|
||||||
|
handlerName := fmt.Sprintf("%shandler.go", method.Name.Lower())
|
||||||
|
filename := filepath.Join(handlerPath, handlerName)
|
||||||
|
// override
|
||||||
|
err := util.With("func").GoFmt(true).Parse(functionTemplate).SaveTo(map[string]interface{}{
|
||||||
|
"head": head,
|
||||||
|
"server": service.Name.Title(),
|
||||||
|
"imports": strings.Join(handlerImports, "\r\n"),
|
||||||
|
"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
|
||||||
|
"method": method.Name.Title(),
|
||||||
|
"package": pkg,
|
||||||
|
"request": method.InType,
|
||||||
|
"response": method.OutType,
|
||||||
|
"hasComment": len(method.Document),
|
||||||
|
"comment": strings.Join(method.Document, "\r\n"),
|
||||||
|
}, filename, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logicTemplate = `package logic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
{{.imports}}
|
||||||
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
{{.logicName}} struct {
|
||||||
|
ctx context.Context
|
||||||
|
logx.Logger
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func New{{.logicName}}(ctx context.Context,svcCtx *svc.ServiceContext) *{{.logicName}} {
|
||||||
|
return &{{.logicName}}{
|
||||||
|
ctx: ctx,
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{.functions}}
|
||||||
|
`
|
||||||
|
logicFunctionTemplate = `{{if .hasComment}}{{.comment}}{{end}}
|
||||||
|
func (l *{{.logicName}}) {{.method}} (in *{{.package}}.{{.request}}) (*{{.package}}.{{.response}}, error) {
|
||||||
|
var resp {{.package}}.{{.response}}
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return &resp,nil
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genLogic() error {
|
||||||
|
logicPath := g.dirM[dirLogic]
|
||||||
|
protoPkg := g.ast.Package
|
||||||
|
service := g.ast.Service
|
||||||
|
for _, item := range service {
|
||||||
|
for _, method := range item.Funcs {
|
||||||
|
logicName := fmt.Sprintf("%slogic.go", method.Name.Lower())
|
||||||
|
filename := filepath.Join(logicPath, logicName)
|
||||||
|
functions, err := genLogicFunction(protoPkg, method)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
imports := collection.NewSet()
|
||||||
|
pbImport := fmt.Sprintf(`%v "%v"`, protoPkg, g.mustGetPackage(dirPb))
|
||||||
|
svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
|
||||||
|
imports.AddStr(pbImport, svcImport)
|
||||||
|
err = util.With("logic").GoFmt(true).Parse(logicTemplate).SaveTo(map[string]interface{}{
|
||||||
|
"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
|
||||||
|
"functions": functions,
|
||||||
|
"imports": strings.Join(imports.KeysStr(), "\r\n"),
|
||||||
|
}, filename, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genLogicFunction(packageName string, method *parser.Func) (string, error) {
|
||||||
|
var functions = make([]string, 0)
|
||||||
|
buffer, err := util.With("fun").Parse(logicFunctionTemplate).Execute(map[string]interface{}{
|
||||||
|
"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
|
||||||
|
"method": method.Name.Title(),
|
||||||
|
"package": packageName,
|
||||||
|
"request": method.InType,
|
||||||
|
"response": method.OutType,
|
||||||
|
"hasComment": len(method.Document) > 0,
|
||||||
|
"comment": strings.Join(method.Document, "\r\n"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
functions = append(functions, buffer.String())
|
||||||
|
return strings.Join(functions, "\n"), nil
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mainTemplate = `{{.head}}
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/conf"
|
||||||
|
"github.com/tal-tech/go-zero/rpcx"
|
||||||
|
|
||||||
|
{{.imports}}
|
||||||
|
)
|
||||||
|
|
||||||
|
var configFile = flag.String("f", "etc/{{.serviceName}}.json", "the config file")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var c config.Config
|
||||||
|
conf.MustLoad(*configFile, &c)
|
||||||
|
ctx := svc.NewServiceContext(c)
|
||||||
|
{{.srv}}
|
||||||
|
|
||||||
|
s, err := rpcx.NewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
|
||||||
|
{{.registers}}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
|
||||||
|
s.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genMain() error {
|
||||||
|
mainPath := g.dirM[dirTarget]
|
||||||
|
file := g.ast
|
||||||
|
pkg := file.Package
|
||||||
|
|
||||||
|
fileName := filepath.Join(mainPath, fmt.Sprintf("%v.go", g.Ctx.ServiceName.Lower()))
|
||||||
|
imports := make([]string, 0)
|
||||||
|
pbImport := fmt.Sprintf(`%v "%v"`, pkg, g.mustGetPackage(dirPb))
|
||||||
|
svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
|
||||||
|
remoteImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirHandler))
|
||||||
|
configImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirConfig))
|
||||||
|
imports = append(imports, configImport, pbImport, remoteImport, svcImport)
|
||||||
|
srv, registers := g.genServer(pkg, file.Service)
|
||||||
|
head := util.GetHead(g.Ctx.ProtoSource)
|
||||||
|
return util.With("main").GoFmt(true).Parse(mainTemplate).SaveTo(map[string]interface{}{
|
||||||
|
"head": head,
|
||||||
|
"package": pkg,
|
||||||
|
"serviceName": g.Ctx.ServiceName.Lower(),
|
||||||
|
"srv": srv,
|
||||||
|
"registers": registers,
|
||||||
|
"imports": strings.Join(imports, "\r\n"),
|
||||||
|
}, fileName, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genServer(pkg string, list []*parser.RpcService) (string, string) {
|
||||||
|
list1 := make([]string, 0)
|
||||||
|
list2 := make([]string, 0)
|
||||||
|
for _, item := range list {
|
||||||
|
name := item.Name.UnTitle()
|
||||||
|
list1 = append(list1, fmt.Sprintf("%sSrv := handler.New%sServer(ctx)", name, item.Name.Title()))
|
||||||
|
list2 = append(list2, fmt.Sprintf("%s.Register%sServer(grpcServer, %sSrv)", pkg, item.Name.Title(), name))
|
||||||
|
}
|
||||||
|
return strings.Join(list1, "\n"), strings.Join(list2, "\n")
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dsymonds/gotoc/parser"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/lang"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
|
||||||
|
astParser "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genPb() error {
|
||||||
|
importPath, filename := filepath.Split(g.Ctx.ProtoFileSrc)
|
||||||
|
tree, err := parser.ParseFiles([]string{filename}, []string{importPath})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tree.Files) == 0 {
|
||||||
|
return errors.New("proto ast parse failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
file := tree.Files[0]
|
||||||
|
if len(file.Package) == 0 {
|
||||||
|
return errors.New("expected package, but nothing found")
|
||||||
|
}
|
||||||
|
|
||||||
|
targetStruct := make(map[string]lang.PlaceholderType)
|
||||||
|
for _, item := range file.Messages {
|
||||||
|
if len(item.Messages) > 0 {
|
||||||
|
return fmt.Errorf(`line %v: unexpected inner message near: "%v""`, item.Messages[0].Position.Line, item.Messages[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := stringx.From(item.Name)
|
||||||
|
if _, ok := targetStruct[name.Lower()]; ok {
|
||||||
|
return fmt.Errorf("line %v: duplicate %v", item.Position.Line, name)
|
||||||
|
}
|
||||||
|
targetStruct[name.Lower()] = lang.Placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
pbPath := g.dirM[dirPb]
|
||||||
|
protoFileName := filepath.Base(g.Ctx.ProtoFileSrc)
|
||||||
|
err = g.protocGenGo(pbPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pbGo := strings.TrimSuffix(protoFileName, ".proto") + ".pb.go"
|
||||||
|
pbFile := filepath.Join(pbPath, pbGo)
|
||||||
|
bts, err := ioutil.ReadFile(pbFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
aspParser := astParser.NewAstParser(bts, targetStruct, g.Ctx.Console)
|
||||||
|
ast, err := aspParser.Parse()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ast.Service) == 0 {
|
||||||
|
return fmt.Errorf("service not found")
|
||||||
|
}
|
||||||
|
g.ast = ast
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) protocGenGo(target string) error {
|
||||||
|
src := filepath.Dir(g.Ctx.ProtoFileSrc)
|
||||||
|
sh := fmt.Sprintf(`export PATH=%s:$PATH
|
||||||
|
protoc -I=%s --go_out=plugins=grpc:%s %s`, filepath.Join(g.Ctx.GoPath, "bin"), src, target, g.Ctx.ProtoFileSrc)
|
||||||
|
stdout, err := execx.Run(sh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Ctx.Info(stdout)
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
sharedTemplateText = `{{.head}}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination ./mock{{.name}}model.go -package {{.filePackage}} -source $GOFILE
|
||||||
|
|
||||||
|
package {{.filePackage}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
{{.package}}
|
||||||
|
"github.com/tal-tech/go-zero/core/jsonx"
|
||||||
|
"github.com/tal-tech/go-zero/rpcx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
{{.serviceName}}Model interface {
|
||||||
|
{{.interface}}
|
||||||
|
}
|
||||||
|
default{{.serviceName}}Model struct {
|
||||||
|
cli rpcx.Client
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func NewDefault{{.serviceName}}Model(cli rpcx.Client) {{.serviceName}}Model {
|
||||||
|
return &default{{.serviceName}}Model{
|
||||||
|
cli: cli,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{.functions}}
|
||||||
|
`
|
||||||
|
sharedTemplateTypes = `{{.head}}
|
||||||
|
|
||||||
|
package {{.filePackage}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errJsonConvert = errors.New("json convert error")
|
||||||
|
)
|
||||||
|
|
||||||
|
{{.types}}
|
||||||
|
|
||||||
|
`
|
||||||
|
sharedInterfaceFunctionTemplate = `{{if .hasComment}}{{.comment}}
|
||||||
|
{{end}}{{.method}}(ctx context.Context,in *{{.pbRequest}}) {{if .hasResponse}}(*{{.pbResponse}},{{end}} error{{if .hasResponse}}){{end}}`
|
||||||
|
sharedFunctionTemplate = `
|
||||||
|
{{if .hasComment}}{{.comment}}{{end}}
|
||||||
|
func (m *default{{.rpcServiceName}}Model) {{.method}}(ctx context.Context,in *{{.pbRequest}}) {{if .hasResponse}}(*{{.pbResponse}},{{end}} error{{if .hasResponse}}){{end}} {
|
||||||
|
conn:= m.cli.Conn()
|
||||||
|
client := {{.package}}.New{{.rpcServiceName}}Client(conn)
|
||||||
|
var request {{.package}}.{{.pbRequest}}
|
||||||
|
bts, err := jsonx.Marshal(in)
|
||||||
|
if err != nil {
|
||||||
|
return {{if .hasResponse}}nil,{{end}}errJsonConvert
|
||||||
|
}
|
||||||
|
err = jsonx.Unmarshal(bts, &request)
|
||||||
|
if err != nil {
|
||||||
|
return {{if .hasResponse}}nil,{{end}}errJsonConvert
|
||||||
|
}
|
||||||
|
{{if .hasResponse}}resp,err:={{else}}_,err={{end}}client.{{.method}}(ctx, &request)
|
||||||
|
{{if .hasResponse}}if err!=nil{
|
||||||
|
return nil,err
|
||||||
|
}
|
||||||
|
var ret {{.pbResponse}}
|
||||||
|
bts,err=jsonx.Marshal(resp)
|
||||||
|
if err!=nil{
|
||||||
|
return nil,errJsonConvert
|
||||||
|
}
|
||||||
|
err=jsonx.Unmarshal(bts,&ret)
|
||||||
|
if err!=nil{
|
||||||
|
return nil,errJsonConvert
|
||||||
|
}
|
||||||
|
return &ret, nil{{else}}if err!=nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil{{end}}
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genShared() error {
|
||||||
|
sharePackage := filepath.Base(g.Ctx.SharedDir)
|
||||||
|
file := g.ast
|
||||||
|
typeCode, err := file.GenTypesCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pbPkg := file.Package
|
||||||
|
remotePackage := fmt.Sprintf(`%v "%v"`, pbPkg, g.mustGetPackage(dirPb))
|
||||||
|
filename := filepath.Join(g.Ctx.SharedDir, "types.go")
|
||||||
|
head := util.GetHead(g.Ctx.ProtoSource)
|
||||||
|
err = util.With("types").GoFmt(true).Parse(sharedTemplateTypes).SaveTo(map[string]interface{}{
|
||||||
|
"head": head,
|
||||||
|
"filePackage": sharePackage,
|
||||||
|
"pbPkg": pbPkg,
|
||||||
|
"serviceName": g.Ctx.ServiceName.Title(),
|
||||||
|
"lowerStartServiceName": g.Ctx.ServiceName.UnTitle(),
|
||||||
|
"types": typeCode,
|
||||||
|
}, filename, true)
|
||||||
|
|
||||||
|
for _, service := range file.Service {
|
||||||
|
filename := filepath.Join(g.Ctx.SharedDir, fmt.Sprintf("%smodel.go", service.Name.Lower()))
|
||||||
|
functions, err := g.getFuncs(service)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iFunctions, err := g.getInterfaceFuncs(service)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mockFile := filepath.Join(g.Ctx.SharedDir, fmt.Sprintf("mock%smodel.go", service.Name.Lower()))
|
||||||
|
os.Remove(mockFile)
|
||||||
|
err = util.With("shared").GoFmt(true).Parse(sharedTemplateText).SaveTo(map[string]interface{}{
|
||||||
|
"name": service.Name.Lower(),
|
||||||
|
"head": head,
|
||||||
|
"filePackage": sharePackage,
|
||||||
|
"pbPkg": pbPkg,
|
||||||
|
"package": remotePackage,
|
||||||
|
"serviceName": service.Name.Title(),
|
||||||
|
"functions": strings.Join(functions, "\n"),
|
||||||
|
"interface": strings.Join(iFunctions, "\n"),
|
||||||
|
}, filename, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if mockgen is already installed, it will generate code of gomock for shared files
|
||||||
|
_, err = exec.LookPath("mockgen")
|
||||||
|
if err != nil {
|
||||||
|
g.Ctx.Warning("warning:mockgen is not found")
|
||||||
|
} else {
|
||||||
|
execx.Run(fmt.Sprintf("cd %s \ngo generate", g.Ctx.SharedDir))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) getFuncs(service *parser.RpcService) ([]string, error) {
|
||||||
|
file := g.ast
|
||||||
|
pkgName := file.Package
|
||||||
|
functions := make([]string, 0)
|
||||||
|
for _, method := range service.Funcs {
|
||||||
|
data, found := file.Strcuts[strings.ToLower(method.OutType)]
|
||||||
|
if found {
|
||||||
|
found = len(data.Field) > 0
|
||||||
|
}
|
||||||
|
var comment string
|
||||||
|
if len(method.Document) > 0 {
|
||||||
|
comment = method.Document[0]
|
||||||
|
}
|
||||||
|
buffer, err := util.With("sharedFn").Parse(sharedFunctionTemplate).Execute(map[string]interface{}{
|
||||||
|
"rpcServiceName": service.Name.Title(),
|
||||||
|
"method": method.Name.Title(),
|
||||||
|
"package": pkgName,
|
||||||
|
"pbRequest": method.InType,
|
||||||
|
"pbResponse": method.OutType,
|
||||||
|
"hasResponse": found,
|
||||||
|
"hasComment": len(method.Document) > 0,
|
||||||
|
"comment": comment,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
functions = append(functions, buffer.String())
|
||||||
|
}
|
||||||
|
return functions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) getInterfaceFuncs(service *parser.RpcService) ([]string, error) {
|
||||||
|
file := g.ast
|
||||||
|
functions := make([]string, 0)
|
||||||
|
for _, method := range service.Funcs {
|
||||||
|
data, found := file.Strcuts[strings.ToLower(method.OutType)]
|
||||||
|
if found {
|
||||||
|
found = len(data.Field) > 0
|
||||||
|
}
|
||||||
|
var comment string
|
||||||
|
if len(method.Document) > 0 {
|
||||||
|
comment = method.Document[0]
|
||||||
|
}
|
||||||
|
buffer, err := util.With("interfaceFn").Parse(sharedInterfaceFunctionTemplate).Execute(map[string]interface{}{
|
||||||
|
"hasComment": len(method.Document) > 0,
|
||||||
|
"comment": comment,
|
||||||
|
"method": method.Name.Title(),
|
||||||
|
"pbRequest": method.InType,
|
||||||
|
"pbResponse": method.OutType,
|
||||||
|
"hasResponse": found,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
functions = append(functions, buffer.String())
|
||||||
|
}
|
||||||
|
return functions, nil
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var svcTemplate = `package svc
|
||||||
|
|
||||||
|
import {{.imports}}
|
||||||
|
|
||||||
|
type (
|
||||||
|
ServiceContext struct {
|
||||||
|
c config.Config
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
return &ServiceContext{
|
||||||
|
c:c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func (g *defaultRpcGenerator) genSvc() error {
|
||||||
|
svcPath := g.dirM[dirSvc]
|
||||||
|
fileName := filepath.Join(svcPath, fileServiceContext)
|
||||||
|
return util.With("svc").GoFmt(true).Parse(svcTemplate).SaveTo(map[string]interface{}{
|
||||||
|
"imports": fmt.Sprintf(`"%v"`, g.mustGetPackage(dirConfig)),
|
||||||
|
}, fileName, false)
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package gogen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util/console"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rpcTemplateText = `syntax = "proto3";
|
||||||
|
|
||||||
|
package remoteuser;
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
string username = 1;
|
||||||
|
string password = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response {
|
||||||
|
string name = 1;
|
||||||
|
string gender = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
service User{
|
||||||
|
rpc Login(Request)returns(Response);
|
||||||
|
}`
|
||||||
|
|
||||||
|
type (
|
||||||
|
rpcTemplate struct {
|
||||||
|
out string
|
||||||
|
console.Console
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRpcTemplate(out string, idea bool) *rpcTemplate {
|
||||||
|
return &rpcTemplate{
|
||||||
|
out: out,
|
||||||
|
Console: console.NewConsole(idea),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcTemplate) MustGenerate() {
|
||||||
|
err := util.With("t").Parse(rpcTemplateText).SaveTo(nil, r.out, false)
|
||||||
|
r.Must(err)
|
||||||
|
r.Success("Done.")
|
||||||
|
}
|
@ -0,0 +1,483 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/lang"
|
||||||
|
sx "github.com/tal-tech/go-zero/core/stringx"
|
||||||
|
"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/stringx"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagStar = "*"
|
||||||
|
suffixServer = "Server"
|
||||||
|
referenceContext = "context."
|
||||||
|
unknownPrefix = "XXX_"
|
||||||
|
ignoreJsonTagExpression = `json:"-"`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errorParseError = errors.New("pb parse error")
|
||||||
|
typeTemplate = `type (
|
||||||
|
{{.types}}
|
||||||
|
)`
|
||||||
|
structTemplate = `{{if .type}}type {{end}}{{.name}} struct {
|
||||||
|
{{.fields}}
|
||||||
|
}`
|
||||||
|
fieldTemplate = `{{if .hasDoc}}{{.doc}}
|
||||||
|
{{end}}{{.name}} {{.type}} {{.tag}}{{if .hasComment}}{{.comment}}{{end}}`
|
||||||
|
objectM = make(map[string]*Struct)
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
astParser struct {
|
||||||
|
golang []byte
|
||||||
|
filterStruct map[string]lang.PlaceholderType
|
||||||
|
console.Console
|
||||||
|
fileSet *token.FileSet
|
||||||
|
}
|
||||||
|
Field struct {
|
||||||
|
Name stringx.String
|
||||||
|
TypeName string
|
||||||
|
JsonTag string
|
||||||
|
Document []string
|
||||||
|
Comment []string
|
||||||
|
}
|
||||||
|
Struct struct {
|
||||||
|
Name stringx.String
|
||||||
|
Document []string
|
||||||
|
Comment []string
|
||||||
|
Field []*Field
|
||||||
|
}
|
||||||
|
Func struct {
|
||||||
|
Name stringx.String
|
||||||
|
InType string
|
||||||
|
InTypeName string // remove *Context,such as LoginRequest、UserRequest
|
||||||
|
OutTypeName string // remove *Context
|
||||||
|
OutType string
|
||||||
|
Document []string
|
||||||
|
}
|
||||||
|
RpcService struct {
|
||||||
|
Name stringx.String
|
||||||
|
Funcs []*Func
|
||||||
|
}
|
||||||
|
// parsing for rpc
|
||||||
|
PbAst struct {
|
||||||
|
Package string
|
||||||
|
// external reference
|
||||||
|
Imports map[string]string
|
||||||
|
Strcuts map[string]*Struct
|
||||||
|
// rpc server's functions,not all functions
|
||||||
|
Service []*RpcService
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAstParser(golang []byte, filterStruct map[string]lang.PlaceholderType, log console.Console) *astParser {
|
||||||
|
return &astParser{
|
||||||
|
golang: golang,
|
||||||
|
filterStruct: filterStruct,
|
||||||
|
Console: log,
|
||||||
|
fileSet: token.NewFileSet(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (a *astParser) Parse() (*PbAst, error) {
|
||||||
|
fSet := a.fileSet
|
||||||
|
f, err := parser.ParseFile(fSet, "", a.golang, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commentMap := ast.NewCommentMap(fSet, f, f.Comments)
|
||||||
|
f.Comments = commentMap.Filter(f).Comments()
|
||||||
|
var pbAst PbAst
|
||||||
|
pbAst.Package = a.mustGetIndentName(f.Name)
|
||||||
|
imports := make(map[string]string)
|
||||||
|
for _, item := range f.Imports {
|
||||||
|
if item == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if item.Path == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := a.mustGetIndentName(item.Name)
|
||||||
|
value := item.Path.Value
|
||||||
|
imports[key] = value
|
||||||
|
}
|
||||||
|
structs, funcs := a.mustScope(f.Scope)
|
||||||
|
pbAst.Imports = imports
|
||||||
|
pbAst.Strcuts = structs
|
||||||
|
pbAst.Service = funcs
|
||||||
|
return &pbAst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) mustScope(scope *ast.Scope) (map[string]*Struct, []*RpcService) {
|
||||||
|
if scope == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
objects := scope.Objects
|
||||||
|
structs := make(map[string]*Struct)
|
||||||
|
serviceList := make([]*RpcService, 0)
|
||||||
|
for name, obj := range objects {
|
||||||
|
decl := obj.Decl
|
||||||
|
if decl == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
typeSpec, ok := decl.(*ast.TypeSpec)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tp := typeSpec.Type
|
||||||
|
|
||||||
|
switch v := tp.(type) {
|
||||||
|
|
||||||
|
case *ast.StructType:
|
||||||
|
st, err := a.parseObject(name, v)
|
||||||
|
a.Must(err)
|
||||||
|
structs[st.Name.Lower()] = st
|
||||||
|
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
if !strings.HasSuffix(name, suffixServer) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list := a.mustServerFunctions(v)
|
||||||
|
serviceList = append(serviceList, &RpcService{
|
||||||
|
Name: stringx.From(strings.TrimSuffix(name, suffixServer)),
|
||||||
|
Funcs: list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetStruct := make(map[string]*Struct)
|
||||||
|
for st := range a.filterStruct {
|
||||||
|
lower := strings.ToLower(st)
|
||||||
|
targetStruct[lower] = structs[lower]
|
||||||
|
}
|
||||||
|
return targetStruct, serviceList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) mustServerFunctions(v *ast.InterfaceType) []*Func {
|
||||||
|
funcs := make([]*Func, 0)
|
||||||
|
methodObject := v.Methods
|
||||||
|
if methodObject == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, method := range methodObject.List {
|
||||||
|
var item Func
|
||||||
|
name := a.mustGetIndentName(method.Names[0])
|
||||||
|
doc := a.parseCommentOrDoc(method.Doc)
|
||||||
|
item.Name = stringx.From(name)
|
||||||
|
item.Document = doc
|
||||||
|
types := method.Type
|
||||||
|
if types == nil {
|
||||||
|
funcs = append(funcs, &item)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v, ok := types.(*ast.FuncType)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params := v.Params
|
||||||
|
if params != nil {
|
||||||
|
inList, err := a.parseFields(params.List, true)
|
||||||
|
a.Must(err)
|
||||||
|
|
||||||
|
for _, data := range inList {
|
||||||
|
if strings.HasPrefix(data.TypeName, referenceContext) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// currently,does not support external references
|
||||||
|
item.InTypeName = data.TypeName
|
||||||
|
item.InType = strings.TrimPrefix(data.TypeName, flagStar)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results := v.Results
|
||||||
|
if results != nil {
|
||||||
|
outList, err := a.parseFields(results.List, true)
|
||||||
|
a.Must(err)
|
||||||
|
|
||||||
|
for _, data := range outList {
|
||||||
|
if strings.HasPrefix(data.TypeName, referenceContext) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// currently,does not support external references
|
||||||
|
item.OutTypeName = data.TypeName
|
||||||
|
item.OutType = strings.TrimPrefix(data.TypeName, flagStar)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
funcs = append(funcs, &item)
|
||||||
|
}
|
||||||
|
return funcs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) parseObject(structName string, tp *ast.StructType) (*Struct, error) {
|
||||||
|
if data, ok := objectM[structName]; ok {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
var st Struct
|
||||||
|
st.Name = stringx.From(structName)
|
||||||
|
if tp == nil {
|
||||||
|
return &st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := tp.Fields
|
||||||
|
if fields == nil {
|
||||||
|
objectM[structName] = &st
|
||||||
|
return &st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldList := fields.List
|
||||||
|
members, err := a.parseFields(fieldList, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range members {
|
||||||
|
var field Field
|
||||||
|
field.Name = m.Name
|
||||||
|
field.TypeName = m.TypeName
|
||||||
|
field.JsonTag = m.JsonTag
|
||||||
|
field.Document = m.Document
|
||||||
|
field.Comment = m.Comment
|
||||||
|
st.Field = append(st.Field, &field)
|
||||||
|
}
|
||||||
|
objectM[structName] = &st
|
||||||
|
return &st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) parseFields(fields []*ast.Field, onlyType bool) ([]*Field, error) {
|
||||||
|
ret := make([]*Field, 0)
|
||||||
|
for _, field := range fields {
|
||||||
|
var item Field
|
||||||
|
tag := a.parseTag(field.Tag)
|
||||||
|
if tag == "" && !onlyType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tag == ignoreJsonTagExpression {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
item.JsonTag = tag
|
||||||
|
name := a.parseName(field.Names)
|
||||||
|
if strings.HasPrefix(name, unknownPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.Name = stringx.From(name)
|
||||||
|
typeName, err := a.parseType(field.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item.TypeName = typeName
|
||||||
|
if onlyType {
|
||||||
|
ret = append(ret, &item)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
docs := a.parseCommentOrDoc(field.Doc)
|
||||||
|
comments := a.parseCommentOrDoc(field.Comment)
|
||||||
|
|
||||||
|
item.Document = docs
|
||||||
|
item.Comment = comments
|
||||||
|
|
||||||
|
isInline := name == ""
|
||||||
|
if isInline {
|
||||||
|
return nil, a.wrapError(field.Pos(), "unexpected inline type:%s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, &item)
|
||||||
|
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) parseTag(basicLit *ast.BasicLit) string {
|
||||||
|
if basicLit == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
value := basicLit.Value
|
||||||
|
splits := strings.Split(value, " ")
|
||||||
|
if len(splits) == 1 {
|
||||||
|
return fmt.Sprintf("`%s`", strings.ReplaceAll(splits[0], "`", ""))
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("`%s`", strings.ReplaceAll(splits[1], "`", ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns
|
||||||
|
// resp1:type's string expression,like int、string、[]int64、map[string]User、*User
|
||||||
|
// resp2:error
|
||||||
|
func (a *astParser) parseType(expr ast.Expr) (string, error) {
|
||||||
|
if expr == nil {
|
||||||
|
return "", errorParseError
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := expr.(type) {
|
||||||
|
case *ast.StarExpr:
|
||||||
|
stringExpr, err := a.parseType(v.X)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
e := fmt.Sprintf("*%s", stringExpr)
|
||||||
|
return e, nil
|
||||||
|
|
||||||
|
case *ast.Ident:
|
||||||
|
return a.mustGetIndentName(v), nil
|
||||||
|
case *ast.MapType:
|
||||||
|
keyStringExpr, err := a.parseType(v.Key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
valueStringExpr, err := a.parseType(v.Value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
e := fmt.Sprintf("map[%s]%s", keyStringExpr, valueStringExpr)
|
||||||
|
return e, nil
|
||||||
|
case *ast.ArrayType:
|
||||||
|
stringExpr, err := a.parseType(v.Elt)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
e := fmt.Sprintf("[]%s", stringExpr)
|
||||||
|
return e, nil
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
return "interface{}", nil
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
join := make([]string, 0)
|
||||||
|
xIdent, ok := v.X.(*ast.Ident)
|
||||||
|
xIndentName := a.mustGetIndentName(xIdent)
|
||||||
|
if ok {
|
||||||
|
join = append(join, xIndentName)
|
||||||
|
}
|
||||||
|
sel := v.Sel
|
||||||
|
join = append(join, a.mustGetIndentName(sel))
|
||||||
|
return strings.Join(join, "."), nil
|
||||||
|
case *ast.ChanType:
|
||||||
|
return "", a.wrapError(v.Pos(), "unexpected type 'chan'")
|
||||||
|
case *ast.FuncType:
|
||||||
|
return "", a.wrapError(v.Pos(), "unexpected type 'func'")
|
||||||
|
case *ast.StructType:
|
||||||
|
return "", a.wrapError(v.Pos(), "unexpected inline struct type")
|
||||||
|
default:
|
||||||
|
return "", a.wrapError(v.Pos(), "unexpected type '%v'", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (a *astParser) parseName(names []*ast.Ident) string {
|
||||||
|
if len(names) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
name := names[0]
|
||||||
|
return a.mustGetIndentName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) parseCommentOrDoc(cg *ast.CommentGroup) []string {
|
||||||
|
if cg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
comments := make([]string, 0)
|
||||||
|
for _, comment := range cg.List {
|
||||||
|
if comment == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
text := strings.TrimSpace(comment.Text)
|
||||||
|
if text == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
comments = append(comments, text)
|
||||||
|
}
|
||||||
|
return comments
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) mustGetIndentName(ident *ast.Ident) string {
|
||||||
|
if ident == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ident.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astParser) wrapError(pos token.Pos, format string, arg ...interface{}) error {
|
||||||
|
file := a.fileSet.Position(pos)
|
||||||
|
return fmt.Errorf("line %v: %s", file.Line, fmt.Sprintf(format, arg...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *PbAst) GenTypesCode() (string, error) {
|
||||||
|
types := make([]string, 0)
|
||||||
|
sts := make([]*Struct, 0)
|
||||||
|
for _, item := range a.Strcuts {
|
||||||
|
sts = append(sts, item)
|
||||||
|
}
|
||||||
|
sort.Slice(sts, func(i, j int) bool {
|
||||||
|
return sts[i].Name.Source() < sts[j].Name.Source()
|
||||||
|
})
|
||||||
|
for _, s := range sts {
|
||||||
|
structCode, err := s.genCode(false)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if structCode == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
types = append(types, structCode)
|
||||||
|
}
|
||||||
|
buffer, err := util.With("type").Parse(typeTemplate).Execute(map[string]interface{}{
|
||||||
|
"types": strings.Join(types, "\n"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Struct) genCode(containsTypeStatement bool) (string, error) {
|
||||||
|
if len(s.Field) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
fields := make([]string, 0)
|
||||||
|
for _, f := range s.Field {
|
||||||
|
var comment, doc string
|
||||||
|
if len(f.Comment) > 0 {
|
||||||
|
comment = f.Comment[0]
|
||||||
|
}
|
||||||
|
doc = strings.Join(f.Document, "\n")
|
||||||
|
buffer, err := util.With(sx.Rand()).Parse(fieldTemplate).Execute(map[string]interface{}{
|
||||||
|
"name": f.Name.Title(),
|
||||||
|
"type": f.TypeName,
|
||||||
|
"tag": f.JsonTag,
|
||||||
|
"hasDoc": len(f.Document) > 0,
|
||||||
|
"doc": doc,
|
||||||
|
"hasComment": len(f.Comment) > 0,
|
||||||
|
"comment": comment,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = append(fields, buffer.String())
|
||||||
|
}
|
||||||
|
buffer, err := util.With("struct").Parse(structTemplate).Execute(map[string]interface{}{
|
||||||
|
"type": containsTypeStatement,
|
||||||
|
"name": s.Name.Title(),
|
||||||
|
"fields": strings.Join(fields, "\n"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
var headTemplate = `// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// Source: {{.source}}`
|
||||||
|
|
||||||
|
func GetHead(source string) string {
|
||||||
|
buffer, _ := With("head").Parse(headTemplate).Execute(map[string]interface{}{
|
||||||
|
"source": source,
|
||||||
|
})
|
||||||
|
return buffer.String()
|
||||||
|
}
|
Loading…
Reference in New Issue