summaryrefslogtreecommitdiffstats
path: root/binapigen/generate_rpc.go
diff options
context:
space:
mode:
Diffstat (limited to 'binapigen/generate_rpc.go')
-rw-r--r--binapigen/generate_rpc.go188
1 files changed, 188 insertions, 0 deletions
diff --git a/binapigen/generate_rpc.go b/binapigen/generate_rpc.go
new file mode 100644
index 0000000..b480f4a
--- /dev/null
+++ b/binapigen/generate_rpc.go
@@ -0,0 +1,188 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "fmt"
+ "io"
+ "strings"
+)
+
+func generatePackageRPC(ctx *GenFile, w io.Writer) {
+ logf("----------------------------")
+ logf("generating RPC package: %q", ctx.file.PackageName)
+ logf("----------------------------")
+
+ fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.")
+ fmt.Fprintln(w)
+
+ fmt.Fprintf(w, "package %s\n", ctx.file.PackageName)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, "import (")
+ fmt.Fprintln(w, ` "context"`)
+ fmt.Fprintln(w, ` "io"`)
+ fmt.Fprintln(w)
+ fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
+ fmt.Fprintln(w, ")")
+ fmt.Fprintln(w)
+
+ // generate services
+ if ctx.file.Service != nil && len(ctx.file.Service.RPCs) > 0 {
+ generateServiceMethods(ctx, w, ctx.file.Service.RPCs)
+ }
+
+ // generate message registrations
+ /*fmt.Fprintln(w, "var _RPCService_desc = api.RPCDesc{")
+
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)*/
+
+ fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n")
+ fmt.Fprintf(w, "var _ = api.RegisterMessage\n")
+ fmt.Fprintf(w, "var _ = context.Background\n")
+ fmt.Fprintf(w, "var _ = io.Copy\n")
+
+}
+
+func generateServiceMethods(ctx *GenFile, w io.Writer, methods []RPC) {
+ // generate services comment
+ generateComment(ctx, w, serviceApiName, "services", "service")
+
+ // generate service api
+ fmt.Fprintf(w, "type %s interface {\n", serviceApiName)
+ for _, svc := range methods {
+ generateServiceMethod(ctx, w, &svc)
+ fmt.Fprintln(w)
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ // generate client implementation
+ fmt.Fprintf(w, "type %s struct {\n", serviceImplName)
+ fmt.Fprintf(w, "\tch api.Channel\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ // generate client constructor
+ fmt.Fprintf(w, "func New%s(ch api.Channel) %s {\n", serviceClientName, serviceApiName)
+ fmt.Fprintf(w, "\treturn &%s{ch}\n", serviceImplName)
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ for _, met := range methods {
+ method := camelCaseName(met.RequestMsg)
+ if m := strings.TrimSuffix(method, "Dump"); method != m {
+ method = "Dump" + m
+ }
+
+ fmt.Fprintf(w, "func (c *%s) ", serviceImplName)
+ generateServiceMethod(ctx, w, &met)
+ fmt.Fprintln(w, " {")
+ if met.Stream {
+ streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
+ fmt.Fprintf(w, "\tstream := c.ch.SendMultiRequest(in)\n")
+ fmt.Fprintf(w, "\tx := &%s{stream}\n", streamImpl)
+ fmt.Fprintf(w, "\treturn x, nil\n")
+ } else if replyTyp := camelCaseName(met.ReplyMsg); replyTyp != "" {
+ fmt.Fprintf(w, "\tout := new(%s)\n", replyTyp)
+ fmt.Fprintf(w, "\terr:= c.ch.SendRequest(in).ReceiveReply(out)\n")
+ fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
+ fmt.Fprintf(w, "\treturn out, nil\n")
+ } else {
+ fmt.Fprintf(w, "\tc.ch.SendRequest(in)\n")
+ fmt.Fprintf(w, "\treturn nil\n")
+ }
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ if met.Stream {
+ replyTyp := camelCaseName(met.ReplyMsg)
+ method := camelCaseName(met.RequestMsg)
+ if m := strings.TrimSuffix(method, "Dump"); method != m {
+ method = "Dump" + m
+ }
+ streamApi := fmt.Sprintf("%s_%sClient", serviceApiName, method)
+
+ fmt.Fprintf(w, "type %s interface {\n", streamApi)
+ fmt.Fprintf(w, "\tRecv() (*%s, error)\n", replyTyp)
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ streamImpl := fmt.Sprintf("%s_%sClient", serviceImplName, method)
+ fmt.Fprintf(w, "type %s struct {\n", streamImpl)
+ fmt.Fprintf(w, "\tapi.MultiRequestCtx\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+
+ fmt.Fprintf(w, "func (c *%s) Recv() (*%s, error) {\n", streamImpl, replyTyp)
+ fmt.Fprintf(w, "\tm := new(%s)\n", replyTyp)
+ fmt.Fprintf(w, "\tstop, err := c.MultiRequestCtx.ReceiveReply(m)\n")
+ fmt.Fprintf(w, "\tif err != nil { return nil, err }\n")
+ fmt.Fprintf(w, "\tif stop { return nil, io.EOF }\n")
+ fmt.Fprintf(w, "\treturn m, nil\n")
+ fmt.Fprintln(w, "}")
+ fmt.Fprintln(w)
+ }
+ }
+
+ // TODO: generate service descriptor
+ /*fmt.Fprintf(w, "var %s = api.%s{\n", serviceDescName, serviceDescType)
+ fmt.Fprintf(w, "\tServiceName: \"%s\",\n", ctx.moduleName)
+ fmt.Fprintf(w, "\tHandlerType: (*%s)(nil),\n", serviceApiName)
+ fmt.Fprintf(w, "\tMethods: []api.MethodDesc{\n")
+ for _, method := range methods {
+ fmt.Fprintf(w, "\t {\n")
+ fmt.Fprintf(w, "\t MethodName: \"%s\",\n", method.Name)
+ fmt.Fprintf(w, "\t },\n")
+ }
+ fmt.Fprintf(w, "\t},\n")
+ //fmt.Fprintf(w, "\tCompatibility: %s,\n", messageCrcName)
+ //fmt.Fprintf(w, "\tMetadata: reflect.TypeOf((*%s)(nil)).Elem().PkgPath(),\n", serviceApiName)
+ fmt.Fprintf(w, "\tMetadata: \"%s\",\n", ctx.inputFile)
+ fmt.Fprintln(w, "}")*/
+
+ fmt.Fprintln(w)
+}
+
+func generateServiceMethod(ctx *GenFile, w io.Writer, rpc *RPC) {
+ reqTyp := camelCaseName(rpc.RequestMsg)
+
+ logf(" writing RPC: %+v", reqTyp)
+
+ // method name is same as parameter type name by default
+ method := reqTyp
+ if rpc.Stream {
+ // use Dump as prefix instead of suffix for stream services
+ if m := strings.TrimSuffix(method, "Dump"); method != m {
+ method = "Dump" + m
+ }
+ }
+
+ params := fmt.Sprintf("in *%s", reqTyp)
+ returns := "error"
+
+ if replyType := camelCaseName(rpc.ReplyMsg); replyType != "" {
+ var replyTyp string
+ if rpc.Stream {
+ replyTyp = fmt.Sprintf("%s_%sClient", serviceApiName, method)
+ } else {
+ replyTyp = fmt.Sprintf("*%s", replyType)
+ }
+ returns = fmt.Sprintf("(%s, error)", replyTyp)
+ }
+
+ fmt.Fprintf(w, "\t%s(ctx context.Context, %s) %s", method, params, returns)
+}