Compare commits

...

16 Commits
v0.0.1 ... main

Author SHA1 Message Date
yanweidong cf1e0d30eb fix grpc client 2025-10-06 23:02:44 +08:00
yanweidong 2313df0bca fix bug 2025-10-05 20:32:49 +08:00
yanweidong ac88ca13eb fix bug 2025-10-05 20:29:32 +08:00
yanweidong 45079048af ```
feat(core): 添加 vars 包导入以支持状态回复功能

在生成逻辑文件时,为处理 StatusReply 类型的输出,
添加了对 "git.apinb.com/bsm-sdk/core/vars" 包的导入。
同时调整了模板中导入语句的顺序和格式。
```
2025-09-21 16:58:29 +08:00
yanweidong 15caac2a20 ```
feat(logic): 更新状态回复代码逻辑

将状态回复中的 Code 和 Message 字段替换为使用 vars.OK 常量,
以统一响应格式并提高代码可维护性。同时引入 vars 包的依赖。
```
2025-09-21 16:52:11 +08:00
yanweidong 8c12160173 fix mode 2025-09-06 20:51:24 +08:00
yanweidong 0227a0c384 Merge branch 'main' of https://git.apinb.com/bsm-tools/protoc-gen-slc 2025-09-06 20:47:06 +08:00
yanweidong c121ae0bca update go.mod 2025-09-06 20:46:53 +08:00
yanweidong 9943ed0e6e fix server name caml 2025-05-01 18:47:38 +08:00
yanweidong fa9ceaddc6 fix logic name caml 2025-05-01 18:42:50 +08:00
yanweidong 87734c972f 更新 tpl/logic.go 2025-04-18 12:14:38 +08:00
yanweidong 3b283780ab 更新 tpl/server.go 2025-04-17 23:24:09 +08:00
yanweidong cf7e34c61d 更新 tpl/server.go 2025-04-17 23:16:13 +08:00
yanweidong 9c3ac59467 更新 tpl/logic.go 2025-04-17 22:02:56 +08:00
yanweidong 689afaff97 更新 tpl/logic.go 2025-04-17 16:35:07 +08:00
david.yan dd8913297e fix mod 2025-04-10 17:25:23 +08:00
5 changed files with 189 additions and 65 deletions

23
go.mod
View File

@ -1,20 +1,19 @@
module protoc-gen-slc
module git.apinb.com/bsm-tools/protoc-gen-slc
go 1.24.0
go 1.25.1
require (
git.apinb.com/bsm-sdk/core v0.0.29
golang.org/x/mod v0.24.0
google.golang.org/grpc v1.71.0
google.golang.org/protobuf v1.36.6
git.apinb.com/bsm-sdk/core v0.0.94
golang.org/x/mod v0.28.0
google.golang.org/grpc v1.75.1
google.golang.org/protobuf v1.36.10
)
require (
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
github.com/oklog/ulid/v2 v2.1.1 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect
)

62
go.sum
View File

@ -1,7 +1,7 @@
git.apinb.com/bsm-sdk/core v0.0.29 h1:7ibdZw5doJ0AP+7+5G8/HjZysD1frEapHxtemJySfGs=
git.apinb.com/bsm-sdk/core v0.0.29/go.mod h1:FY2knuEVN7d7eHhpkyI+Cj/4oJ34gqZ8xzE7hB3JkOE=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
git.apinb.com/bsm-sdk/core v0.0.94 h1:yT6VoP76ES+UZTEAysiuNmkVcA9UHU7NlvRE5k/jyKg=
git.apinb.com/bsm-sdk/core v0.0.94/go.mod h1:rCmMma8R2pvByImgoZDm2OPLdr+IUNr7LBPyayb8aN0=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@ -10,32 +10,34 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=

96
main.go
View File

