gencall.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. package gen
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
  9. "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
  10. "github.com/tal-tech/go-zero/tools/goctl/util"
  11. )
  12. const (
  13. callTemplateText = `{{.head}}
  14. //go:generate mockgen -destination ./{{.name}}_mock.go -package {{.filePackage}} -source $GOFILE
  15. package {{.filePackage}}
  16. import (
  17. "context"
  18. {{.package}}
  19. "github.com/tal-tech/go-zero/core/jsonx"
  20. "github.com/tal-tech/go-zero/rpcx"
  21. )
  22. type (
  23. {{.serviceName}} interface {
  24. {{.interface}}
  25. }
  26. default{{.serviceName}} struct {
  27. cli rpcx.Client
  28. }
  29. )
  30. func New{{.serviceName}}(cli rpcx.Client) {{.serviceName}} {
  31. return &default{{.serviceName}}{
  32. cli: cli,
  33. }
  34. }
  35. {{.functions}}
  36. `
  37. callTemplateTypes = `{{.head}}
  38. package {{.filePackage}}
  39. import "errors"
  40. var errJsonConvert = errors.New("json convert error")
  41. {{.types}}
  42. `
  43. callInterfaceFunctionTemplate = `{{if .hasComment}}{{.comment}}
  44. {{end}}{{.method}}(ctx context.Context,in *{{.pbRequest}}) {{if .hasResponse}}(*{{.pbResponse}},{{end}} error{{if .hasResponse}}){{end}}`
  45. callFunctionTemplate = `
  46. {{if .hasComment}}{{.comment}}{{end}}
  47. func (m *default{{.rpcServiceName}}) {{.method}}(ctx context.Context,in *{{.pbRequest}}) {{if .hasResponse}}(*{{.pbResponse}},{{end}} error{{if .hasResponse}}){{end}} {
  48. var request {{.package}}.{{.pbRequest}}
  49. bts, err := jsonx.Marshal(in)
  50. if err != nil {
  51. return {{if .hasResponse}}nil, {{end}}errJsonConvert
  52. }
  53. err = jsonx.Unmarshal(bts, &request)
  54. if err != nil {
  55. return {{if .hasResponse}}nil, {{end}}errJsonConvert
  56. }
  57. client := {{.package}}.New{{.rpcServiceName}}Client(m.cli.Conn())
  58. {{if .hasResponse}}resp, err := {{else}}_, err = {{end}}client.{{.method}}(ctx, &request)
  59. {{if .hasResponse}}if err != nil{
  60. return nil, err
  61. }
  62. var ret {{.pbResponse}}
  63. bts, err = jsonx.Marshal(resp)
  64. if err != nil{
  65. return nil, errJsonConvert
  66. }
  67. err = jsonx.Unmarshal(bts, &ret)
  68. if err != nil{
  69. return nil, errJsonConvert
  70. }
  71. return &ret, nil{{else}}if err != nil {
  72. return err
  73. }
  74. return nil{{end}}
  75. }
  76. `
  77. )
  78. func (g *defaultRpcGenerator) genCall() error {
  79. file := g.ast
  80. if len(file.Service) == 0 {
  81. return nil
  82. }
  83. if len(file.Service) > 1 {
  84. return fmt.Errorf("we recommend only one service in a proto, currently %d", len(file.Service))
  85. }
  86. typeCode, err := file.GenTypesCode()
  87. if err != nil {
  88. return err
  89. }
  90. service := file.Service[0]
  91. callPath := filepath.Join(g.dirM[dirTarget], service.Name.Lower())
  92. if err = util.MkdirIfNotExist(callPath); err != nil {
  93. return err
  94. }
  95. pbPkg := file.Package
  96. remotePackage := fmt.Sprintf(`%v "%v"`, pbPkg, g.mustGetPackage(dirPb))
  97. filename := filepath.Join(callPath, "types.go")
  98. head := util.GetHead(g.Ctx.ProtoSource)
  99. err = util.With("types").GoFmt(true).Parse(callTemplateTypes).SaveTo(map[string]interface{}{
  100. "head": head,
  101. "filePackage": service.Name.Lower(),
  102. "pbPkg": pbPkg,
  103. "serviceName": g.Ctx.ServiceName.Title(),
  104. "lowerStartServiceName": g.Ctx.ServiceName.UnTitle(),
  105. "types": typeCode,
  106. }, filename, true)
  107. if err != nil {
  108. return err
  109. }
  110. _, err = exec.LookPath("mockgen")
  111. mockGenInstalled := err == nil
  112. filename = filepath.Join(callPath, fmt.Sprintf("%s.go", service.Name.Lower()))
  113. functions, err := g.getFuncs(service)
  114. if err != nil {
  115. return err
  116. }
  117. iFunctions, err := g.getInterfaceFuncs(service)
  118. if err != nil {
  119. return err
  120. }
  121. mockFile := filepath.Join(callPath, fmt.Sprintf("%s_mock.go", service.Name.Lower()))
  122. _ = os.Remove(mockFile)
  123. err = util.With("shared").GoFmt(true).Parse(callTemplateText).SaveTo(map[string]interface{}{
  124. "name": service.Name.Lower(),
  125. "head": head,
  126. "filePackage": service.Name.Lower(),
  127. "pbPkg": pbPkg,
  128. "package": remotePackage,
  129. "serviceName": service.Name.Title(),
  130. "functions": strings.Join(functions, "\n"),
  131. "interface": strings.Join(iFunctions, "\n"),
  132. }, filename, true)
  133. if err != nil {
  134. return err
  135. }
  136. // if mockgen is already installed, it will generate code of gomock for shared files
  137. // Deprecated: it will be removed
  138. if mockGenInstalled && g.Ctx.IsInGoEnv {
  139. _, _ = execx.Run(fmt.Sprintf("go generate %s", filename), "")
  140. }
  141. return nil
  142. }
  143. func (g *defaultRpcGenerator) getFuncs(service *parser.RpcService) ([]string, error) {
  144. file := g.ast
  145. pkgName := file.Package
  146. functions := make([]string, 0)
  147. for _, method := range service.Funcs {
  148. data, found := file.Strcuts[strings.ToLower(method.OutType)]
  149. if found {
  150. found = len(data.Field) > 0
  151. }
  152. var comment string
  153. if len(method.Document) > 0 {
  154. comment = method.Document[0]
  155. }
  156. buffer, err := util.With("sharedFn").Parse(callFunctionTemplate).Execute(map[string]interface{}{
  157. "rpcServiceName": service.Name.Title(),
  158. "method": method.Name.Title(),
  159. "package": pkgName,
  160. "pbRequest": method.InType,
  161. "pbResponse": method.OutType,
  162. "hasResponse": found,
  163. "hasComment": len(method.Document) > 0,
  164. "comment": comment,
  165. })
  166. if err != nil {
  167. return nil, err
  168. }
  169. functions = append(functions, buffer.String())
  170. }
  171. return functions, nil
  172. }
  173. func (g *defaultRpcGenerator) getInterfaceFuncs(service *parser.RpcService) ([]string, error) {
  174. file := g.ast
  175. functions := make([]string, 0)
  176. for _, method := range service.Funcs {
  177. data, found := file.Strcuts[strings.ToLower(method.OutType)]
  178. if found {
  179. found = len(data.Field) > 0
  180. }
  181. var comment string
  182. if len(method.Document) > 0 {
  183. comment = method.Document[0]
  184. }
  185. buffer, err := util.With("interfaceFn").Parse(callInterfaceFunctionTemplate).Execute(
  186. map[string]interface{}{
  187. "hasComment": len(method.Document) > 0,
  188. "comment": comment,
  189. "method": method.Name.Title(),
  190. "pbRequest": method.InType,
  191. "pbResponse": method.OutType,
  192. "hasResponse": found,
  193. })
  194. if err != nil {
  195. return nil, err
  196. }
  197. functions = append(functions, buffer.String())
  198. }
  199. return functions, nil
  200. }