// 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" ) // generated service names const ( serviceApiName = "RPCService" // name for the RPC service interface serviceImplName = "serviceClient" // name for the RPC service implementation serviceClientName = "ServiceClient" // name for the RPC service client // TODO: register service descriptor //serviceDescType = "ServiceDesc" // name for service descriptor type //serviceDescName = "_ServiceRPC_serviceDesc" // name for service descriptor var ) func generateFileRPC(ctx *GenFile, w io.Writer) { logf("----------------------------") logf("generating RPC file package: %q", ctx.file.PackageName) logf("----------------------------") // generate file header fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.") fmt.Fprintln(w) // generate package header fmt.Fprintf(w, "package %s\n", ctx.file.PackageName) fmt.Fprintln(w) // generate imports 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 RPC service if ctx.file.Service != nil && len(ctx.file.Service.RPCs) > 0 { generateService(ctx, w, ctx.file.Service) } // generate message registrations /*fmt.Fprintln(w, "var _RPCService_desc = api.RPCDesc{") fmt.Fprintln(w, "}") fmt.Fprintln(w)*/ // generate import refs 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 generateService(ctx *GenFile, w io.Writer, svc *Service) { // generate services comment generateComment(ctx, w, serviceApiName, "services", "service") // generate service api fmt.Fprintf(w, "type %s interface {\n", serviceApiName) for _, rpc := range svc.RPCs { generateRPCMethod(ctx, w, &rpc) 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 _, rpc := range svc.RPCs { method := camelCaseName(rpc.RequestMsg) if m := strings.TrimSuffix(method, "Dump"); method != m { method = "Dump" + m } fmt.Fprintf(w, "func (c *%s) ", serviceImplName) generateRPCMethod(ctx, w, &rpc) fmt.Fprintln(w, " {") if rpc.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(rpc.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 rpc.Stream { replyTyp := camelCaseName(rpc.ReplyMsg) method := camelCaseName(rpc.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 rpcs { 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 generateRPCMethod(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) }