socket adapter: don't bother sending sockclnt_delete messages
[govpp.git] / binapigen / generate_rpc.go
1 //  Copyright (c) 2020 Cisco and/or its affiliates.
2 //
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:
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15 package binapigen
16
17 import (
18         "fmt"
19         "io"
20         "strings"
21 )
22
23 // generated service names
24 const (
25         serviceApiName    = "RPCService"    // name for the RPC service interface
26         serviceImplName   = "serviceClient" // name for the RPC service implementation
27         serviceClientName = "ServiceClient" // name for the RPC service client
28
29         // TODO: register service descriptor
30         //serviceDescType = "ServiceDesc"             // name for service descriptor type
31         //serviceDescName = "_ServiceRPC_serviceDesc" // name for service descriptor var
32 )
33
34 func generateFileRPC(ctx *GenFile, w io.Writer) {
35         logf("----------------------------")
36         logf("generating RPC file package: %q", ctx.file.PackageName)
37         logf("----------------------------")
38
39         // generate file header
40         fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
41         fmt.Fprintln(w)
42
43         // generate package header
44         fmt.Fprintf(w, "package %s\n", ctx.file.PackageName)
45         fmt.Fprintln(w)
46
47         // generate imports
48         fmt.Fprintln(w, "import (")
49         fmt.Fprintln(w, `       "context"`)
50         fmt.Fprintln(w, `       "io"`)
51         fmt.Fprintln(w)
52         fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
53         fmt.Fprintln(w, ")")
54         fmt.Fprintln(w)
55
56         // generate RPC service
57         if ctx.file.Service != nil && len(ctx.file.Service.RPCs) > 0 {
58                 generateService(ctx, w, ctx.file.Service)
59         }
60
61         // generate message registrations
62         /*fmt.Fprintln(w, "var _RPCService_desc = api.RPCDesc{")
63
64           fmt.Fprintln(w, "}")
65           fmt.Fprintln(w)*/
66
67         // generate import refs
68         fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
69         fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
70         fmt.Fprintf(w, "var _ = context.Background\n")
71         fmt.Fprintf(w, "var _ = io.Copy\n")
72
73 }
74
75 func generateService(ctx *GenFile, w io.Writer, svc *Service) {
76         // generate services comment
77         generateComment(ctx, w, serviceApiName, "services", "service")
78
79         // generate service api
80         fmt.Fprintf(w, "type %s interface {\n", serviceApiName)
81         for _, rpc := range svc.RPCs {
82                 generateRPCMethod(ctx, w, &rpc)
83                 fmt.Fprintln(w)
84         }
85         fmt.Fprintln(w, "}")
86         fmt.Fprintln(w)
87
88         // generate client implementation
89         fmt.Fprintf(w, "type %s struct {\n", serviceImplName)
90         fmt.Fprintf(w, "\tch api.Channel\n")
91         fmt.Fprintln(w, "}")
92         fmt.Fprintln(w)
93
94         // generate client constructor
95         fmt.Fprintf(w, "func New%s(ch api.Channel) %s {\n", serviceClientName, serviceApiName)
96         fmt.Fprintf(w, "\treturn &%s{ch}\n", serviceImplName)
97         fmt.Fprintln(w, "}")
98         fmt.Fprintln(w)
99
100         for _, rpc := range svc.RPCs {
101                 method := camelCaseName(rpc.RequestMsg)
102                 if m := strings.TrimSuffix(method, "Dump"); method != m {
103                         method = "Dump" + m
104                 }
105
106                 fmt.Fprintf(w, "func (c *%s) ", serviceImplName)
107                 generateRPCMethod(ctx, w, &rpc)
108                 fmt.Fprintln(w, " {")
109                 if rpc.Stream {
110                         streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
111                         fmt.Fprintf(w, "\tstream := c.ch.SendMultiRequest(in)\n")
112                         fmt.Fprintf(w, "\tx := &%s{stream}\n", streamImpl)
113                         fmt.Fprintf(w, "\treturn x, nil\n")
114                 } else if replyTyp := camelCaseName(rpc.ReplyMsg); replyTyp != "" {
115                         fmt.Fprintf(w, "\tout := new(%s)\n", replyTyp)
116                         fmt.Fprintf(w, "\terr:= c.ch.SendRequest(in).ReceiveReply(out)\n")
117                         fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
118                         fmt.Fprintf(w, "\treturn out, nil\n")
119                 } else {
120                         fmt.Fprintf(w, "\tc.ch.SendRequest(in)\n")
121                         fmt.Fprintf(w, "\treturn nil\n")
122                 }
123                 fmt.Fprintln(w, "}")
124                 fmt.Fprintln(w)
125
126                 if rpc.Stream {
127                         replyTyp := camelCaseName(rpc.ReplyMsg)
128                         method := camelCaseName(rpc.RequestMsg)
129                         if m := strings.TrimSuffix(method, "Dump"); method != m {
130                                 method = "Dump" + m
131                         }
132                         streamApi := fmt.Sprintf("%s_%sClient", serviceApiName, method)
133
134                         fmt.Fprintf(w, "type %s interface {\n", streamApi)
135                         fmt.Fprintf(w, "\tRecv() (*%s, error)\n", replyTyp)
136                         fmt.Fprintln(w, "}")
137                         fmt.Fprintln(w)
138
139                         streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
140                         fmt.Fprintf(w, "type %s struct {\n", streamImpl)
141                         fmt.Fprintf(w, "\tapi.MultiRequestCtx\n")
142                         fmt.Fprintln(w, "}")
143                         fmt.Fprintln(w)
144
145                         fmt.Fprintf(w, "func (c *%s) Recv() (*%s, error) {\n", streamImpl, replyTyp)
146                         fmt.Fprintf(w, "\tm := new(%s)\n", replyTyp)
147                         fmt.Fprintf(w, "\tstop, err := c.MultiRequestCtx.ReceiveReply(m)\n")
148                         fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
149                         fmt.Fprintf(w, "\tif stop { return nil, io.EOF }\n")
150                         fmt.Fprintf(w, "\treturn m, nil\n")
151                         fmt.Fprintln(w, "}")
152                         fmt.Fprintln(w)
153                 }
154         }
155
156         // TODO: generate service descriptor
157         /*fmt.Fprintf(w, "var %s = api.%s{\n", serviceDescName, serviceDescType)
158           fmt.Fprintf(w, "\tServiceName: \"%s\",\n", ctx.moduleName)
159           fmt.Fprintf(w, "\tHandlerType: (*%s)(nil),\n", serviceApiName)
160           fmt.Fprintf(w, "\tMethods: []api.MethodDesc{\n")
161           for _, method := range rpcs {
162                 fmt.Fprintf(w, "\t  {\n")
163                 fmt.Fprintf(w, "\t    MethodName: \"%s\",\n", method.Name)
164                 fmt.Fprintf(w, "\t  },\n")
165           }
166           fmt.Fprintf(w, "\t},\n")
167           //fmt.Fprintf(w, "\tCompatibility: %s,\n", messageCrcName)
168           //fmt.Fprintf(w, "\tMetadata: reflect.TypeOf((*%s)(nil)).Elem().PkgPath(),\n", serviceApiName)
169           fmt.Fprintf(w, "\tMetadata: \"%s\",\n", ctx.inputFile)
170           fmt.Fprintln(w, "}")*/
171
172         fmt.Fprintln(w)
173 }
174
175 func generateRPCMethod(ctx *GenFile, w io.Writer, rpc *RPC) {
176         reqTyp := camelCaseName(rpc.RequestMsg)
177
178         logf(" writing RPC: %+v", reqTyp)
179
180         // method name is same as parameter type name by default
181         method := reqTyp
182         if rpc.Stream {
183                 // use Dump as prefix instead of suffix for stream services
184                 if m := strings.TrimSuffix(method, "Dump"); method != m {
185                         method = "Dump" + m
186                 }
187         }
188
189         params := fmt.Sprintf("in *%s", reqTyp)
190         returns := "error"
191
192         if replyType := camelCaseName(rpc.ReplyMsg); replyType != "" {
193                 var replyTyp string
194                 if rpc.Stream {
195                         replyTyp = fmt.Sprintf("%s_%sClient", serviceApiName, method)
196                 } else {
197                         replyTyp = fmt.Sprintf("*%s", replyType)
198                 }
199                 returns = fmt.Sprintf("(%s, error)", replyTyp)
200         }
201
202         fmt.Fprintf(w, "\t%s(ctx context.Context, %s) %s", method, params, returns)
203 }