193 lines
4.6 KiB
Go
193 lines
4.6 KiB
Go
// 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")
|
|
}
|