@ -1,15 +1,18 @@
package main
import (
"bytes"
"errors"
"fmt"
"go/format"
"io"
"os"
"path/filepath"
"protoc-gen-slc/tpl"
"regexp"
"strings"
"unicode"
"git.apinb.com/bsm-tools/protoc-gen-slc/tpl"
"git.apinb.com/bsm-sdk/core/utils"
"golang.org/x/mod/modfile"
@ -24,15 +27,15 @@ func main() {
gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
if !utils.PathExists("./internal") {
os.MkdirAll("./internal", 777)
os.MkdirAll("./internal", 0755)
}
if !utils.PathExists("./internal/server") {
os.MkdirAll("./internal/server", 777)
os.MkdirAll("./internal/server", 0755)
}
if !utils.PathExists("./internal/logic") {
os.MkdirAll("./internal/logic", 777)
os.MkdirAll("./internal/logic", 0755)
}
for _, f := range gen.Files {
@ -91,7 +94,8 @@ func generateNewServerFile(services []string) error {
// register grpc gw
var gw []string
for _, service := range services {
gw = append(gw, "pb.Register"+service+"HandlerFromEndpoint(srv.Ctx, srv.Mux, addr, opts)")
handler := strings.ReplaceAll(tpl.Handler, "{service}", service)
gw = append(gw, handler)
}
code = strings.ReplaceAll(code, "{gw}", strings.Join(gw, "\n"))
@ -107,13 +111,13 @@ func generateNewServerFile(services []string) error {
}
func generateServerFile(gen *protogen.Plugin, file *protogen.File, service *protogen.Service) error {
filename := fmt.Sprintf("./internal/server/%s_server.go", strings.ToLower(service.GoName))
filename := fmt.Sprintf("./internal/server/%s_server.go", toSnakeCase(service.GoName))
moduleName := getModuleName()
//create servers.
code := tpl.Server
imports := []string{
"\"" + moduleName + "/internal/logic/" + strings.ToLower(service.GoName) + "\"",
"\"" + moduleName + "/internal/logic/" + toSnakeCase(service.GoName) + "\"",
"pb \"" + moduleName + "/pb\"",
}
@ -145,14 +149,8 @@ func generateServerFile(gen *protogen.Plugin, file *protogen.File, service *prot
return nil
}
func generateClientFile(gen *protogen.Plugin, file *protogen.File, service *protogen.Service) error {
filename := fmt.Sprintf("%s_client.pb.go", strings.ToLower(service.GoName))
fmt.Println(filename, file.GoImportPath)
return nil
}
func generateLogicFile(gen *protogen.Plugin, file *protogen.File, service *protogen.Service) error {
logicPath := "./internal/logic/" + strings.ToLower(service.GoName)
logicPath := "./internal/logic/" + toSnakeCase(service.GoName)
if !utils.PathExists(logicPath) {
os.MkdirAll(logicPath, os.ModePerm)
}
@ -169,6 +167,27 @@ func generateLogicFile(gen *protogen.Plugin, file *protogen.File, service *proto
"pb \"" + moduleName + "/pb\"",
}
if strings.ToLower(method.Input.GoIdent.GoName) == "identrequest" || strings.ToLower(method.Input.GoIdent.GoName) == "fetchrequest" {
if strings.ToLower(method.Input.GoIdent.GoName) == "identrequest" {
imports = append(imports, "\"git.apinb.com/bsm-sdk/core/errcode\"")
code = strings.ReplaceAll(code, "{valid}", tpl.ValidCode)
}
if strings.ToLower(method.Input.GoIdent.GoName) == "fetchrequest" {
code = strings.ReplaceAll(code, "{valid}", tpl.FetchValidCode)
}
} else {
code = strings.ReplaceAll(code, "{valid}", "// TODO: valid code")
}
if strings.ToLower(method.Output.GoIdent.GoName) == "statusreply" {
imports = append(imports, "\"time\"")
imports = append(imports, "\"git.apinb.com/bsm-sdk/core/vars\"")
code = strings.ReplaceAll(code, "{return}", tpl.StatusReplyCode)
} else {
code = strings.ReplaceAll(code, "{return}", "return ")
}
code = strings.ReplaceAll(code, "{import}", strings.Join(imports, "\n"))
commit := strings.TrimSpace(method.Comments.Leading.String())
code = strings.ReplaceAll(code, "{func}", method.GoName)
@ -176,13 +195,12 @@ func generateLogicFile(gen *protogen.Plugin, file *protogen.File, service *proto
code = strings.ReplaceAll(code, "{input}", method.Input.GoIdent.GoName)
code = strings.ReplaceAll(code, "{output}", method.Output.GoIdent.GoName)
// formattedCode, err := format.Source([]byte(code))
// if err != nil {
// return fmt.Errorf("failed to format generated code: %w", err)
// }
formattedCode, err := format.Source([]byte(code))
if err != nil {
return fmt.Errorf("failed to format generated code: %w", err)
}
// StringToFile(filename, string(formattedCode))
StringToFile(filename, code)
StringToFile(filename, string(formattedCode))
}
return nil
@ -250,3 +268,41 @@ func StringToFile(path, content string) error {
}
return nil
}
// parseOptions 解析注释中的选项字符串并返回一个 map
func parseOptions(comment string) map[string]string {
// 去掉注释符号和分号
comment = strings.Trim(comment, " //;")
// 按逗号分割选项
options := strings.Split(comment, ",")
result := make(map[string]string)
for _, opt := range options {
// 按等号分割键和值
parts := strings.SplitN(opt, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
result[key] = value
}
}
return result
}
// CamelToSnake 将驼峰命名转换为下划线命名
func CamelToSnake(s string) string {
var buf bytes.Buffer
for i, r := range s {
if unicode.IsUpper(r) {
// 如果不是第一个字符,添加下划线
if i > 0 {
buf.WriteRune('_')
}
buf.WriteRune(unicode.ToLower(r))
} else {
buf.WriteRune(r)
}
}
return buf.String()
}

View File

@ -4,6 +4,7 @@ var LogicFile = `package {methodName}
import (
"context"
{import}
"git.apinb.com/bsm-sdk/core/service"
)
@ -15,9 +16,37 @@ func {func}(ctx context.Context, in *pb.{input}) (reply *pb.{output},err error)
if err != nil {
return nil, err
}
{valid}
// TODO: add your logic code & delete this line.
return
{return}
}
`
var CreateCode = `
`
var ValidCode = `
// valildate request id,identity.
if in.Id == 0 && in.Identity == "" {
return nil, errcode.ErrInvalidArgument
}
`
var FetchValidCode = `
// valildate request page_no,page_size.
if in.GetPageNo() < 1 {
in.PageNo = 1
}
if in.GetPageSize() < 10 {
in.PageSize = 50
}
`
var StatusReplyCode = `
return &pb.StatusReply{
Data: vars.OK,
Timeseq: time.Now().UnixMilli(),
}, nil
`

View File

@ -9,10 +9,13 @@ import (
"net/http"
"os"
"strings"
"time"
{import}
"git.apinb.com/bsm-sdk/core/vars"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
"google.golang.org/protobuf/proto"
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
)
@ -20,18 +23,34 @@ type Server struct {
Grpc *grpc.Server
Ctx context.Context
Mux *gwRuntime.ServeMux
grpcConns map[string]*grpc.ClientConn // 连接池
}
func New(addr string) *Server {
srv := &Server{Ctx: context.Background(), Grpc: grpc.NewServer(), Mux: gwRuntime.NewServeMux()}
srv := &Server{
Ctx: context.Background(),
Grpc: grpc.NewServer(),
Mux: gwRuntime.NewServeMux(gwRuntime.WithForwardResponseRewriter(responseEnvelope)),
grpcConns: make(map[string]*grpc.ClientConn),
}
// register service to grpc.Server
{register}
reflection.Register(srv.Grpc)
// 连接池: 只创建一次连接并复用
conn, ok := srv.grpcConns[addr]
if !ok {
var err error
conn, err = grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
panic("failed to dial grpc server: " + err.Error())
}
srv.grpcConns[addr] = conn
}
// 将服务注册到Gateway
opts := []grpc.DialOption{grpc.WithInsecure()}
{gw}
// Register services swagger
@ -58,6 +77,20 @@ func (s *Server) RegisterSwagger() {
})
}
// response envelope
func responseEnvelope(_ context.Context, response proto.Message) (interface{}, error) {
name := string(response.ProtoReflect().Descriptor().Name())
if name == "Status" || name == "Error" || name == "StatusReply" {
return response, nil
}
return map[string]any{
"code": 0,
"message": vars.OK,
"details": response,
"timeseq": time.Now().Unix(),
}, nil
}
`
var Server = `
@ -85,3 +118,8 @@ func (s *{service}Server) {func}(ctx context.Context,in *pb.{input}) (*pb.{outpu
return {serviceLower}.{func}(ctx, in)
}
`
var Handler = `
if err := pb.Register{service}Handler(srv.Ctx, srv.Mux, conn); err != nil {
panic("Failed to register {service} handler: " + err.Error())
}
`