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