protoc-gen-coco/router_generate.go

506 lines
12 KiB
Go

package main
import (
"bytes"
"errors"
"fmt"
"os"
"strings"
"text/template"
"gitter.top/common/goast"
"google.golang.org/protobuf/compiler/protogen"
)
type generateRouterMethod struct {
MethodName string
InputName string
OutputName string
API string
Author string
Describe string
Method string
}
type generateRouters struct {
StructName string
BaseURL string
GenTo string
Methods []generateRouterMethod
}
const routerGenerateTpl = `type AutoGen{{.StructName}}Impl interface { ` + `{{range .Methods}}
{{.MethodName}}(ctx *coco.Context, req *{{.InputName}}) (resp *{{.OutputName}}, err error){{end}}
}
type {{.StructName}}_RouterMap struct {}
func (receiver *{{.StructName}}_RouterMap) GetRouterMap() *coco.Routers {
return &coco.Routers {
BaseURL: "{{.BaseURL}}",
Apis: coco.RouterMap{ {{range .Methods}}
"{{.MethodName}}": {
API: "{{.API}}",
Method: "{{.Method}}",
Author: "{{.Author}}",
Describe: "{{.Describe}}",
},{{end}}
},
}
}
`
func GenerateRouterMap(srv *protogen.Service) (string, error) {
var generate = new(generateRouters)
generate.StructName = srv.GoName
generate.BaseURL = GetCommentBaseURL(srv.Comments)
generate.GenTo = GetCommentGenerateTo(srv.Comments)
for _, method := range srv.Methods {
generate.Methods = append(generate.Methods, generateRouterMethod{
MethodName: method.GoName,
InputName: method.Input.GoIdent.GoName,
OutputName: method.Output.GoIdent.GoName,
API: GetCommentApiURL(method),
Author: GetCommentAuthor(method.Comments),
Describe: GetCommentDescribe(method.Comments),
Method: GetCommentHttpMethod(method.Comments),
})
}
tpl, err := template.New("group_router").Parse(routerGenerateTpl)
if err != nil {
return "", fmt.Errorf("parse %s group router template failed: %v", srv.GoName, err)
}
tpl.DefinedTemplates()
var buf bytes.Buffer
if err = tpl.Execute(&buf, generate); err != nil {
return "", fmt.Errorf("execute %s group router template failed: %v", srv.GoName, err)
}
return buf.String(), nil
}
type rpcInfo struct {
FuncName string // 函数名
RouterPath string // 路由路径
Method string // 请求类型 POST/GET...
Author string // 接口作者
Describe string // 描述
ReqName string // request name
RespName string // response name
}
type routerImpl struct {
Filename string
PkgName string
SrvName string
GoPkgName string
ProjectName string
Version string
Routers []*rpcInfo
}
// newRouterImpl 生成路由信息
// filename 文件生成地址
// pkgName 文件包名
// srvName 被控制的service名称
// service service信息
func newRouterImpl(filename, pkgName, goPkgName, srvName string, service *protogen.Service) *routerImpl {
if filename == "" {
filename = "./"
}
return &routerImpl{
Filename: filename,
SrvName: srvName,
PkgName: pkgName,
GoPkgName: goPkgName,
Routers: GetRPCInfoList(service),
}
}
func newRouterImplWire(filename, pkgName, goPkgName, srvName string) *routerImpl {
return &routerImpl{
Filename: filename,
SrvName: srvName,
PkgName: pkgName,
GoPkgName: goPkgName,
}
}
// fixPkgName 修正不规范的包名
func fixPkgName(name string) string {
name = strings.ReplaceAll(name, "-", "_")
name = strings.ReplaceAll(name, ".", "_")
return name
}
func (ri *routerImpl) IsFileExist() bool {
_, err := os.Stat(ri.Filename)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return false
} else {
_, _ = fmt.Fprintf(os.Stderr, "get file stat failed: %v", err)
}
return false
}
return true
}
func (ri *routerImpl) GetFileInfo(filename string) (os.FileInfo, error) {
return os.Stat(filename)
}
const CompleteRouteGenerateAndPackageTpl = `package {{.PkgName}}
import (
"gitter.top/coco/coco"
{{$.GoPkgName}} "{{.ProjectName}}/gen/v1"
)
type {{.SrvName}} struct {}
// IDE: {{.SrvName}} implemented {{.GoPkgName}}.AutoGen{{.SrvName}}Impl interface
var _ {{.GoPkgName}}.AutoGen{{.SrvName}}Impl = (*{{.SrvName}})(nil)
{{range $route := .Routers}}
// {{$route.FuncName}} {{$route.Describe}}
func (receiver *{{$.SrvName}}) {{$route.FuncName}}(ctx *coco.Context, req *{{$.GoPkgName}}.{{$route.ReqName}}) (resp *{{$.GoPkgName}}.{{$route.RespName}}, err error) {
resp = new({{$.GoPkgName}}.{{$route.RespName}})
// TODO impl...
return resp, nil
}
{{end}}
`
func (ri *routerImpl) generateNewFile() error {
t, err := template.New("generate_new_file").Parse(CompleteRouteGenerateAndPackageTpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
return nil
}
const CompleteGrpcGenerateAndPackageTpl = `package {{.PkgName}}
import (
"context"
{{.GoPkgName}} "{{.ProjectName}}/gen/v1"
)
type {{.SrvName}} struct {}
// IDE: {{.SrvName}} implemented {{.GoPkgName}}.{{.SrvName}}Server interface
var _ {{.GoPkgName}}.{{.SrvName}}Server = (*{{.SrvName}})(nil)
{{range $route := .Routers}}
// {{$route.FuncName}} {{$route.Describe}}
func (receiver *{{$.SrvName}}) {{$route.FuncName}}(ctx context.Context, req *{{$.GoPkgName}}.{{$route.ReqName}}) (resp *{{$.GoPkgName}}.{{$route.RespName}}, err error) {
resp = new({{$.GoPkgName}}.{{$route.RespName}})
// TODO impl...
return resp, nil
}
{{end}}
`
func (ri *routerImpl) generateNewGrpcFile() error {
t, err := template.New("generate_new_grpc_file").Parse(CompleteGrpcGenerateAndPackageTpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
return nil
}
func (ri *routerImpl) generateNewRouters() error {
info, err := ri.GetFileInfo(ri.Filename)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
parser, err := goast.NewParser(ri.Filename)
if err != nil {
return err
}
definer, err := parser.Parse()
if err != nil {
return err
}
if !definer.ExistType(ri.SrvName) {
tpl := `
type {{.SrvName}} struct {}
// IDE: {{.SrvName}} implemented {{.GoPkgName}}.AutoGen{{.SrvName}}Impl interface
var _ {{.GoPkgName}}.AutoGen{{.SrvName}}Impl = (*{{.SrvName}})(nil)
`
t, err := template.New("generate_type").Parse(tpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
}
var needImpls []*rpcInfo
for _, router := range ri.Routers {
if !definer.ExistMethod(fmt.Sprintf("%s.%s", ri.SrvName, router.FuncName)) {
needImpls = append(needImpls, router)
}
}
if len(needImpls) != 0 {
tpl := `{{range $route := .Routers}}
// {{$route.FuncName}} {{$route.Describe}}
func (receiver *{{$.SrvName}}) {{$route.FuncName}}(ctx *coco.Context, req *{{$.GoPkgName}}.{{$route.ReqName}}) (resp *{{$.GoPkgName}}.{{$route.RespName}}, err error) {
resp = new({{$.GoPkgName}}.{{$route.RespName}})
// TODO impl...
return resp, nil
}
{{end}}`
ri.Routers = needImpls
t, err := template.New("generate_impls").Parse(tpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
}
return nil
}
func (ri *routerImpl) generateNewGrpcServers() error {
info, err := ri.GetFileInfo(ri.Filename)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
parser, err := goast.NewParser(ri.Filename)
if err != nil {
return err
}
definer, err := parser.Parse()
if err != nil {
return err
}
if !definer.ExistType(ri.SrvName) {
tpl := `
type {{.SrvName}} struct {}
// IDE: {{.SrvName}} implemented {{.GoPkgName}}.{{.SrvName}}Server interface
var _ {{.GoPkgName}}.{{.SrvName}}Server = (*{{.SrvName}})(nil)
`
t, err := template.New("generate_type").Parse(tpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
}
var needImpls []*rpcInfo
for _, router := range ri.Routers {
if !definer.ExistMethod(fmt.Sprintf("%s.%s", ri.SrvName, router.FuncName)) {
needImpls = append(needImpls, router)
}
}
if len(needImpls) != 0 {
tpl := `{{range $route := .Routers}}
// {{$route.FuncName}} {{$route.Describe}}
func (receiver *{{$.SrvName}}) {{$route.FuncName}}(ctx context.Context, req *{{$.GoPkgName}}.{{$route.ReqName}}) (resp *{{$.GoPkgName}}.{{$route.RespName}}, err error) {
resp = new({{$.GoPkgName}}.{{$route.RespName}})
// TODO impl...
return resp, nil
}
{{end}}`
ri.Routers = needImpls
t, err := template.New("generate_impls").Parse(tpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
}
return nil
}
const routeWireCompleteTpl = `//go:build wireinject
// +build wireinject
package wire
import (
"github.com/google/wire"
"{{.ProjectName}}/gen/{{.Version}}"
"{{.ProjectName}}/services/controller/{{.Version}}"
)
`
func (ri *routerImpl) generateNewWireFile() error {
t, err := template.New("generate_new_wire").Parse(routeWireCompleteTpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
return nil
}
const routeWireTpl = `type {{.SrvName}} struct {
*controller{{.Version}}.{{.SrvName}}
*gen{{.Version}}.{{.SrvName}}_RouterMap
}
func New{{.SrvName}}(c *controller{{.Version}}.{{.SrvName}}, r *gen{{.Version}}.{{.SrvName}}_RouterMap) *{{.SrvName}} {
return &{{.SrvName}}{c, r}
}
func Init{{.SrvName}}() *{{.SrvName}} {
wire.Build(wire.Struct(new(controller{{.Version}}.{{.SrvName}})), wire.Struct(new(gen{{.Version}}.{{.SrvName}}_RouterMap)), New{{.SrvName}})
return &{{.SrvName}}{}
}
`
func (ri *routerImpl) generateServicesWire() error {
info, err := ri.GetFileInfo(ri.Filename)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
parser, err := goast.NewParser(ri.Filename)
if err != nil {
return err
}
definer, err := parser.Parse()
if err != nil {
return err
}
if definer.ExistType(ri.SrvName) {
return nil
}
t, err := template.New("generate_router_wire").Parse(routeWireTpl)
if err != nil {
return err
}
t.DefinedTemplates()
var buf bytes.Buffer
if err = t.Execute(&buf, ri); err != nil {
return err
}
f, err := os.OpenFile(ri.Filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(buf.Bytes()); err != nil {
return err
}
return nil
}