X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=cmd%2Fbinapi-generator%2Fgenerate.go;fp=cmd%2Fbinapi-generator%2Fgenerate.go;h=0000000000000000000000000000000000000000;hb=94620e85f0bdbb054af07ce3670fadc1f76cfdf0;hp=715836dceb7fd3adeb2c33244d9d6dd6e49d8634;hpb=280b1c6c83b676ef4e592f4ecf60cb5b54b6a753;p=govpp.git diff --git a/cmd/binapi-generator/generate.go b/cmd/binapi-generator/generate.go deleted file mode 100644 index 715836d..0000000 --- a/cmd/binapi-generator/generate.go +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright (c) 2017 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 main - -import ( - "bytes" - "fmt" - "io" - "os/exec" - "path" - "path/filepath" - "sort" - "strings" - "unicode" -) - -// generatedCodeVersion indicates a version of the generated code. -// It is incremented whenever an incompatibility between the generated code and -// GoVPP api package is introduced; the generated code references -// a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion). -const generatedCodeVersion = 1 - -const ( - inputFileExt = ".api.json" // file extension of the VPP API files - outputFileExt = ".ba.go" // file extension of the Go generated files - - constModuleName = "ModuleName" // module name constant - constAPIVersion = "APIVersion" // API version constant - constVersionCrc = "VersionCrc" // version CRC constant - - unionDataField = "XXX_UnionData" // name for the union data field - - serviceApiName = "RPCService" // name for the RPC service interface - serviceImplName = "serviceClient" // name for the RPC service implementation - serviceClientName = "ServiceClient" // name for the RPC service client -) - -// context is a structure storing data for code generation -type context struct { - inputFile string // input file with VPP API in JSON - outputFile string // output file with generated Go package - - importPrefix string // defines import path prefix for importing types - - inputData []byte // contents of the input file - - includeAPIVersion bool // include constant with API version string - includeComments bool // include parts of original source in comments - includeBinapiNames bool // include binary API names as struct tag - includeServices bool // include service interface with client implementation - - moduleName string // name of the source VPP module - packageName string // name of the Go package being generated - - packageData *Package // parsed package data -} - -// newContext returns context details of the code generation task -func newContext(inputFile, outputDir string) (*context, error) { - if !strings.HasSuffix(inputFile, inputFileExt) { - return nil, fmt.Errorf("invalid input file name: %q", inputFile) - } - - ctx := &context{ - inputFile: inputFile, - } - - // package name - inputFileName := filepath.Base(inputFile) - ctx.moduleName = inputFileName[:strings.Index(inputFileName, ".")] - - // alter package names for modules that are reserved keywords in Go - switch ctx.moduleName { - case "interface": - ctx.packageName = "interfaces" - case "map": - ctx.packageName = "maps" - default: - ctx.packageName = ctx.moduleName - } - - // output file - packageDir := filepath.Join(outputDir, ctx.packageName) - outputFileName := ctx.packageName + outputFileExt - ctx.outputFile = filepath.Join(packageDir, outputFileName) - - return ctx, nil -} - -func generatePackage(ctx *context, w io.Writer) error { - logf("----------------------------") - logf("generating package %q", ctx.packageName) - logf("----------------------------") - - fmt.Fprintln(w, "// Code generated by GoVPP's binapi-generator. DO NOT EDIT.") - fmt.Fprintf(w, "// source: %s\n", ctx.inputFile) - fmt.Fprintln(w) - - generateHeader(ctx, w) - generateImports(ctx, w) - - // generate module desc - fmt.Fprintln(w, "const (") - fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName) - fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.moduleName) - - if ctx.includeAPIVersion { - if ctx.packageData.Version != "" { - fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion) - fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.packageData.Version) - } - fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc) - fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.packageData.CRC) - } - fmt.Fprintln(w, ")") - fmt.Fprintln(w) - - // generate enums - if len(ctx.packageData.Enums) > 0 { - for _, enum := range ctx.packageData.Enums { - if imp, ok := ctx.packageData.Imports[enum.Name]; ok { - generateImportedAlias(ctx, w, enum.Name, &imp) - continue - } - generateEnum(ctx, w, &enum) - } - } - - // generate aliases - if len(ctx.packageData.Aliases) > 0 { - for _, alias := range ctx.packageData.Aliases { - if imp, ok := ctx.packageData.Imports[alias.Name]; ok { - generateImportedAlias(ctx, w, alias.Name, &imp) - continue - } - generateAlias(ctx, w, &alias) - } - } - - // generate types - if len(ctx.packageData.Types) > 0 { - for _, typ := range ctx.packageData.Types { - if imp, ok := ctx.packageData.Imports[typ.Name]; ok { - generateImportedAlias(ctx, w, typ.Name, &imp) - continue - } - generateType(ctx, w, &typ) - } - } - - // generate unions - if len(ctx.packageData.Unions) > 0 { - for _, union := range ctx.packageData.Unions { - if imp, ok := ctx.packageData.Imports[union.Name]; ok { - generateImportedAlias(ctx, w, union.Name, &imp) - continue - } - generateUnion(ctx, w, &union) - } - } - - // generate messages - if len(ctx.packageData.Messages) > 0 { - for _, msg := range ctx.packageData.Messages { - generateMessage(ctx, w, &msg) - } - - // generate message registrations - fmt.Fprintln(w, "func init() {") - for _, msg := range ctx.packageData.Messages { - name := camelCaseName(msg.Name) - fmt.Fprintf(w, "\tapi.RegisterMessage((*%s)(nil), \"%s\")\n", name, ctx.moduleName+"."+name) - } - fmt.Fprintln(w, "}") - fmt.Fprintln(w) - - // generate list of messages - fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n") - fmt.Fprintln(w, "func AllMessages() []api.Message {") - fmt.Fprintln(w, "\treturn []api.Message{") - for _, msg := range ctx.packageData.Messages { - name := camelCaseName(msg.Name) - fmt.Fprintf(w, "\t(*%s)(nil),\n", name) - } - fmt.Fprintln(w, "}") - fmt.Fprintln(w, "}") - } - - if ctx.includeServices { - // generate services - if len(ctx.packageData.Services) > 0 { - generateServices(ctx, w, ctx.packageData.Services) - } - } - - generateFooter(ctx, w) - - return nil -} - -func generateHeader(ctx *context, w io.Writer) { - fmt.Fprintln(w, "/*") - fmt.Fprintf(w, "Package %s is a generated VPP binary API for '%s' module.\n", ctx.packageName, ctx.moduleName) - fmt.Fprintln(w) - fmt.Fprintln(w, "It consists of:") - printObjNum := func(obj string, num int) { - if num > 0 { - if num > 1 { - if strings.HasSuffix(obj, "s") { - - obj += "es" - } else { - obj += "s" - } - } - fmt.Fprintf(w, "\t%3d %s\n", num, obj) - } - } - printObjNum("enum", len(ctx.packageData.Enums)) - printObjNum("alias", len(ctx.packageData.Aliases)) - printObjNum("type", len(ctx.packageData.Types)) - printObjNum("union", len(ctx.packageData.Unions)) - printObjNum("message", len(ctx.packageData.Messages)) - printObjNum("service", len(ctx.packageData.Services)) - fmt.Fprintln(w, "*/") - fmt.Fprintf(w, "package %s\n", ctx.packageName) - fmt.Fprintln(w) - -} - -func generateImports(ctx *context, w io.Writer) { - fmt.Fprintln(w, "import (") - fmt.Fprintln(w, ` "bytes"`) - fmt.Fprintln(w, ` "context"`) - fmt.Fprintln(w, ` "io"`) - fmt.Fprintln(w, ` "strconv"`) - fmt.Fprintln(w) - fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api") - fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc") - if len(ctx.packageData.Imports) > 0 { - fmt.Fprintln(w) - for _, imp := range getImports(ctx) { - importPath := path.Join(ctx.importPrefix, imp) - if importPath == "" { - importPath = getImportPath(filepath.Dir(ctx.outputFile), imp) - } - fmt.Fprintf(w, "\t%s \"%s\"\n", imp, strings.TrimSpace(importPath)) - } - } - fmt.Fprintln(w, ")") - fmt.Fprintln(w) -} - -func getImportPath(outputDir string, pkg string) string { - absPath, _ := filepath.Abs(filepath.Join(outputDir, "..", pkg)) - cmd := exec.Command("go", "list", absPath) - var errbuf, outbuf bytes.Buffer - cmd.Stdout = &outbuf - cmd.Stderr = &errbuf - if err := cmd.Run(); err != nil { - fmt.Printf("ERR: %v\n", errbuf.String()) - panic(err) - } - return outbuf.String() -} - -func getImports(ctx *context) (imports []string) { - impmap := map[string]struct{}{} - for _, imp := range ctx.packageData.Imports { - if _, ok := impmap[imp.Package]; !ok { - imports = append(imports, imp.Package) - impmap[imp.Package] = struct{}{} - } - } - sort.Strings(imports) - return imports -} - -func generateFooter(ctx *context, w io.Writer) { - fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file") - fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.") - fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the") - fmt.Fprintln(w, "// GoVPP api package needs to be updated.") - fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion) - 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 _ = bytes.NewBuffer\n") - fmt.Fprintf(w, "var _ = context.Background\n") - fmt.Fprintf(w, "var _ = io.Copy\n") - fmt.Fprintf(w, "var _ = strconv.Itoa\n") - fmt.Fprintf(w, "var _ = struc.Pack\n") -} - -func generateComment(ctx *context, w io.Writer, goName string, vppName string, objKind string) { - if objKind == "service" { - fmt.Fprintf(w, "// %s represents RPC service API for %s module.\n", goName, ctx.moduleName) - } else { - fmt.Fprintf(w, "// %s represents VPP binary API %s '%s'.\n", goName, objKind, vppName) - } - - if !ctx.includeComments { - return - } - - var isNotSpace = func(r rune) bool { - return !unicode.IsSpace(r) - } - - // print out the source of the generated object - mapType := false - objFound := false - objTitle := fmt.Sprintf(`"%s",`, vppName) - switch objKind { - case "alias", "service": - objTitle = fmt.Sprintf(`"%s": {`, vppName) - mapType = true - } - - inputBuff := bytes.NewBuffer(ctx.inputData) - inputLine := 0 - - var trimIndent string - var indent int - for { - line, err := inputBuff.ReadString('\n') - if err != nil { - break - } - inputLine++ - - noSpaceAt := strings.IndexFunc(line, isNotSpace) - if !objFound { - indent = strings.Index(line, objTitle) - if indent == -1 { - continue - } - trimIndent = line[:indent] - // If no other non-whitespace character then we are at the message header. - if trimmed := strings.TrimSpace(line); trimmed == objTitle { - objFound = true - fmt.Fprintln(w, "//") - } - } else if noSpaceAt < indent { - break // end of the definition in JSON for array types - } else if objFound && mapType && noSpaceAt <= indent { - fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent)) - break // end of the definition in JSON for map types (aliases, services) - } - fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent)) - } - - fmt.Fprintln(w, "//") -} - -func generateEnum(ctx *context, w io.Writer, enum *Enum) { - name := camelCaseName(enum.Name) - typ := binapiTypes[enum.Type] - - logf(" writing enum %q (%s) with %d entries", enum.Name, name, len(enum.Entries)) - - // generate enum comment - generateComment(ctx, w, name, enum.Name, "enum") - - // generate enum definition - fmt.Fprintf(w, "type %s %s\n", name, typ) - fmt.Fprintln(w) - - // generate enum entries - fmt.Fprintln(w, "const (") - for _, entry := range enum.Entries { - fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value) - } - fmt.Fprintln(w, ")") - fmt.Fprintln(w) - - // generate enum conversion maps - fmt.Fprintf(w, "var %s_name = map[%s]string{\n", name, typ) - for _, entry := range enum.Entries { - fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name) - } - fmt.Fprintln(w, "}") - fmt.Fprintln(w) - - fmt.Fprintf(w, "var %s_value = map[string]%s{\n", name, typ) - for _, entry := range enum.Entries { - fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value) - } - fmt.Fprintln(w, "}") - fmt.Fprintln(w) - - fmt.Fprintf(w, "func (x %s) String() string {\n", name) - fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ) - fmt.Fprintf(w, "\tif ok { return s }\n") - fmt.Fprintf(w, "\treturn strconv.Itoa(int(x))\n") - fmt.Fprintln(w, "}") - fmt.Fprintln(w) -} - -func generateImportedAlias(ctx *context, w io.Writer, tName string, imp *Import) { - name := camelCaseName(tName) - - fmt.Fprintf(w, "type %s = %s.%s\n", name, imp.Package, name) - - fmt.Fprintln(w) -} - -func generateAlias(ctx *context, w io.Writer, alias *Alias) { - name := camelCaseName(alias.Name) - - logf(" writing type %q (%s), length: %d", alias.Name, name, alias.Length) - - // generate struct comment - generateComment(ctx, w, name, alias.Name, "alias") - - // generate struct definition - fmt.Fprintf(w, "type %s ", name) - - if alias.Length > 0 { - fmt.Fprintf(w, "[%d]", alias.Length) - } - - dataType := convertToGoType(ctx, alias.Type) - fmt.Fprintf(w, "%s\n", dataType) - - fmt.Fprintln(w) -} - -func generateUnion(ctx *context, w io.Writer, union *Union) { - name := camelCaseName(union.Name) - - logf(" writing union %q (%s) with %d fields", union.Name, name, len(union.Fields)) - - // generate struct comment - generateComment(ctx, w, name, union.Name, "union") - - // generate struct definition - fmt.Fprintln(w, "type", name, "struct {") - - // maximum size for union - maxSize := getUnionSize(ctx, union) - - // generate data field - fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize) - - // generate end of the struct - fmt.Fprintln(w, "}") - - // generate name getter - generateTypeNameGetter(w, name, union.Name) - - // generate CRC getter - if union.CRC != "" { - generateCrcGetter(w, name, union.CRC) - } - - // generate getters for fields - for _, field := range union.Fields { - fieldName := camelCaseName(field.Name) - fieldType := convertToGoType(ctx, field.Type) - generateUnionGetterSetter(w, name, fieldName, fieldType) - } - - // generate union methods - //generateUnionMethods(w, name) - - fmt.Fprintln(w) -} - -// generateUnionMethods generates methods that implement struc.Custom -// interface to allow having XXX_uniondata field unexported -// TODO: do more testing when unions are actually used in some messages -/*func generateUnionMethods(w io.Writer, structName string) { - // generate struc.Custom implementation for union - fmt.Fprintf(w, ` -func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) { - var b = new(bytes.Buffer) - if err := struc.PackWithOptions(b, u.union_data, opt); err != nil { - return 0, err - } - copy(p, b.Bytes()) - return b.Len(), nil -} -func (u *%[1]s) Unpack(r io.Reader, length int, opt *struc.Options) error { - return struc.UnpackWithOptions(r, u.union_data[:], opt) -} -func (u *%[1]s) Size(opt *struc.Options) int { - return len(u.union_data) -} -func (u *%[1]s) String() string { - return string(u.union_data[:]) -} -`, structName) -}*/ - -func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) { - fmt.Fprintf(w, ` -func %[1]s%[2]s(a %[3]s) (u %[1]s) { - u.Set%[2]s(a) - return -} -func (u *%[1]s) Set%[2]s(a %[3]s) { - var b = new(bytes.Buffer) - if err := struc.Pack(b, &a); err != nil { - return - } - copy(u.%[4]s[:], b.Bytes()) -} -func (u *%[1]s) Get%[2]s() (a %[3]s) { - var b = bytes.NewReader(u.%[4]s[:]) - struc.Unpack(b, &a) - return -} -`, structName, getterField, getterStruct, unionDataField) -} - -func generateType(ctx *context, w io.Writer, typ *Type) { - name := camelCaseName(typ.Name) - - logf(" writing type %q (%s) with %d fields", typ.Name, name, len(typ.Fields)) - - // generate struct comment - generateComment(ctx, w, name, typ.Name, "type") - - // generate struct definition - fmt.Fprintf(w, "type %s struct {\n", name) - - // generate struct fields - for i, field := range typ.Fields { - // skip internal fields - switch strings.ToLower(field.Name) { - case crcField, msgIdField: - continue - } - - generateField(ctx, w, typ.Fields, i) - } - - // generate end of the struct - fmt.Fprintln(w, "}") - - // generate name getter - generateTypeNameGetter(w, name, typ.Name) - - // generate CRC getter - if typ.CRC != "" { - generateCrcGetter(w, name, typ.CRC) - } - - fmt.Fprintln(w) -} - -func generateMessage(ctx *context, w io.Writer, msg *Message) { - name := camelCaseName(msg.Name) - - logf(" writing message %q (%s) with %d fields", msg.Name, name, len(msg.Fields)) - - // generate struct comment - generateComment(ctx, w, name, msg.Name, "message") - - // generate struct definition - fmt.Fprintf(w, "type %s struct {", name) - - msgType := otherMessage - wasClientIndex := false - - // generate struct fields - n := 0 - for i, field := range msg.Fields { - if i == 1 { - if field.Name == clientIndexField { - // "client_index" as the second member, - // this might be an event message or a request - msgType = eventMessage - wasClientIndex = true - } else if field.Name == contextField { - // reply needs "context" as the second member - msgType = replyMessage - } - } else if i == 2 { - if wasClientIndex && field.Name == contextField { - // request needs "client_index" as the second member - // and "context" as the third member - msgType = requestMessage - } - } - - // skip internal fields - switch strings.ToLower(field.Name) { - case crcField, msgIdField: - continue - case clientIndexField, contextField: - if n == 0 { - continue - } - } - n++ - if n == 1 { - fmt.Fprintln(w) - } - - generateField(ctx, w, msg.Fields, i) - } - - // generate end of the struct - fmt.Fprintln(w, "}") - - // generate message methods - generateMessageResetMethod(w, name) - generateMessageNameGetter(w, name, msg.Name) - generateCrcGetter(w, name, msg.CRC) - generateMessageTypeGetter(w, name, msgType) - - fmt.Fprintln(w) -} - -func generateField(ctx *context, w io.Writer, fields []Field, i int) { - field := fields[i] - - fieldName := strings.TrimPrefix(field.Name, "_") - fieldName = camelCaseName(fieldName) - - dataType := convertToGoType(ctx, field.Type) - fieldType := dataType - - // generate length field for strings - if field.Type == "string" && field.Length == 0 { - fmt.Fprintf(w, "\tXXX_%sLen uint32 `struc:\"sizeof=%s\"`\n", fieldName, fieldName) - } - - // check if it is array - if field.Length > 0 || field.SizeFrom != "" { - if dataType == "uint8" { - dataType = "byte" - } - if dataType == "string" && field.SpecifiedLen { - fieldType = "string" - dataType = "byte" - } else { - fieldType = "[]" + dataType - } - } - fmt.Fprintf(w, "\t%s %s", fieldName, fieldType) - - fieldTags := map[string]string{} - - if field.Length > 0 && field.SpecifiedLen { - // fixed size array - fieldTags["struc"] = fmt.Sprintf("[%d]%s", field.Length, dataType) - } else { - for _, f := range fields { - if f.SizeFrom == field.Name { - // variable sized array - sizeOfName := camelCaseName(f.Name) - fieldTags["struc"] = fmt.Sprintf("sizeof=%s", sizeOfName) - } - } - } - - if ctx.includeBinapiNames { - fieldTags["binapi"] = field.Name - } - if field.Meta.Limit > 0 { - fieldTags["binapi"] = fmt.Sprintf("%s,limit=%d", fieldTags["binapi"], field.Meta.Limit) - } - - if len(fieldTags) > 0 { - fmt.Fprintf(w, "\t`") - var keys []string - for k := range fieldTags { - keys = append(keys, k) - } - sort.Strings(keys) - var n int - for _, tt := range keys { - t, ok := fieldTags[tt] - if !ok { - continue - } - if n > 0 { - fmt.Fprintf(w, " ") - } - n++ - fmt.Fprintf(w, `%s:"%s"`, tt, t) - } - fmt.Fprintf(w, "`") - } - - fmt.Fprintln(w) -} - -func generateMessageResetMethod(w io.Writer, structName string) { - fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName) -} - -func generateMessageNameGetter(w io.Writer, structName, msgName string) { - fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName) -} - -func generateTypeNameGetter(w io.Writer, structName, msgName string) { - fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName) -} - -func generateCrcGetter(w io.Writer, structName, crc string) { - crc = strings.TrimPrefix(crc, "0x") - fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc) -} - -func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) { - fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {") - if msgType == requestMessage { - fmt.Fprintf(w, "\treturn api.RequestMessage") - } else if msgType == replyMessage { - fmt.Fprintf(w, "\treturn api.ReplyMessage") - } else if msgType == eventMessage { - fmt.Fprintf(w, "\treturn api.EventMessage") - } else { - fmt.Fprintf(w, "\treturn api.OtherMessage") - } - fmt.Fprintln(w, "}") - fmt.Fprintln(w) -} - -func generateServices(ctx *context, w io.Writer, services []Service) { - - // generate services comment - generateComment(ctx, w, serviceApiName, "services", "service") - - // generate service api - fmt.Fprintf(w, "type %s interface {\n", serviceApiName) - for _, svc := range services { - 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 _, svc := range services { - method := camelCaseName(svc.RequestType) - if m := strings.TrimSuffix(method, "Dump"); method != m { - method = "Dump" + m - } - - fmt.Fprintf(w, "func (c *%s) ", serviceImplName) - generateServiceMethod(ctx, w, &svc) - fmt.Fprintln(w, " {") - if svc.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(svc.ReplyType); 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 svc.Stream { - replyTyp := camelCaseName(svc.ReplyType) - method := camelCaseName(svc.RequestType) - 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) - } - } - - fmt.Fprintln(w) -} - -func generateServiceMethod(ctx *context, w io.Writer, svc *Service) { - reqTyp := camelCaseName(svc.RequestType) - - // method name is same as parameter type name by default - method := reqTyp - if svc.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(svc.ReplyType); replyType != "" { - var replyTyp string - if svc.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) -}