From 94620e85f0bdbb054af07ce3670fadc1f76cfdf0 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Thu, 18 Jun 2020 08:22:13 +0200 Subject: Refactored binapi generator with message encoding Change-Id: I5a6abb68b9d058866f94818169300e5c2fc43895 Signed-off-by: Ondrej Fabry --- binapigen/generate_rpc.go | 188 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 binapigen/generate_rpc.go (limited to 'binapigen/generate_rpc.go') 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) +} -- cgit 1.2.3-korg