go-common/app/tool/gorpc/main.go

193 lines
4.6 KiB
Go
Raw Normal View History

2019-04-22 10:49:16 +00:00
// A commandline tool for generating rpc code by service methods.
//
// This tool can generate rpc client code ,rpc arg model for specific Go project dir.
// Usage :
// $gorpc [options]
// Available options:
//
// -client generate rpc client code.
//
// -d specific go project service dir (default ./service/)
//
// -model generate rpc arg model code.
//
// -o specific rpc client code output file. (default ./rpc/client/client.go)
//
// -m specific rpc arg model output file. (default ./model/rpc.go)
//
// -s print code to stdout.
// Example:
// $ cd $GOPATH/relation
// $ gorpc
// such command will generate rpc client code by functions of ./service/* and write code to $GOPATH/relation/rpc/client/client.go
package main
import (
"bytes"
"flag"
"fmt"
"go/importer"
"io"
"io/ioutil"
"log"
"path"
"go-common/app/tool/gorpc/goparser"
"go-common/app/tool/gorpc/input"
"go-common/app/tool/gorpc/model"
"golang.org/x/tools/imports"
)
const (
tpl = `var (
_noRes = &struct{}{}
)
type Service struct {
client *rpc.Client2
}
func New(c *rpc.ClientConfig) (s *Service) {
s = &Service{}
s.client = rpc.NewDiscoveryCli(c)
return
}`
)
type output struct {
rpcClient *bytes.Buffer
methodStr *bytes.Buffer
methods *bytes.Buffer
model *bytes.Buffer
}
var out = &output{
rpcClient: new(bytes.Buffer),
methodStr: new(bytes.Buffer),
methods: new(bytes.Buffer),
model: new(bytes.Buffer),
}
var (
dir = flag.String("d", "./service/", "source code dir")
argModel = flag.Bool("model", false, "use -model to generate rpc arg model")
client = flag.Bool("client", true, "use -client to generate rpc client code")
clientfile = flag.String("o", "./rpc/client/client.go", "out file name")
modelfile = flag.String("m", "./model/rpc.go", "generate rpc arg model")
std = flag.Bool("s", false, "use -s to print code to stdout")
)
func main() {
flag.Parse()
p := &goparser.Parser{Importer: importer.Default()}
files, err := input.Files(path.Dir(*dir))
if err != nil {
panic(err)
}
if *argModel {
out.model.WriteString("package model\n")
}
for _, f := range files {
rs, err := p.Parse(string(f), files)
if err != nil {
panic(err)
}
for _, f := range rs.Funcs {
if f.IsExported && len(f.Results) <= 1 && f.Receiver != nil && f.ReturnsError {
if *client {
out.generateMeStr(f)
out.generateMethod(f)
}
if *argModel {
out.generateModel(f)
}
}
}
}
if *argModel {
m, err := imports.Process("model.go", out.model.Bytes(), &imports.Options{TabWidth: 4})
if err != nil {
log.Printf("gopimorts err %v", err)
return
}
if *std {
fmt.Printf("%s", m)
} else {
ioutil.WriteFile(*modelfile, m, 0666)
}
}
if *client {
out.rpcClient.WriteString("package client \n")
out.rpcClient.WriteString(tpl)
out.rpcClient.WriteString("\nconst ( \n")
io.Copy(out.rpcClient, out.methodStr)
out.rpcClient.WriteString("\n)\n")
io.Copy(out.rpcClient, out.methods)
c, err := imports.Process("client.go", out.rpcClient.Bytes(), &imports.Options{TabWidth: 4})
if err != nil {
log.Printf("gopimorts err %v", err)
return
}
if *std {
fmt.Printf("%s", c)
} else {
ioutil.WriteFile(*clientfile, c, 0666)
}
}
}
func (o *output) generateMeStr(f *model.Function) {
b := o.methodStr
fmt.Fprintf(b, "_%s = \"RPC.%s\"\n", f.Name, f.Name)
}
func (o *output) generateModel(f *model.Function) {
b := o.model
for _, p := range f.Parameters {
if !p.IsBasicType() {
if p.Type.String() == "context.Context" {
continue
}
return
}
}
fmt.Fprintf(b, fmt.Sprintf("type Arg%s struct{\n", f.Name))
for _, p := range f.Parameters {
fmt.Fprintln(b, string(bytes.ToUpper([]byte(p.Name))), p.Type)
}
fmt.Fprintln(b, "}")
}
func (o *output) generateMethod(f *model.Function) {
b := o.methods
name := f.Name
for _, p := range f.Parameters {
if !p.IsBasicType() {
if p.Type.String() == "context.Context" {
continue
}
return
}
}
fmt.Fprintf(b, "func(s %s)%s(", f.Receiver.Type, f.Name)
fmt.Fprintf(b, "c context.Context, arg *model.Arg%s)(", f.Name)
if f.ReturnsError {
if len(f.Results) > 0 {
fmt.Fprintf(b, "res %s", f.Results[0].Type)
fmt.Fprint(b, ",")
}
fmt.Fprintf(b, "err error)")
} else {
fmt.Fprint(b, ")")
}
fmt.Fprint(b, " {\n")
if len(f.Results) == 0 {
fmt.Fprintf(b, "err = s.client.Call(c, _%s, arg, &_noRes)", name)
} else if f.Results[0].Type.IsStar {
fmt.Fprintf(b, "res = new(%s)\n", f.Results[0].Type.String()[1:])
fmt.Fprintf(b, "err = s.client.Call(c, _%s, arg, res)", name)
} else {
fmt.Fprintf(b, "err = s.client.Call(c, _%s, arg, &res)", name)
}
fmt.Fprintf(b, "\nreturn\n}\n")
}