summaryrefslogtreecommitdiffstats
path: root/cmd/binapi-generator/generate.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/binapi-generator/generate.go')
-rw-r--r--cmd/binapi-generator/generate.go848
1 files changed, 0 insertions, 848 deletions
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)
-}