1 // Copyright (c) 2020 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
21 "github.com/sirupsen/logrus"
25 RegisterPlugin("rpc", GenerateRPC)
28 // library dependencies
30 contextPkg = GoImportPath("context")
31 ioPkg = GoImportPath("io")
36 serviceApiName = "RPCService" // name for the RPC service interface
37 serviceImplName = "serviceClient" // name for the RPC service implementation
38 serviceClientName = "ServiceClient" // name for the RPC service client
40 // TODO: register service descriptor
41 //serviceDescType = "ServiceDesc" // name for service descriptor type
42 //serviceDescName = "_ServiceRPC_serviceDesc" // name for service descriptor var
45 func GenerateRPC(gen *Generator, file *File) *GenFile {
46 if file.Service == nil {
50 logf("----------------------------")
51 logf(" Generate RPC - %s", file.Desc.Name)
52 logf("----------------------------")
54 filename := path.Join(file.FilenamePrefix, file.Desc.Name+"_rpc.ba.go")
55 g := gen.NewGenFile(filename, file.GoImportPath)
58 // generate file header
59 g.P("// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
61 g.P("package ", file.PackageName)
64 // generate RPC service
65 if len(file.Service.RPCs) > 0 {
66 genService(g, file.Service)
72 func genService(g *GenFile, svc *Service) {
74 g.P("// ", serviceApiName, " defines RPC service ", g.file.Desc.Name, ".")
76 // generate service interface
77 g.P("type ", serviceApiName, " interface {")
78 for _, rpc := range svc.RPCs {
79 g.P(rpcMethodSignature(g, rpc))
84 // generate client implementation
85 g.P("type ", serviceImplName, " struct {")
86 g.P("conn ", govppApiPkg.Ident("Connection"))
90 // generate client constructor
91 g.P("func New", serviceClientName, "(conn ", govppApiPkg.Ident("Connection"), ") ", serviceApiName, " {")
92 g.P("return &", serviceImplName, "{conn}")
96 msgControlPingReply, ok := g.gen.messagesByName["control_ping_reply"]
98 logrus.Fatalf("no message for %v", "control_ping_reply")
100 msgControlPing, ok := g.gen.messagesByName["control_ping"]
102 logrus.Fatalf("no message for %v", "control_ping")
105 for _, rpc := range svc.RPCs {
106 logf(" gen RPC: %v (%s)", rpc.GoName, rpc.VPP.Request)
108 g.P("func (c *", serviceImplName, ") ", rpcMethodSignature(g, rpc), " {")
110 streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, rpc.GoName)
111 streamApi := fmt.Sprintf("%s_%sClient", serviceApiName, rpc.GoName)
113 msgDetails := rpc.MsgReply
114 var msgReply *Message
115 if rpc.MsgStream != nil {
116 msgDetails = rpc.MsgStream
117 msgReply = rpc.MsgReply
119 msgDetails = rpc.MsgReply
120 msgReply = msgControlPingReply
123 g.P("stream, err := c.conn.NewStream(ctx)")
124 g.P("if err != nil { return nil, err }")
125 g.P("x := &", streamImpl, "{stream}")
126 g.P("if err := x.Stream.SendMsg(in); err != nil {")
127 g.P(" return nil, err")
129 if rpc.MsgStream == nil {
130 g.P("if err = x.Stream.SendMsg(&", msgControlPing.GoIdent, "{}); err != nil {")
131 g.P(" return nil, err")
137 g.P("type ", streamApi, " interface {")
138 g.P(" Recv() (*", msgDetails.GoIdent, ", error)")
139 g.P(" ", govppApiPkg.Ident("Stream"))
143 g.P("type ", streamImpl, " struct {")
144 g.P(" ", govppApiPkg.Ident("Stream"))
148 g.P("func (c *", streamImpl, ") Recv() (*", msgDetails.GoIdent, ", error) {")
149 g.P(" msg, err := c.Stream.RecvMsg()")
150 g.P(" if err != nil { return nil, err }")
151 g.P(" switch m := msg.(type) {")
152 g.P(" case *", msgDetails.GoIdent, ":")
153 g.P(" return m, nil")
154 g.P(" case *", msgReply.GoIdent, ":")
155 g.P(" return nil, ", ioPkg.Ident("EOF"))
157 g.P(" return nil, ", fmtPkg.Ident("Errorf"), "(\"unexpected message: %T %v\", m, m)")
159 } else if rpc.MsgReply != nil {
160 g.P("out := new(", rpc.MsgReply.GoIdent, ")")
161 g.P("err := c.conn.Invoke(ctx, in, out)")
162 g.P("if err != nil { return nil, err }")
163 g.P("return out, nil")
165 g.P("stream, err := c.conn.NewStream(ctx)")
166 g.P("if err != nil { return err }")
167 g.P("err = stream.SendMsg(in)")
168 g.P("if err != nil { return err }")
175 // TODO: generate service descriptor
176 /*fmt.Fprintf(w, "var %s = api.%s{\n", serviceDescName, serviceDescType)
177 fmt.Fprintf(w, "\tServiceName: \"%s\",\n", ctx.moduleName)
178 fmt.Fprintf(w, "\tHandlerType: (*%s)(nil),\n", serviceApiName)
179 fmt.Fprintf(w, "\tMethods: []api.MethodDesc{\n")
180 for _, method := range rpcs {
181 fmt.Fprintf(w, "\t {\n")
182 fmt.Fprintf(w, "\t MethodName: \"%s\",\n", method.Name)
183 fmt.Fprintf(w, "\t },\n")
185 fmt.Fprintf(w, "\t},\n")
186 //fmt.Fprintf(w, "\tCompatibility: %s,\n", messageCrcName)
187 //fmt.Fprintf(w, "\tMetadata: reflect.TypeOf((*%s)(nil)).Elem().PkgPath(),\n", serviceApiName)
188 fmt.Fprintf(w, "\tMetadata: \"%s\",\n", ctx.inputFile)
189 fmt.Fprintln(w, "}")*/
194 func rpcMethodSignature(g *GenFile, rpc *RPC) string {
195 s := rpc.GoName + "(ctx " + g.GoIdent(contextPkg.Ident("Context"))
196 s += ", in *" + g.GoIdent(rpc.MsgRequest.GoIdent) + ") ("
198 s += serviceApiName + "_" + rpc.GoName + "Client, "
199 } else if rpc.MsgReply != nil {
200 s += "*" + g.GoIdent(rpc.MsgReply.GoIdent) + ", "