diff options
author | 2020-06-18 08:22:13 +0200 | |
---|---|---|
committer | 2020-06-22 14:37:14 +0200 | |
commit | 94620e85f0bdbb054af07ce3670fadc1f76cfdf0 (patch) | |
tree | 7784ddf381c4e08a6a1ece5b55911b47ea8395f3 /cmd/binapi-generator | |
parent | 280b1c6c83b676ef4e592f4ecf60cb5b54b6a753 (diff) |
Refactored binapi generator with message encoding
Change-Id: I5a6abb68b9d058866f94818169300e5c2fc43895
Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
Diffstat (limited to 'cmd/binapi-generator')
-rw-r--r-- | cmd/binapi-generator/definitions.go | 152 | ||||
-rw-r--r-- | cmd/binapi-generator/definitions_test.go | 25 | ||||
-rw-r--r-- | cmd/binapi-generator/doc.go | 16 | ||||
-rw-r--r-- | cmd/binapi-generator/generate.go | 848 | ||||
-rw-r--r-- | cmd/binapi-generator/generate_test.go | 450 | ||||
-rw-r--r-- | cmd/binapi-generator/main.go | 350 | ||||
-rw-r--r-- | cmd/binapi-generator/objects.go | 139 | ||||
-rw-r--r-- | cmd/binapi-generator/parse.go | 557 | ||||
-rw-r--r-- | cmd/binapi-generator/parse_test.go | 97 | ||||
-rw-r--r-- | cmd/binapi-generator/testdata/acl.api.json | 929 | ||||
-rw-r--r-- | cmd/binapi-generator/testdata/af_packet.api.json | 163 | ||||
-rw-r--r-- | cmd/binapi-generator/testdata/input-generate-error.json | 3 | ||||
-rw-r--r-- | cmd/binapi-generator/testdata/input-read-json-error.json | 1 | ||||
-rw-r--r-- | cmd/binapi-generator/testdata/input.txt | 0 | ||||
-rw-r--r-- | cmd/binapi-generator/testdata/ip.api.json | 2246 | ||||
-rw-r--r-- | cmd/binapi-generator/types.go | 147 | ||||
-rw-r--r-- | cmd/binapi-generator/util.go | 81 |
17 files changed, 160 insertions, 6044 deletions
diff --git a/cmd/binapi-generator/definitions.go b/cmd/binapi-generator/definitions.go deleted file mode 100644 index 9bf9e53..0000000 --- a/cmd/binapi-generator/definitions.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2018 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 ( - "strings" - "unicode" -) - -// commonInitialisms is a set of common initialisms that need to stay in upper case. -var commonInitialisms = map[string]bool{ - "ACL": true, - "API": true, - //"ASCII": true, // there are only two use cases for ASCII which already have initialism before and after - "CPU": true, - "CSS": true, - "DNS": true, - "DHCP": true, - "EOF": true, - "GUID": true, - "HTML": true, - "HTTP": true, - "HTTPS": true, - "ID": true, - "IP": true, - "ICMP": true, - "JSON": true, - "LHS": true, - "QPS": true, - "PID": true, - "RAM": true, - "RHS": true, - "RPC": true, - "SLA": true, - "SMTP": true, - "SQL": true, - "SSH": true, - "TCP": true, - "TLS": true, - "TTL": true, - "UDP": true, - "UI": true, - "UID": true, - "UUID": true, - "URI": true, - "URL": true, - "UTF8": true, - "VM": true, - "VPN": true, - "XML": true, - "XMPP": true, - "XSRF": true, - "XSS": true, -} - -// specialInitialisms is a set of special initialisms that need part to stay in upper case. -var specialInitialisms = map[string]string{ - "IPV": "IPv", - //"IPV4": "IPv4", - //"IPV6": "IPv6", -} - -func usesInitialism(s string) string { - if u := strings.ToUpper(s); commonInitialisms[u] { - return u - } else if su, ok := specialInitialisms[u]; ok { - return su - } - return "" -} - -// camelCaseName returns correct name identifier (camelCase). -func camelCaseName(name string) (should string) { - name = strings.Title(name) - - // Fast path for simple cases: "_" and all lowercase. - if name == "_" { - return name - } - allLower := true - for _, r := range name { - if !unicode.IsLower(r) { - allLower = false - break - } - } - if allLower { - return name - } - - // Split camelCase at any lower->upper transition, and split on underscores. - // Check each word for common initialisms. - runes := []rune(name) - w, i := 0, 0 // index of start of word, scan - for i+1 <= len(runes) { - eow := false // whether we hit the end of a word - if i+1 == len(runes) { - eow = true - } else if runes[i+1] == '_' { - // underscore; shift the remainder forward over any run of underscores - eow = true - n := 1 - for i+n+1 < len(runes) && runes[i+n+1] == '_' { - n++ - } - - // Leave at most one underscore if the underscore is between two digits - if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) { - n-- - } - - copy(runes[i+1:], runes[i+n+1:]) - runes = runes[:len(runes)-n] - } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) { - // lower->non-lower - eow = true - } - i++ - if !eow { - continue - } - - // [w,i) is a word. - word := string(runes[w:i]) - if u := usesInitialism(word); u != "" { - // Keep consistent case, which is lowercase only at the start. - if w == 0 && unicode.IsLower(runes[w]) { - u = strings.ToLower(u) - } - // All the common initialisms are ASCII, - // so we can replace the bytes exactly. - copy(runes[w:], []rune(u)) - } else if w > 0 && strings.ToLower(word) == word { - // already all lowercase, and not the first word, so uppercase the first character. - runes[w] = unicode.ToUpper(runes[w]) - } - w = i - } - return string(runes) -} diff --git a/cmd/binapi-generator/definitions_test.go b/cmd/binapi-generator/definitions_test.go deleted file mode 100644 index 30c85ae..0000000 --- a/cmd/binapi-generator/definitions_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "testing" -) - -func TestInitialism(t *testing.T) { - tests := []struct { - name string - input string - expOutput string - }{ - {name: "id", input: "id", expOutput: "ID"}, - {name: "ipv6", input: "is_ipv6", expOutput: "IsIPv6"}, - {name: "ip6", input: "is_ip6", expOutput: "IsIP6"}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - output := camelCaseName(test.input) - if output != test.expOutput { - t.Errorf("expected %q, got %q", test.expOutput, output) - } - }) - } -} diff --git a/cmd/binapi-generator/doc.go b/cmd/binapi-generator/doc.go index d74d47b..9be49e2 100644 --- a/cmd/binapi-generator/doc.go +++ b/cmd/binapi-generator/doc.go @@ -1,4 +1,18 @@ -// Generator of Go structs out of the VPP binary API definitions in JSON format. +// 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. + +// Context of Go structs out of the VPP binary API definitions in JSON format. // // The JSON input can be specified as a single file (using the `input-file` // CLI flag), or as a directory that will be scanned for all `.json` files 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) -} diff --git a/cmd/binapi-generator/generate_test.go b/cmd/binapi-generator/generate_test.go deleted file mode 100644 index 4bec874..0000000 --- a/cmd/binapi-generator/generate_test.go +++ /dev/null @@ -1,450 +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 ( - "bufio" - "io/ioutil" - "os" - "testing" - - . "github.com/onsi/gomega" -) - -func TestGetInputFiles(t *testing.T) { - RegisterTestingT(t) - result, err := getInputFiles("testdata", 1) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result).To(HaveLen(3)) - for _, file := range result { - Expect(file).To(BeAnExistingFile()) - } -} - -func TestGetInputFilesError(t *testing.T) { - RegisterTestingT(t) - result, err := getInputFiles("nonexisting_directory", 1) - Expect(err).Should(HaveOccurred()) - Expect(result).To(BeNil()) -} - -func TestGenerateFromFile(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - // remove directory created during test - defer os.RemoveAll(outDir) - err := generateFromFile("testdata/acl.api.json", outDir, nil) - Expect(err).ShouldNot(HaveOccurred()) - fileInfo, err := os.Stat(outDir + "/acl/acl.ba.go") - Expect(err).ShouldNot(HaveOccurred()) - Expect(fileInfo.IsDir()).To(BeFalse()) - Expect(fileInfo.Name()).To(BeEquivalentTo("acl.ba.go")) -} - -func TestGenerateFromFileInputError(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - err := generateFromFile("testdata/nonexisting.json", outDir, nil) - Expect(err).Should(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("invalid input file name")) -} - -func TestGenerateFromFileReadJsonError(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - err := generateFromFile("testdata/input-read-json-error.json", outDir, nil) - Expect(err).Should(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("invalid input file name")) -} - -func TestGenerateFromFileGeneratePackageError(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - // generate package throws panic, recover after it - defer func() { - if recovery := recover(); recovery != nil { - t.Logf("Recovered from panic: %v", recovery) - } - os.RemoveAll(outDir) - }() - err := generateFromFile("testdata/input-generate-error.json", outDir, nil) - Expect(err).Should(HaveOccurred()) -} - -func TestGetContext(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - result, err := newContext("testdata/af_packet.api.json", outDir) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result).ToNot(BeNil()) - Expect(result.outputFile).To(BeEquivalentTo(outDir + "/af_packet/af_packet.ba.go")) -} - -func TestGetContextNoJsonFile(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - result, err := newContext("testdata/input.txt", outDir) - Expect(err).Should(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("invalid input file name")) - Expect(result).To(BeNil()) -} - -func TestGetContextInterfaceJson(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - result, err := newContext("testdata/ip.api.json", outDir) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result).ToNot(BeNil()) - Expect(result.outputFile) - Expect(result.outputFile).To(BeEquivalentTo(outDir + "/ip/ip.ba.go")) -} - -func TestReadJson(t *testing.T) { - RegisterTestingT(t) - inputData, err := ioutil.ReadFile("testdata/af_packet.api.json") - Expect(err).ShouldNot(HaveOccurred()) - result, err := parseInputJSON(inputData) - Expect(err).ShouldNot(HaveOccurred()) - Expect(result).ToNot(BeNil()) - Expect(result.Len()).To(BeEquivalentTo(5)) -} - -func TestReadJsonError(t *testing.T) { - RegisterTestingT(t) - inputData, err := ioutil.ReadFile("testdata/input-read-json-error.json") - Expect(err).ShouldNot(HaveOccurred()) - result, err := parseInputJSON(inputData) - Expect(err).Should(HaveOccurred()) - Expect(result).To(BeNil()) -} - -func TestGeneratePackage(t *testing.T) { - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - - // prepare input/output output files - inputData, err := ioutil.ReadFile("testdata/ip.api.json") - Expect(err).ShouldNot(HaveOccurred()) - jsonRoot, err := parseInputJSON(inputData) - Expect(err).ShouldNot(HaveOccurred()) - testCtx.packageData, err = parsePackage(testCtx, jsonRoot) - Expect(err).ShouldNot(HaveOccurred()) - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - defer os.RemoveAll(outDir) - - // prepare writer - writer := bufio.NewWriter(outFile) - Expect(writer.Buffered()).To(BeZero()) - err = generatePackage(testCtx, writer) - Expect(err).ShouldNot(HaveOccurred()) -} - -func TestGenerateMessageType(t *testing.T) { - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - - // prepare input/output output files - inputData, err := ioutil.ReadFile("testdata/ip.api.json") - Expect(err).ShouldNot(HaveOccurred()) - jsonRoot, err := parseInputJSON(inputData) - Expect(err).ShouldNot(HaveOccurred()) - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - testCtx.packageData, err = parsePackage(testCtx, jsonRoot) - Expect(err).ShouldNot(HaveOccurred()) - defer os.RemoveAll(outDir) - - // prepare writer - writer := bufio.NewWriter(outFile) - - for _, msg := range testCtx.packageData.Messages { - generateMessage(testCtx, writer, &msg) - Expect(writer.Buffered()).ToNot(BeZero()) - } -} - -/*func TestGenerateMessageName(t *testing.T) { - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - - // prepare input/output output files - inputData, err := readFile("testdata/ip.api.json") - Expect(err).ShouldNot(HaveOccurred()) - testCtx.inputBuff = bytes.NewBuffer(inputData) - inFile, _ := parseJSON(inputData) - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - defer os.RemoveAll(outDir) - - // prepare writer - writer := bufio.NewWriter(outFile) - - types := inFile.Map("types") - Expect(types.Len()).To(BeEquivalentTo(1)) - for i := 0; i < types.Len(); i++ { - typ := types.At(i) - Expect(writer.Buffered()).To(BeZero()) - err := generateMessage(testCtx, writer, typ, false) - Expect(err).ShouldNot(HaveOccurred()) - Expect(writer.Buffered()).ToNot(BeZero()) - - } -} - -func TestGenerateMessageFieldTypes(t *testing.T) { - // expected results according to acl.api.json in testdata - expectedTypes := []string{ - "\tIsPermit uint8", - "\tIsIpv6 uint8", - "\tSrcIPAddr []byte `struc:\"[16]byte\"`", - "\tSrcIPPrefixLen uint8", - "\tDstIPAddr []byte `struc:\"[16]byte\"`", - "\tDstIPPrefixLen uint8", - "\tProto uint8", - "\tSrcportOrIcmptypeFirst uint16", - "\tSrcportOrIcmptypeLast uint16", - "\tDstportOrIcmpcodeFirst uint16", - "\tDstportOrIcmpcodeLast uint16", - "\tTCPFlagsMask uint8", - "\tTCPFlagsValue uint8"} - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - - // prepare input/output output files - inputData, err := readFile("testdata/acl.api.json") - Expect(err).ShouldNot(HaveOccurred()) - inFile, err := parseJSON(inputData) - Expect(err).ShouldNot(HaveOccurred()) - Expect(inFile).ToNot(BeNil()) - - // test types - types := inFile.Map("types") - fields := make([]string, 0) - for i := 0; i < types.Len(); i++ { - for j := 0; j < types.At(i).Len(); j++ { - field := types.At(i).At(j) - if field.GetType() == jsongo.TypeArray { - err := processMessageField(testCtx, &fields, field, false) - Expect(err).ShouldNot(HaveOccurred()) - Expect(fields[j-1]).To(BeEquivalentTo(expectedTypes[j-1])) - } - } - } -} - -func TestGenerateMessageFieldMessages(t *testing.T) { - // expected results according to acl.api.json in testdata - expectedFields := []string{"\tMajor uint32", "\tMinor uint32", "\tRetval int32", - "\tVpePid uint32", "\tACLIndex uint32", "\tTag []byte `struc:\"[64]byte\"`", - "\tCount uint32"} - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - - // prepare input/output output files - inputData, err := readFile("testdata/acl.api.json") - Expect(err).ShouldNot(HaveOccurred()) - inFile, err := parseJSON(inputData) - Expect(err).ShouldNot(HaveOccurred()) - Expect(inFile).ToNot(BeNil()) - - // test message fields - messages := inFile.Map("messages") - customIndex := 0 - fields := make([]string, 0) - for i := 0; i < messages.Len(); i++ { - for j := 0; j < messages.At(i).Len(); j++ { - field := messages.At(i).At(j) - if field.GetType() == jsongo.TypeArray { - specificFieldName := field.At(1).Get().(string) - if specificFieldName == "crc" || specificFieldName == "_vl_msg_id" || - specificFieldName == "client_index" || specificFieldName == "context" { - continue - } - err := processMessageField(testCtx, &fields, field, false) - Expect(err).ShouldNot(HaveOccurred()) - Expect(fields[customIndex]).To(BeEquivalentTo(expectedFields[customIndex])) - customIndex++ - if customIndex >= len(expectedFields) { - // there is too much fields now for one UT... - return - } - } - } - } -} - -func TestGeneratePackageHeader(t *testing.T) { - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - - // prepare input/output output files - inputData, err := readFile("testdata/acl.api.json") - Expect(err).ShouldNot(HaveOccurred()) - inFile, err := parseJSON(inputData) - Expect(err).ShouldNot(HaveOccurred()) - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - defer os.RemoveAll(outDir) - // prepare writer - writer := bufio.NewWriter(outFile) - Expect(writer.Buffered()).To(BeZero()) - generateHeader(testCtx, writer, inFile) - Expect(writer.Buffered()).ToNot(BeZero()) -} - -func TestGenerateMessageCommentType(t *testing.T) { - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - testCtx.inputBuff = bytes.NewBuffer([]byte("test content")) - - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - writer := bufio.NewWriter(outFile) - defer os.RemoveAll(outDir) - Expect(writer.Buffered()).To(BeZero()) - generateMessageComment(testCtx, writer, "test-struct", "msg-name", true) - Expect(writer.Buffered()).ToNot(BeZero()) -} - -func TestGenerateMessageCommentMessage(t *testing.T) { - RegisterTestingT(t) - // prepare context - testCtx := new(context) - testCtx.packageName = "test-package-name" - testCtx.inputBuff = bytes.NewBuffer([]byte("test content")) - - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - writer := bufio.NewWriter(outFile) - defer os.RemoveAll(outDir) - Expect(writer.Buffered()).To(BeZero()) - generateMessageComment(testCtx, writer, "test-struct", "msg-name", false) - Expect(writer.Buffered()).ToNot(BeZero()) -} - -func TestGenerateMessageNameGetter(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - writer := bufio.NewWriter(outFile) - defer os.RemoveAll(outDir) - Expect(writer.Buffered()).To(BeZero()) - generateMessageNameGetter(writer, "test-struct", "msg-name") - Expect(writer.Buffered()).ToNot(BeZero()) -} - -func TestGenerateTypeNameGetter(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - writer := bufio.NewWriter(outFile) - defer os.RemoveAll(outDir) - Expect(writer.Buffered()).To(BeZero()) - generateTypeNameGetter(writer, "test-struct", "msg-name") - Expect(writer.Buffered()).ToNot(BeZero()) -} - -func TestGenerateCrcGetter(t *testing.T) { - RegisterTestingT(t) - outDir := "test_output_directory" - outFile, err := os.Create(outDir) - Expect(err).ShouldNot(HaveOccurred()) - writer := bufio.NewWriter(outFile) - defer os.RemoveAll(outDir) - Expect(writer.Buffered()).To(BeZero()) - generateCrcGetter(writer, "test-struct", "msg-name") - Expect(writer.Buffered()).ToNot(BeZero()) -} - -func TestTranslateVppType(t *testing.T) { - RegisterTestingT(t) - context := new(context) - typesToTranslate := []string{"u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f64"} - expected := []string{"uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64", "float64"} - var translated []string - for _, value := range typesToTranslate { - translated = append(translated, convertToGoType(context, value, false)) - } - for index, value := range expected { - Expect(value).To(BeEquivalentTo(translated[index])) - } - -} - -func TestTranslateVppTypeArray(t *testing.T) { - RegisterTestingT(t) - context := new(context) - translated := convertToGoType(context, "u8", true) - Expect(translated).To(BeEquivalentTo("byte")) -} - -func TestTranslateVppUnknownType(t *testing.T) { - defer func() { - if recovery := recover(); recovery != nil { - t.Logf("Recovered from panic: %v", recovery) - } - }() - context := new(context) - convertToGoType(context, "?", false) -} - -func TestCamelCase(t *testing.T) { - RegisterTestingT(t) - // test camel case functionality - expected := "allYourBaseAreBelongToUs" - result := camelCaseName("all_your_base_are_belong_to_us") - Expect(expected).To(BeEquivalentTo(result)) - // test underscore - expected = "_" - result = camelCaseName(expected) - Expect(expected).To(BeEquivalentTo(result)) - // test all lower - expected = "lower" - result = camelCaseName(expected) - Expect(expected).To(BeEquivalentTo(result)) -} - -func TestCommonInitialisms(t *testing.T) { - RegisterTestingT(t) - - for key, value := range commonInitialisms { - Expect(value).ShouldNot(BeFalse()) - Expect(key).ShouldNot(BeEmpty()) - } -} -*/ diff --git a/cmd/binapi-generator/main.go b/cmd/binapi-generator/main.go index fcd85ae..c0bdbb9 100644 --- a/cmd/binapi-generator/main.go +++ b/cmd/binapi-generator/main.go @@ -15,324 +15,102 @@ package main import ( - "bytes" - "encoding/json" "flag" "fmt" - "go/format" - "io/ioutil" "os" - "path/filepath" - "strings" - "github.com/bennyscetbun/jsongo" "github.com/sirupsen/logrus" + "git.fd.io/govpp.git/binapigen" + "git.fd.io/govpp.git/binapigen/vppapi" "git.fd.io/govpp.git/version" ) -var ( - theInputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.") - theInputTypes = flag.String("input-types", "", "Types input file with VPP API in JSON format. (split by comma)") - theInputDir = flag.String("input-dir", "/usr/share/vpp/api", "Input directory with VPP API files in JSON format.") - theOutputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.") - - includeAPIVer = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.") - includeServices = flag.Bool("include-services", true, "Include RPC service api and client implementation.") - includeComments = flag.Bool("include-comments", false, "Include JSON API source in comments for each object.") - includeBinapiNames = flag.Bool("include-binapi-names", false, "Include binary API names in struct tag.") - importPrefix = flag.String("import-prefix", "", "Define import path prefix to be used to import types.") - - continueOnError = flag.Bool("continue-onerror", false, "Continue with next file on error.") - debugMode = flag.Bool("debug", os.Getenv("GOVPP_DEBUG") != "", "Enable debug mode.") - - printVersion = flag.Bool("version", false, "Prints current version and exits.") -) +func init() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [OPTION]... [API]...\n", os.Args[0]) + fmt.Fprintln(flag.CommandLine.Output(), "Generate code for each API.") + fmt.Fprintf(flag.CommandLine.Output(), "Example: %s -output-dir=binapi acl interface l2\n", os.Args[0]) + fmt.Fprintln(flag.CommandLine.Output()) + fmt.Fprintln(flag.CommandLine.Output(), "Options:") + flag.CommandLine.PrintDefaults() + } +} func main() { + var ( + theInputFile = flag.String("input-file", "", "Input VPP API file. (DEPRECATED: Use program arguments to define VPP API files)") + theApiDir = flag.String("input-dir", vppapi.DefaultAPIDir, "Directory with VPP API files.") + theOutputDir = flag.String("output-dir", ".", "Output directory where code will be generated.") + + importPrefix = flag.String("import-prefix", "", "Define import path prefix to be used to import types.") + importTypes = flag.Bool("import-types", false, "Generate packages for imported types.") + includeAPIVer = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.") + includeServices = flag.Bool("include-services", true, "Include RPC service api and client implementation.") + includeComments = flag.Bool("include-comments", false, "Include JSON API source in comments for each object.") + includeBinapiNames = flag.Bool("include-binapi-names", true, "Include binary API names in struct tag.") + includeVppVersion = flag.Bool("include-vpp-version", true, "Include version of the VPP that provided input files.") + + debugMode = flag.Bool("debug", os.Getenv("DEBUG_GOVPP") != "", "Enable debug mode.") + printVersion = flag.Bool("version", false, "Prints version and exits.") + ) flag.Parse() - if flag.NArg() > 1 { - flag.Usage() - os.Exit(1) - } - - if flag.NArg() > 0 { - switch cmd := flag.Arg(0); cmd { - case "version": - fmt.Fprintln(os.Stdout, version.Verbose()) - os.Exit(0) - - default: - fmt.Fprintf(os.Stderr, "unknown command: %s\n", cmd) - flag.Usage() - os.Exit(2) - } - } - if *printVersion { fmt.Fprintln(os.Stdout, version.Info()) os.Exit(0) } - if *debugMode { - logrus.SetLevel(logrus.DebugLevel) - logrus.Info("debug mode enabled") - } - - if err := run(*theInputFile, *theInputDir, *theOutputDir, *continueOnError); err != nil { - logrus.Errorln("binapi-generator:", err) - os.Exit(1) + if flag.NArg() == 1 && flag.Arg(0) == "version" { + fmt.Fprintln(os.Stdout, version.Verbose()) + os.Exit(0) } -} -func run(inputFile, inputDir string, outputDir string, continueErr bool) (err error) { - if inputFile == "" && inputDir == "" { - return fmt.Errorf("input-file or input-dir must be specified") - } + var opts binapigen.Options - var typesPkgs []*context - if *theInputTypes != "" { - types := strings.Split(*theInputTypes, ",") - typesPkgs, err = loadTypesPackages(types...) - if err != nil { - return fmt.Errorf("loading types input failed: %v", err) - } - } - - if inputFile != "" { - // process one input file - if err := generateFromFile(inputFile, outputDir, typesPkgs); err != nil { - return fmt.Errorf("code generation from %s failed: %v\n", inputFile, err) + if *theInputFile != "" { + if flag.NArg() > 0 { + fmt.Fprintln(os.Stderr, "input-file cannot be combined with files to generate in arguments") + os.Exit(1) } + opts.FilesToGenerate = append(opts.FilesToGenerate, *theInputFile) } else { - // process all files in specified directory - dir, err := filepath.Abs(inputDir) - if err != nil { - return fmt.Errorf("invalid input directory: %v\n", err) - } - files, err := getInputFiles(inputDir, 1) - if err != nil { - return fmt.Errorf("problem getting files from input directory: %v\n", err) - } else if len(files) == 0 { - return fmt.Errorf("no input files found in input directory: %v\n", dir) - } - for _, file := range files { - if err := generateFromFile(file, outputDir, typesPkgs); err != nil { - if continueErr { - logrus.Warnf("code generation from %s failed: %v (error ignored)\n", file, err) - continue - } else { - return fmt.Errorf("code generation from %s failed: %v\n", file, err) - } - } - } - } - - return nil -} - -// getInputFiles returns all input files located in specified directory -func getInputFiles(inputDir string, deep int) (files []string, err error) { - entries, err := ioutil.ReadDir(inputDir) - if err != nil { - return nil, fmt.Errorf("reading directory %s failed: %v", inputDir, err) - } - for _, e := range entries { - if e.IsDir() && deep > 0 { - nestedDir := filepath.Join(inputDir, e.Name()) - if nested, err := getInputFiles(nestedDir, deep-1); err != nil { - return nil, err - } else { - files = append(files, nested...) - } - } else if strings.HasSuffix(e.Name(), inputFileExt) { - files = append(files, filepath.Join(inputDir, e.Name())) - } - } - return files, nil -} - -func parseInputJSON(inputData []byte) (*jsongo.Node, error) { - jsonRoot := new(jsongo.Node) - if err := json.Unmarshal(inputData, jsonRoot); err != nil { - return nil, fmt.Errorf("unmarshalling JSON failed: %v", err) - } - return jsonRoot, nil -} - -// generateFromFile generates Go package from one input JSON file -func generateFromFile(inputFile, outputDir string, typesPkgs []*context) error { - // create generator context - ctx, err := newContext(inputFile, outputDir) - if err != nil { - return err + opts.FilesToGenerate = append(opts.FilesToGenerate, flag.Args()...) } - logf("------------------------------------------------------------") - logf("module: %s", ctx.moduleName) - logf(" - input: %s", ctx.inputFile) - logf(" - output: %s", ctx.outputFile) - logf("------------------------------------------------------------") - // prepare options - ctx.includeAPIVersion = *includeAPIVer - ctx.includeComments = *includeComments - ctx.includeBinapiNames = *includeBinapiNames - ctx.includeServices = *includeServices - ctx.importPrefix = *importPrefix - - // read API definition from input file - ctx.inputData, err = ioutil.ReadFile(ctx.inputFile) - if err != nil { - return fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err) - } - // parse JSON data into objects - jsonRoot, err := parseInputJSON(ctx.inputData) - if err != nil { - return fmt.Errorf("parsing JSON input failed: %v", err) - } - ctx.packageData, err = parsePackage(ctx, jsonRoot) - if err != nil { - return fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err) - } - - if len(typesPkgs) > 0 { - err = loadTypeAliases(ctx, typesPkgs) - if err != nil { - return fmt.Errorf("loading type aliases failed: %v", err) - } - } - - // generate Go package - var buf bytes.Buffer - if err := generatePackage(ctx, &buf); err != nil { - return fmt.Errorf("generating Go package for %s failed: %v", ctx.packageName, err) - } - // format generated source code - gosrc, err := format.Source(buf.Bytes()) - if err != nil { - return fmt.Errorf("formatting source code for package %s failed: %v", ctx.packageName, err) + if ver := os.Getenv("VPP_API_VERSION"); ver != "" { + // use version from env var if set + opts.VPPVersion = ver + } else { + opts.VPPVersion = ResolveVppVersion(*theApiDir) } + opts.IncludeAPIVersion = *includeAPIVer + opts.IncludeComments = *includeComments + opts.IncludeBinapiNames = *includeBinapiNames + opts.IncludeServices = *includeServices + opts.IncludeVppVersion = *includeVppVersion + opts.ImportPrefix = *importPrefix + opts.ImportTypes = *importTypes - // create output directory - packageDir := filepath.Dir(ctx.outputFile) - if err := os.MkdirAll(packageDir, 0775); err != nil { - return fmt.Errorf("creating output dir %s failed: %v", packageDir, err) - } - // write generated code to output file - if err := ioutil.WriteFile(ctx.outputFile, gosrc, 0666); err != nil { - return fmt.Errorf("writing to output file %s failed: %v", ctx.outputFile, err) + if *debugMode { + logrus.SetLevel(logrus.DebugLevel) + logrus.Debug("debug mode enabled") } - return nil -} + apiDir := *theApiDir + outputDir := *theOutputDir -func loadTypesPackages(types ...string) ([]*context, error) { - var ctxs []*context - for _, inputFile := range types { - // create generator context - ctx, err := newContext(inputFile, "") - if err != nil { - return nil, err - } - // read API definition from input file - ctx.inputData, err = ioutil.ReadFile(ctx.inputFile) - if err != nil { - return nil, fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err) - } - // parse JSON data into objects - jsonRoot, err := parseInputJSON(ctx.inputData) - if err != nil { - return nil, fmt.Errorf("parsing JSON input failed: %v", err) - } - ctx.packageData, err = parsePackage(ctx, jsonRoot) - if err != nil { - return nil, fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err) - } - ctxs = append(ctxs, ctx) - } - return ctxs, nil -} - -func loadTypeAliases(ctx *context, typesCtxs []*context) error { - for _, t := range ctx.packageData.Types { - for _, c := range typesCtxs { - if _, ok := ctx.packageData.Imports[t.Name]; ok { - break - } - for _, at := range c.packageData.Types { - if at.Name != t.Name { - continue - } - if len(at.Fields) != len(t.Fields) { - continue - } - ctx.packageData.Imports[t.Name] = Import{ - Package: c.packageName, - } - } - } - } - for _, t := range ctx.packageData.Aliases { - for _, c := range typesCtxs { - if _, ok := ctx.packageData.Imports[t.Name]; ok { - break - } - for _, at := range c.packageData.Aliases { - if at.Name != t.Name { - continue - } - if at.Length != t.Length { - continue - } - if at.Type != t.Type { - continue - } - ctx.packageData.Imports[t.Name] = Import{ - Package: c.packageName, - } - } - } - } - for _, t := range ctx.packageData.Enums { - for _, c := range typesCtxs { - if _, ok := ctx.packageData.Imports[t.Name]; ok { - break + binapigen.Run(apiDir, opts, func(g *binapigen.Generator) error { + for _, file := range g.Files { + if !file.Generate { + continue } - for _, at := range c.packageData.Enums { - if at.Name != t.Name { - continue - } - if at.Type != t.Type { - continue - } - ctx.packageData.Imports[t.Name] = Import{ - Package: c.packageName, - } + binapigen.GenerateBinapiFile(g, file, outputDir) + if g.IncludeServices && file.Service != nil { + binapigen.GenerateRPC(g, file, outputDir) } } - } - for _, t := range ctx.packageData.Unions { - for _, c := range typesCtxs { - if _, ok := ctx.packageData.Imports[t.Name]; ok { - break - } - for _, at := range c.packageData.Unions { - if at.Name != t.Name { - continue - } - ctx.packageData.Imports[t.Name] = Import{ - Package: c.packageName, - } - } - } - } - return nil -} - -func logf(f string, v ...interface{}) { - if *debugMode { - logrus.Debugf(f, v...) - } + return nil + }) } diff --git a/cmd/binapi-generator/objects.go b/cmd/binapi-generator/objects.go deleted file mode 100644 index 9871abc..0000000 --- a/cmd/binapi-generator/objects.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import "fmt" - -// Package represents collection of objects parsed from VPP binary API JSON data -type Package struct { - Name string - Version string - CRC string - Services []Service - Enums []Enum - Aliases []Alias - Types []Type - Unions []Union - Messages []Message - RefMap map[string]string - Imports map[string]Import -} - -type Import struct { - Package string -} - -// Service represents VPP binary API service -type Service struct { - Name string - RequestType string - ReplyType string - Stream bool - Events []string -} - -// Enum represents VPP binary API enum -type Enum struct { - Name string - Type string - Entries []EnumEntry -} - -// EnumEntry represents VPP binary API enum entry -type EnumEntry struct { - Name string - Value interface{} -} - -// Alias represents VPP binary API alias -type Alias struct { - Name string - Type string - Length int -} - -// Type represents VPP binary API type -type Type struct { - Name string - CRC string - Fields []Field -} - -// Field represents VPP binary API object field -type Field struct { - Name string - Type string - Length int - SpecifiedLen bool - SizeFrom string - Meta FieldMeta -} - -// FieldMeta represents VPP binary API meta info for field -type FieldMeta struct { - Limit int - Default string -} - -// Union represents VPP binary API union -type Union struct { - Name string - CRC string - Fields []Field -} - -// Message represents VPP binary API message -type Message struct { - Name string - CRC string - Fields []Field -} - -// MessageType represents the type of a VPP message -type MessageType int - -const ( - requestMessage MessageType = iota // VPP request message - replyMessage // VPP reply message - eventMessage // VPP event message - otherMessage // other VPP message -) - -// printPackage prints all loaded objects for package -func printPackage(pkg *Package) { - logf("package: %s %s (%s)", pkg.Name, pkg.Version, pkg.CRC) - if len(pkg.Enums) > 0 { - logf(" %d enums:", len(pkg.Enums)) - for _, enum := range pkg.Enums { - logf(" - %s: %+v", enum.Name, enum) - } - } - if len(pkg.Unions) > 0 { - logf(" %d unions:", len(pkg.Unions)) - for _, union := range pkg.Unions { - logf(" - %s: %+v", union.Name, union) - } - } - if len(pkg.Types) > 0 { - logf(" %d types:", len(pkg.Types)) - for _, typ := range pkg.Types { - logf(" - %s (%d fields): %+v", typ.Name, len(typ.Fields), typ) - } - } - if len(pkg.Messages) > 0 { - logf(" %d messages:", len(pkg.Messages)) - for _, msg := range pkg.Messages { - logf(" - %s (%d fields) %s", msg.Name, len(msg.Fields), msg.CRC) - } - } - if len(pkg.Services) > 0 { - logf(" %d services:", len(pkg.Services)) - for _, svc := range pkg.Services { - var info string - if svc.Stream { - info = "(STREAM)" - } else if len(svc.Events) > 0 { - info = fmt.Sprintf("(EVENTS: %v)", svc.Events) - } - logf(" - %s: %q -> %q %s", svc.Name, svc.RequestType, svc.ReplyType, info) - } - } -} diff --git a/cmd/binapi-generator/parse.go b/cmd/binapi-generator/parse.go deleted file mode 100644 index 6598b7b..0000000 --- a/cmd/binapi-generator/parse.go +++ /dev/null @@ -1,557 +0,0 @@ -// Copyright (c) 2018 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 ( - "errors" - "fmt" - "sort" - "strings" - - "github.com/bennyscetbun/jsongo" - "github.com/sirupsen/logrus" -) - -// top level objects -const ( - objTypes = "types" - objMessages = "messages" - objUnions = "unions" - objEnums = "enums" - objServices = "services" - objAliases = "aliases" - vlAPIVersion = "vl_api_version" - objOptions = "options" -) - -// various object fields -const ( - crcField = "crc" - msgIdField = "_vl_msg_id" - - clientIndexField = "client_index" - contextField = "context" - - aliasLengthField = "length" - aliasTypeField = "type" - - replyField = "reply" - streamField = "stream" - eventsField = "events" -) - -// service name parts -const ( - serviceEventPrefix = "want_" - serviceDumpSuffix = "_dump" - serviceDetailsSuffix = "_details" - serviceReplySuffix = "_reply" - serviceNoReply = "null" -) - -// field meta info -const ( - fieldMetaLimit = "limit" - fieldMetaDefault = "default" -) - -// module options -const ( - versionOption = "version" -) - -// parsePackage parses provided JSON data into objects prepared for code generation -func parsePackage(ctx *context, jsonRoot *jsongo.Node) (*Package, error) { - pkg := Package{ - Name: ctx.packageName, - RefMap: make(map[string]string), - Imports: map[string]Import{}, - } - - // parse CRC for API version - if crc := jsonRoot.At(vlAPIVersion); crc.GetType() == jsongo.TypeValue { - pkg.CRC = crc.Get().(string) - } - - // parse version string - if opt := jsonRoot.Map(objOptions); opt.GetType() == jsongo.TypeMap { - if ver := opt.Map(versionOption); ver.GetType() == jsongo.TypeValue { - pkg.Version = ver.Get().(string) - } - } - - logf("parsing package %s (version: %s, CRC: %s)", pkg.Name, pkg.Version, pkg.CRC) - logf(" consists of:") - for _, key := range jsonRoot.GetKeys() { - logf(" - %d %s", jsonRoot.At(key).Len(), key) - } - - // parse enums - enums := jsonRoot.Map(objEnums) - pkg.Enums = make([]Enum, 0) - for i := 0; i < enums.Len(); i++ { - enumNode := enums.At(i) - - enum, err := parseEnum(ctx, enumNode) - if err != nil { - return nil, err - } - - enumApi := toApiType(enum.Name) - if _, ok := pkg.RefMap[enumApi]; ok { - logf("enum %v already known", enumApi) - continue - } - pkg.RefMap[enumApi] = enum.Name - pkg.Enums = append(pkg.Enums, *enum) - } - // sort enums - sort.SliceStable(pkg.Enums, func(i, j int) bool { - return pkg.Enums[i].Name < pkg.Enums[j].Name - }) - - // parse aliases - aliases := jsonRoot.Map(objAliases) - if aliases.GetType() == jsongo.TypeMap { - pkg.Aliases = make([]Alias, 0) - for _, key := range aliases.GetKeys() { - aliasNode := aliases.At(key) - - alias, err := parseAlias(ctx, key.(string), aliasNode) - if err != nil { - return nil, err - } - - aliasApi := toApiType(alias.Name) - if _, ok := pkg.RefMap[aliasApi]; ok { - logf("alias %v already known", aliasApi) - continue - } - pkg.RefMap[aliasApi] = alias.Name - pkg.Aliases = append(pkg.Aliases, *alias) - } - } - // sort aliases to ensure consistent order - sort.Slice(pkg.Aliases, func(i, j int) bool { - return pkg.Aliases[i].Name < pkg.Aliases[j].Name - }) - - // parse types - types := jsonRoot.Map(objTypes) - pkg.Types = make([]Type, 0) - for i := 0; i < types.Len(); i++ { - typNode := types.At(i) - - typ, err := parseType(ctx, typNode) - if err != nil { - return nil, err - } - - typApi := toApiType(typ.Name) - if _, ok := pkg.RefMap[typApi]; ok { - logf("type %v already known", typApi) - continue - } - pkg.RefMap[typApi] = typ.Name - pkg.Types = append(pkg.Types, *typ) - } - // sort types - sort.SliceStable(pkg.Types, func(i, j int) bool { - return pkg.Types[i].Name < pkg.Types[j].Name - }) - - // parse unions - unions := jsonRoot.Map(objUnions) - pkg.Unions = make([]Union, 0) - for i := 0; i < unions.Len(); i++ { - unionNode := unions.At(i) - - union, err := parseUnion(ctx, unionNode) - if err != nil { - return nil, err - } - - unionApi := toApiType(union.Name) - if _, ok := pkg.RefMap[unionApi]; ok { - logf("union %v already known", unionApi) - continue - } - pkg.RefMap[unionApi] = union.Name - pkg.Unions = append(pkg.Unions, *union) - } - // sort unions - sort.SliceStable(pkg.Unions, func(i, j int) bool { - return pkg.Unions[i].Name < pkg.Unions[j].Name - }) - - // parse messages - messages := jsonRoot.Map(objMessages) - pkg.Messages = make([]Message, messages.Len()) - for i := 0; i < messages.Len(); i++ { - msgNode := messages.At(i) - - msg, err := parseMessage(ctx, msgNode) - if err != nil { - return nil, err - } - pkg.Messages[i] = *msg - } - // sort messages - sort.SliceStable(pkg.Messages, func(i, j int) bool { - return pkg.Messages[i].Name < pkg.Messages[j].Name - }) - - // parse services - services := jsonRoot.Map(objServices) - if services.GetType() == jsongo.TypeMap { - pkg.Services = make([]Service, services.Len()) - for i, key := range services.GetKeys() { - svcNode := services.At(key) - - svc, err := parseService(ctx, key.(string), svcNode) - if err != nil { - return nil, err - } - pkg.Services[i] = *svc - } - } - // sort services - sort.Slice(pkg.Services, func(i, j int) bool { - // dumps first - if pkg.Services[i].Stream != pkg.Services[j].Stream { - return pkg.Services[i].Stream - } - return pkg.Services[i].RequestType < pkg.Services[j].RequestType - }) - - printPackage(&pkg) - - return &pkg, nil -} - -// parseEnum parses VPP binary API enum object from JSON node -func parseEnum(ctx *context, enumNode *jsongo.Node) (*Enum, error) { - if enumNode.Len() == 0 || enumNode.At(0).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for enum specified") - } - - enumName, ok := enumNode.At(0).Get().(string) - if !ok { - return nil, fmt.Errorf("enum name is %T, not a string", enumNode.At(0).Get()) - } - enumType, ok := enumNode.At(enumNode.Len() - 1).At("enumtype").Get().(string) - if !ok { - return nil, fmt.Errorf("enum type invalid or missing") - } - - enum := Enum{ - Name: enumName, - Type: enumType, - } - - // loop through enum entries, skip first (name) and last (enumtype) - for j := 1; j < enumNode.Len()-1; j++ { - if enumNode.At(j).GetType() == jsongo.TypeArray { - entry := enumNode.At(j) - - if entry.Len() < 2 || entry.At(0).GetType() != jsongo.TypeValue || entry.At(1).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for enum entry specified") - } - - entryName, ok := entry.At(0).Get().(string) - if !ok { - return nil, fmt.Errorf("enum entry name is %T, not a string", entry.At(0).Get()) - } - entryVal := entry.At(1).Get() - - enum.Entries = append(enum.Entries, EnumEntry{ - Name: entryName, - Value: entryVal, - }) - } - } - - return &enum, nil -} - -// parseUnion parses VPP binary API union object from JSON node -func parseUnion(ctx *context, unionNode *jsongo.Node) (*Union, error) { - if unionNode.Len() == 0 || unionNode.At(0).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for union specified") - } - - unionName, ok := unionNode.At(0).Get().(string) - if !ok { - return nil, fmt.Errorf("union name is %T, not a string", unionNode.At(0).Get()) - } - var unionCRC string - if unionNode.At(unionNode.Len()-1).GetType() == jsongo.TypeMap { - unionCRC = unionNode.At(unionNode.Len() - 1).At(crcField).Get().(string) - } - - union := Union{ - Name: unionName, - CRC: unionCRC, - } - - // loop through union fields, skip first (name) - for j := 1; j < unionNode.Len(); j++ { - if unionNode.At(j).GetType() == jsongo.TypeArray { - fieldNode := unionNode.At(j) - - field, err := parseField(ctx, fieldNode) - if err != nil { - return nil, err - } - - union.Fields = append(union.Fields, *field) - } - } - - return &union, nil -} - -// parseType parses VPP binary API type object from JSON node -func parseType(ctx *context, typeNode *jsongo.Node) (*Type, error) { - if typeNode.Len() == 0 || typeNode.At(0).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for type specified") - } - - typeName, ok := typeNode.At(0).Get().(string) - if !ok { - return nil, fmt.Errorf("type name is %T, not a string", typeNode.At(0).Get()) - } - var typeCRC string - if lastField := typeNode.At(typeNode.Len() - 1); lastField.GetType() == jsongo.TypeMap { - typeCRC = lastField.At(crcField).Get().(string) - } - - typ := Type{ - Name: typeName, - CRC: typeCRC, - } - - // loop through type fields, skip first (name) - for j := 1; j < typeNode.Len(); j++ { - if typeNode.At(j).GetType() == jsongo.TypeArray { - fieldNode := typeNode.At(j) - - field, err := parseField(ctx, fieldNode) - if err != nil { - return nil, err - } - - typ.Fields = append(typ.Fields, *field) - } - } - - return &typ, nil -} - -// parseAlias parses VPP binary API alias object from JSON node -func parseAlias(ctx *context, aliasName string, aliasNode *jsongo.Node) (*Alias, error) { - if aliasNode.Len() == 0 || aliasNode.At(aliasTypeField).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for alias specified") - } - - alias := Alias{ - Name: aliasName, - } - - if typeNode := aliasNode.At(aliasTypeField); typeNode.GetType() == jsongo.TypeValue { - typ, ok := typeNode.Get().(string) - if !ok { - return nil, fmt.Errorf("alias type is %T, not a string", typeNode.Get()) - } - if typ != "null" { - alias.Type = typ - } - } - - if lengthNode := aliasNode.At(aliasLengthField); lengthNode.GetType() == jsongo.TypeValue { - length, ok := lengthNode.Get().(float64) - if !ok { - return nil, fmt.Errorf("alias length is %T, not a float64", lengthNode.Get()) - } - alias.Length = int(length) - } - - return &alias, nil -} - -// parseMessage parses VPP binary API message object from JSON node -func parseMessage(ctx *context, msgNode *jsongo.Node) (*Message, error) { - if msgNode.Len() == 0 || msgNode.At(0).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for message specified") - } - - msgName, ok := msgNode.At(0).Get().(string) - if !ok { - return nil, fmt.Errorf("message name is %T, not a string", msgNode.At(0).Get()) - } - msgCRC, ok := msgNode.At(msgNode.Len() - 1).At(crcField).Get().(string) - if !ok { - - return nil, fmt.Errorf("message crc invalid or missing") - } - - msg := Message{ - Name: msgName, - CRC: msgCRC, - } - - // loop through message fields, skip first (name) and last (crc) - for j := 1; j < msgNode.Len()-1; j++ { - if msgNode.At(j).GetType() == jsongo.TypeArray { - fieldNode := msgNode.At(j) - - field, err := parseField(ctx, fieldNode) - if err != nil { - return nil, err - } - - msg.Fields = append(msg.Fields, *field) - } - } - - return &msg, nil -} - -// parseField parses VPP binary API object field from JSON node -func parseField(ctx *context, field *jsongo.Node) (*Field, error) { - if field.Len() < 2 || field.At(0).GetType() != jsongo.TypeValue || field.At(1).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for field specified") - } - - fieldType, ok := field.At(0).Get().(string) - if !ok { - return nil, fmt.Errorf("field type is %T, not a string", field.At(0).Get()) - } - fieldName, ok := field.At(1).Get().(string) - if !ok { - return nil, fmt.Errorf("field name is %T, not a string", field.At(1).Get()) - } - - f := &Field{ - Name: fieldName, - Type: fieldType, - } - - if field.Len() >= 3 { - switch field.At(2).GetType() { - case jsongo.TypeValue: - fieldLength, ok := field.At(2).Get().(float64) - if !ok { - return nil, fmt.Errorf("field length is %T, not float64", field.At(2).Get()) - } - f.Length = int(fieldLength) - f.SpecifiedLen = true - - case jsongo.TypeMap: - fieldMeta := field.At(2) - - for _, key := range fieldMeta.GetKeys() { - metaNode := fieldMeta.At(key) - - switch metaName := key.(string); metaName { - case fieldMetaLimit: - f.Meta.Limit = int(metaNode.Get().(float64)) - case fieldMetaDefault: - f.Meta.Default = fmt.Sprint(metaNode.Get()) - default: - logrus.Warnf("unknown meta info (%s) for field (%s)", metaName, fieldName) - } - } - default: - return nil, errors.New("invalid JSON for field specified") - } - } - if field.Len() >= 4 { - fieldLengthFrom, ok := field.At(3).Get().(string) - if !ok { - return nil, fmt.Errorf("field length from is %T, not a string", field.At(3).Get()) - } - f.SizeFrom = fieldLengthFrom - } - - return f, nil -} - -// parseService parses VPP binary API service object from JSON node -func parseService(ctx *context, svcName string, svcNode *jsongo.Node) (*Service, error) { - if svcNode.Len() == 0 || svcNode.At(replyField).GetType() != jsongo.TypeValue { - return nil, errors.New("invalid JSON for service specified") - } - - svc := Service{ - Name: svcName, - RequestType: svcName, - } - - if replyNode := svcNode.At(replyField); replyNode.GetType() == jsongo.TypeValue { - reply, ok := replyNode.Get().(string) - if !ok { - return nil, fmt.Errorf("service reply is %T, not a string", replyNode.Get()) - } - if reply != serviceNoReply { - svc.ReplyType = reply - } - } - - // stream service (dumps) - if streamNode := svcNode.At(streamField); streamNode.GetType() == jsongo.TypeValue { - var ok bool - svc.Stream, ok = streamNode.Get().(bool) - if !ok { - return nil, fmt.Errorf("service stream is %T, not a string", streamNode.Get()) - } - } - - // events service (event subscription) - if eventsNode := svcNode.At(eventsField); eventsNode.GetType() == jsongo.TypeArray { - for j := 0; j < eventsNode.Len(); j++ { - event := eventsNode.At(j).Get().(string) - svc.Events = append(svc.Events, event) - } - } - - // validate service - if len(svc.Events) > 0 { - // EVENT service - if !strings.HasPrefix(svc.RequestType, serviceEventPrefix) { - logrus.Debugf("unusual EVENTS service: %+v\n"+ - "- events service %q does not have %q prefix in request.", - svc, svc.Name, serviceEventPrefix) - } - } else if svc.Stream { - // STREAM service - if !strings.HasSuffix(svc.RequestType, serviceDumpSuffix) || - !strings.HasSuffix(svc.ReplyType, serviceDetailsSuffix) { - logrus.Debugf("unusual STREAM service: %+v\n"+ - "- stream service %q does not have %q suffix in request or reply does not have %q suffix.", - svc, svc.Name, serviceDumpSuffix, serviceDetailsSuffix) - } - } else if svc.ReplyType != "" && svc.ReplyType != serviceNoReply { - // REQUEST service - // some messages might have `null` reply (for example: memclnt) - if !strings.HasSuffix(svc.ReplyType, serviceReplySuffix) { - logrus.Debugf("unusual REQUEST service: %+v\n"+ - "- service %q does not have %q suffix in reply.", - svc, svc.Name, serviceReplySuffix) - } - } - - return &svc, nil -} diff --git a/cmd/binapi-generator/parse_test.go b/cmd/binapi-generator/parse_test.go deleted file mode 100644 index e145979..0000000 --- a/cmd/binapi-generator/parse_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "testing" -) - -func TestBinapiTypeSizes(t *testing.T) { - tests := []struct { - name string - input string - expsize int - }{ - {name: "basic1", input: "u8", expsize: 1}, - {name: "basic2", input: "i8", expsize: 1}, - {name: "basic3", input: "u16", expsize: 2}, - {name: "basic4", input: "i32", expsize: 4}, - {name: "invalid1", input: "x", expsize: -1}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - size := getBinapiTypeSize(test.input) - if size != test.expsize { - t.Errorf("expected %d, got %d", test.expsize, size) - } - }) - } -} - -func TestSizeOfType(t *testing.T) { - tests := []struct { - name string - input Type - expsize int - }{ - { - name: "basic1", - input: Type{ - Fields: []Field{ - {Type: "u8"}, - }, - }, - expsize: 1, - }, - { - name: "basic2", - input: Type{ - Fields: []Field{ - {Type: "u8", Length: 4}, - }, - }, - expsize: 4, - }, - { - name: "basic3", - input: Type{ - Fields: []Field{ - {Type: "u8", Length: 16}, - }, - }, - expsize: 16, - }, - { - name: "withEnum", - input: Type{ - Fields: []Field{ - {Type: "u16"}, - {Type: "vl_api_myenum_t"}, - }, - }, - expsize: 6, - }, - { - name: "invalid1", - input: Type{ - Fields: []Field{ - {Type: "x", Length: 16}, - }, - }, - expsize: 0, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - ctx := &context{ - packageData: &Package{ - Enums: []Enum{ - {Name: "myenum", Type: "u32"}, - }, - }, - } - size := getSizeOfType(ctx, &test.input) - if size != test.expsize { - t.Errorf("expected %d, got %d", test.expsize, size) - } - }) - } -} diff --git a/cmd/binapi-generator/testdata/acl.api.json b/cmd/binapi-generator/testdata/acl.api.json deleted file mode 100644 index 4c6653c..0000000 --- a/cmd/binapi-generator/testdata/acl.api.json +++ /dev/null @@ -1,929 +0,0 @@ -{ - "services": [ - { - "acl_interface_add_del": { - "reply": "acl_interface_add_del_reply" - } - }, - { - "acl_del": { - "reply": "acl_del_reply" - } - }, - { - "macip_acl_del": { - "reply": "macip_acl_del_reply" - } - }, - { - "acl_plugin_get_version": { - "reply": "acl_plugin_get_version_reply" - } - }, - { - "macip_acl_interface_add_del": { - "reply": "macip_acl_interface_add_del_reply" - } - }, - { - "acl_interface_set_acl_list": { - "reply": "acl_interface_set_acl_list_reply" - } - }, - { - "acl_dump": { - "reply": "acl_details", - "stream": true - } - }, - { - "acl_interface_list_dump": { - "reply": "acl_interface_list_details", - "stream": true - } - }, - { - "macip_acl_interface_list_dump": { - "reply": "macip_acl_interface_list_details", - "stream": true - } - }, - { - "acl_add_replace": { - "reply": "acl_add_replace_reply" - } - }, - { - "acl_plugin_control_ping": { - "reply": "acl_plugin_control_ping_reply" - } - }, - { - "macip_acl_interface_get": { - "reply": "macip_acl_interface_get_reply" - } - }, - { - "macip_acl_add": { - "reply": "macip_acl_add_reply" - } - }, - { - "macip_acl_add_replace": { - "reply": "macip_acl_add_replace_reply" - } - }, - { - "macip_acl_dump": { - "reply": "macip_acl_details", - "stream": true - } - } - ], - "vl_api_version": "0x1db2ece9", - "enums": [], - "messages": [ - [ - "acl_plugin_get_version", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "acl_plugin_get_version_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "major" - ], - [ - "u32", - "minor" - ], - { - "crc": "0x9b32cf86" - } - ], - [ - "acl_plugin_control_ping", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "acl_plugin_control_ping_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "vpe_pid" - ], - { - "crc": "0xf6b0b8ca" - } - ], - [ - "acl_add_replace", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - [ - "u8", - "tag", - 64 - ], - [ - "u32", - "count" - ], - [ - "vl_api_acl_rule_t", - "r", - 0, - "count" - ], - { - "crc": "0xe839997e" - } - ], - [ - "acl_add_replace_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xac407b0c" - } - ], - [ - "acl_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - { - "crc": "0xef34fea4" - } - ], - [ - "acl_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "acl_interface_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "is_input" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u32", - "acl_index" - ], - { - "crc": "0x0b2aedd1" - } - ], - [ - "acl_interface_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "acl_interface_set_acl_list", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "count" - ], - [ - "u8", - "n_input" - ], - [ - "u32", - "acls", - 0, - "count" - ], - { - "crc": "0x8baece38" - } - ], - [ - "acl_interface_set_acl_list_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "acl_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - { - "crc": "0xef34fea4" - } - ], - [ - "acl_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - [ - "u8", - "tag", - 64 - ], - [ - "u32", - "count" - ], - [ - "vl_api_acl_rule_t", - "r", - 0, - "count" - ], - { - "crc": "0x5bd895be" - } - ], - [ - "acl_interface_list_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - { - "crc": "0x529cb13f" - } - ], - [ - "acl_interface_list_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "count" - ], - [ - "u8", - "n_input" - ], - [ - "u32", - "acls", - 0, - "count" - ], - { - "crc": "0xd5e80809" - } - ], - [ - "macip_acl_add", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "tag", - 64 - ], - [ - "u32", - "count" - ], - [ - "vl_api_macip_acl_rule_t", - "r", - 0, - "count" - ], - { - "crc": "0xb3d3d65a" - } - ], - [ - "macip_acl_add_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xac407b0c" - } - ], - [ - "macip_acl_add_replace", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - [ - "u8", - "tag", - 64 - ], - [ - "u32", - "count" - ], - [ - "vl_api_macip_acl_rule_t", - "r", - 0, - "count" - ], - { - "crc": "0xa0e8c01b" - } - ], - [ - "macip_acl_add_replace_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xac407b0c" - } - ], - [ - "macip_acl_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - { - "crc": "0xef34fea4" - } - ], - [ - "macip_acl_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "macip_acl_interface_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "is_add" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u32", - "acl_index" - ], - { - "crc": "0x6a6be97c" - } - ], - [ - "macip_acl_interface_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "macip_acl_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - { - "crc": "0xef34fea4" - } - ], - [ - "macip_acl_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "acl_index" - ], - [ - "u8", - "tag", - 64 - ], - [ - "u32", - "count" - ], - [ - "vl_api_macip_acl_rule_t", - "r", - 0, - "count" - ], - { - "crc": "0xdd2b55ba" - } - ], - [ - "macip_acl_interface_get", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "macip_acl_interface_get_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "count" - ], - [ - "u32", - "acls", - 0, - "count" - ], - { - "crc": "0xaccf9b05" - } - ], - [ - "macip_acl_interface_list_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - { - "crc": "0x529cb13f" - } - ], - [ - "macip_acl_interface_list_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "count" - ], - [ - "u32", - "acls", - 0, - "count" - ], - { - "crc": "0x29783fa0" - } - ] - ], - "types": [ - [ - "acl_rule", - [ - "u8", - "is_permit" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "src_ip_addr", - 16 - ], - [ - "u8", - "src_ip_prefix_len" - ], - [ - "u8", - "dst_ip_addr", - 16 - ], - [ - "u8", - "dst_ip_prefix_len" - ], - [ - "u8", - "proto" - ], - [ - "u16", - "srcport_or_icmptype_first" - ], - [ - "u16", - "srcport_or_icmptype_last" - ], - [ - "u16", - "dstport_or_icmpcode_first" - ], - [ - "u16", - "dstport_or_icmpcode_last" - ], - [ - "u8", - "tcp_flags_mask" - ], - [ - "u8", - "tcp_flags_value" - ], - { - "crc": "0x6f99bf4d" - } - ], - [ - "macip_acl_rule", - [ - "u8", - "is_permit" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "src_mac", - 6 - ], - [ - "u8", - "src_mac_mask", - 6 - ], - [ - "u8", - "src_ip_addr", - 16 - ], - [ - "u8", - "src_ip_prefix_len" - ], - { - "crc": "0x70589f1e" - } - ] - ] -} diff --git a/cmd/binapi-generator/testdata/af_packet.api.json b/cmd/binapi-generator/testdata/af_packet.api.json deleted file mode 100644 index 81ee2f9..0000000 --- a/cmd/binapi-generator/testdata/af_packet.api.json +++ /dev/null @@ -1,163 +0,0 @@ -{ - "services": [ - { - "af_packet_delete": { - "reply": "af_packet_delete_reply" - } - }, - { - "af_packet_create": { - "reply": "af_packet_create_reply" - } - }, - { - "af_packet_set_l4_cksum_offload": { - "reply": "af_packet_set_l4_cksum_offload_reply" - } - } - ], - "vl_api_version": "0x8957ca8b", - "enums": [], - "messages": [ - [ - "af_packet_create", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "host_if_name", - 64 - ], - [ - "u8", - "hw_addr", - 6 - ], - [ - "u8", - "use_random_hw_addr" - ], - { - "crc": "0x6d5d30d6" - } - ], - [ - "af_packet_create_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - [ - "u32", - "sw_if_index" - ], - { - "crc": "0xfda5941f" - } - ], - [ - "af_packet_delete", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "host_if_name", - 64 - ], - { - "crc": "0x3efceda3" - } - ], - [ - "af_packet_delete_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "af_packet_set_l4_cksum_offload", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "sw_if_index" - ], - [ - "u8", - "set" - ], - { - "crc": "0x86538585" - } - ], - [ - "af_packet_set_l4_cksum_offload_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ] - ], - "types": [] -} diff --git a/cmd/binapi-generator/testdata/input-generate-error.json b/cmd/binapi-generator/testdata/input-generate-error.json deleted file mode 100644 index d5df76e..0000000 --- a/cmd/binapi-generator/testdata/input-generate-error.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "key": "value" -}
\ No newline at end of file diff --git a/cmd/binapi-generator/testdata/input-read-json-error.json b/cmd/binapi-generator/testdata/input-read-json-error.json deleted file mode 100644 index 02691e3..0000000 --- a/cmd/binapi-generator/testdata/input-read-json-error.json +++ /dev/null @@ -1 +0,0 @@ -%
\ No newline at end of file diff --git a/cmd/binapi-generator/testdata/input.txt b/cmd/binapi-generator/testdata/input.txt deleted file mode 100644 index e69de29..0000000 --- a/cmd/binapi-generator/testdata/input.txt +++ /dev/null diff --git a/cmd/binapi-generator/testdata/ip.api.json b/cmd/binapi-generator/testdata/ip.api.json deleted file mode 100644 index 530b6d6..0000000 --- a/cmd/binapi-generator/testdata/ip.api.json +++ /dev/null @@ -1,2246 +0,0 @@ -{ - "services": [ - { - "ip_source_and_port_range_check_add_del": { - "reply": "ip_source_and_port_range_check_add_del_reply" - } - }, - { - "ip6_fib_dump": { - "reply": "ip6_fib_details", - "stream": true - } - }, - { - "want_ip6_nd_events": { - "reply": "want_ip6_nd_events_reply" - } - }, - { - "ip_punt_police": { - "reply": "ip_punt_police_reply" - } - }, - { - "set_arp_neighbor_limit": { - "reply": "set_arp_neighbor_limit_reply" - } - }, - { - "ip6nd_proxy_add_del": { - "reply": "ip6nd_proxy_add_del_reply" - } - }, - { - "ioam_disable": { - "reply": "ioam_disable_reply" - } - }, - { - "ip_table_add_del": { - "reply": "ip_table_add_del_reply" - } - }, - { - "ip_neighbor_dump": { - "reply": "ip_neighbor_details", - "stream": true - } - }, - { - "ip4_arp_event": { - "reply": null - } - }, - { - "ip_punt_redirect": { - "reply": "ip_punt_redirect_reply" - } - }, - { - "sw_interface_ip6nd_ra_prefix": { - "reply": "sw_interface_ip6nd_ra_prefix_reply" - } - }, - { - "reset_fib": { - "reply": "reset_fib_reply" - } - }, - { - "ip6_mfib_dump": { - "reply": "ip6_mfib_details", - "stream": true - } - }, - { - "sw_interface_ip6nd_ra_config": { - "reply": "sw_interface_ip6nd_ra_config_reply" - } - }, - { - "sw_interface_ip6_enable_disable": { - "reply": "sw_interface_ip6_enable_disable_reply" - } - }, - { - "sw_interface_ip6_set_link_local_address": { - "reply": "sw_interface_ip6_set_link_local_address_reply" - } - }, - { - "mfib_signal_dump": { - "reply": "mfib_signal_details", - "stream": true - } - }, - { - "ip_container_proxy_add_del": { - "reply": "ip_container_proxy_add_del_reply" - } - }, - { - "ip_mfib_dump": { - "reply": "ip_mfib_details", - "stream": true - } - }, - { - "ip_address_dump": { - "reply": "ip_address_details", - "stream": true - } - }, - { - "ip_dump": { - "reply": "ip_details", - "stream": true - } - }, - { - "ip_neighbor_add_del": { - "reply": "ip_neighbor_add_del_reply" - } - }, - { - "proxy_arp_intfc_enable_disable": { - "reply": "proxy_arp_intfc_enable_disable_reply" - } - }, - { - "proxy_arp_add_del": { - "reply": "proxy_arp_add_del_reply" - } - }, - { - "ip_add_del_route": { - "reply": "ip_add_del_route_reply" - } - }, - { - "ip6nd_proxy_dump": { - "reply": "ip6nd_proxy_details", - "stream": true - } - }, - { - "ip_fib_dump": { - "reply": "ip_fib_details", - "stream": true - } - }, - { - "want_ip4_arp_events": { - "reply": "want_ip4_arp_events_reply" - } - }, - { - "ioam_enable": { - "reply": "ioam_enable_reply" - } - }, - { - "ip6_nd_event": { - "reply": null - } - }, - { - "ip_mroute_add_del": { - "reply": "ip_mroute_add_del_reply" - } - }, - { - "ip_source_and_port_range_check_interface_add_del": { - "reply": "ip_source_and_port_range_check_interface_add_del_reply" - } - }, - { - "set_ip_flow_hash": { - "reply": "set_ip_flow_hash_reply" - } - } - ], - "vl_api_version": "0xb395c625", - "enums": [], - "messages": [ - [ - "ip_table_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "table_id" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "name", - 64 - ], - { - "crc": "0x0240c89d" - } - ], - [ - "ip_table_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_fib_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "ip_fib_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "table_id" - ], - [ - "u8", - "table_name", - 64 - ], - [ - "u8", - "address_length" - ], - [ - "u8", - "address", - 4 - ], - [ - "u32", - "count" - ], - [ - "vl_api_fib_path_t", - "path", - 0, - "count" - ], - { - "crc": "0x99dfd73b" - } - ], - [ - "ip6_fib_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "ip6_fib_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "table_id" - ], - [ - "u8", - "table_name", - 64 - ], - [ - "u8", - "address_length" - ], - [ - "u8", - "address", - 16 - ], - [ - "u32", - "count" - ], - [ - "vl_api_fib_path_t", - "path", - 0, - "count" - ], - { - "crc": "0xabd0060e" - } - ], - [ - "ip_neighbor_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_ipv6" - ], - { - "crc": "0x6b7bcd0a" - } - ], - [ - "ip_neighbor_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_static" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "mac_address", - 6 - ], - [ - "u8", - "ip_address", - 16 - ], - { - "crc": "0x85e32a72" - } - ], - [ - "ip_neighbor_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "is_static" - ], - [ - "u8", - "is_no_adj_fib" - ], - [ - "u8", - "mac_address", - 6 - ], - [ - "u8", - "dst_address", - 16 - ], - { - "crc": "0x4711eb25" - } - ], - [ - "ip_neighbor_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "set_ip_flow_hash", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "vrf_id" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "src" - ], - [ - "u8", - "dst" - ], - [ - "u8", - "sport" - ], - [ - "u8", - "dport" - ], - [ - "u8", - "proto" - ], - [ - "u8", - "reverse" - ], - { - "crc": "0x32ebf737" - } - ], - [ - "set_ip_flow_hash_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "sw_interface_ip6nd_ra_config", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "suppress" - ], - [ - "u8", - "managed" - ], - [ - "u8", - "other" - ], - [ - "u8", - "ll_option" - ], - [ - "u8", - "send_unicast" - ], - [ - "u8", - "cease" - ], - [ - "u8", - "is_no" - ], - [ - "u8", - "default_router" - ], - [ - "u32", - "max_interval" - ], - [ - "u32", - "min_interval" - ], - [ - "u32", - "lifetime" - ], - [ - "u32", - "initial_count" - ], - [ - "u32", - "initial_interval" - ], - { - "crc": "0xc3f02daa" - } - ], - [ - "sw_interface_ip6nd_ra_config_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "sw_interface_ip6nd_ra_prefix", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "address", - 16 - ], - [ - "u8", - "address_length" - ], - [ - "u8", - "use_default" - ], - [ - "u8", - "no_advertise" - ], - [ - "u8", - "off_link" - ], - [ - "u8", - "no_autoconfig" - ], - [ - "u8", - "no_onlink" - ], - [ - "u8", - "is_no" - ], - [ - "u32", - "val_lifetime" - ], - [ - "u32", - "pref_lifetime" - ], - { - "crc": "0xca763c9a" - } - ], - [ - "sw_interface_ip6nd_ra_prefix_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip6nd_proxy_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_del" - ], - [ - "u8", - "address", - 16 - ], - { - "crc": "0xd95f0fa0" - } - ], - [ - "ip6nd_proxy_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip6nd_proxy_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "address", - 16 - ], - { - "crc": "0xd73bf1ab" - } - ], - [ - "ip6nd_proxy_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "sw_interface_ip6_enable_disable", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "enable" - ], - { - "crc": "0xa36fadc0" - } - ], - [ - "sw_interface_ip6_enable_disable_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "sw_interface_ip6_set_link_local_address", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "address", - 16 - ], - { - "crc": "0xd73bf1ab" - } - ], - [ - "sw_interface_ip6_set_link_local_address_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_add_del_route", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "next_hop_sw_if_index" - ], - [ - "u32", - "table_id" - ], - [ - "u32", - "classify_table_index" - ], - [ - "u32", - "next_hop_table_id" - ], - [ - "u32", - "next_hop_id" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "is_drop" - ], - [ - "u8", - "is_unreach" - ], - [ - "u8", - "is_prohibit" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "is_local" - ], - [ - "u8", - "is_classify" - ], - [ - "u8", - "is_multipath" - ], - [ - "u8", - "is_resolve_host" - ], - [ - "u8", - "is_resolve_attached" - ], - [ - "u8", - "is_dvr" - ], - [ - "u8", - "is_source_lookup" - ], - [ - "u8", - "is_udp_encap" - ], - [ - "u8", - "next_hop_weight" - ], - [ - "u8", - "next_hop_preference" - ], - [ - "u8", - "next_hop_proto" - ], - [ - "u8", - "dst_address_length" - ], - [ - "u8", - "dst_address", - 16 - ], - [ - "u8", - "next_hop_address", - 16 - ], - [ - "u8", - "next_hop_n_out_labels" - ], - [ - "u32", - "next_hop_via_label" - ], - [ - "u32", - "next_hop_out_label_stack", - 0, - "next_hop_n_out_labels" - ], - { - "crc": "0xc85f8290" - } - ], - [ - "ip_add_del_route_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_mroute_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "next_hop_sw_if_index" - ], - [ - "u32", - "table_id" - ], - [ - "u32", - "entry_flags" - ], - [ - "u32", - "itf_flags" - ], - [ - "u32", - "rpf_id" - ], - [ - "u32", - "bier_imp" - ], - [ - "u16", - "grp_address_length" - ], - [ - "u8", - "next_hop_afi" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "is_local" - ], - [ - "u8", - "grp_address", - 16 - ], - [ - "u8", - "src_address", - 16 - ], - { - "crc": "0xc37112f7" - } - ], - [ - "ip_mroute_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_mfib_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "ip_mfib_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "table_id" - ], - [ - "u32", - "entry_flags" - ], - [ - "u32", - "rpf_id" - ], - [ - "u8", - "address_length" - ], - [ - "u8", - "grp_address", - 4 - ], - [ - "u8", - "src_address", - 4 - ], - [ - "u32", - "count" - ], - [ - "vl_api_fib_path_t", - "path", - 0, - "count" - ], - { - "crc": "0x5e530d5e" - } - ], - [ - "ip6_mfib_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "ip6_mfib_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "u32", - "table_id" - ], - [ - "u8", - "address_length" - ], - [ - "u8", - "grp_address", - 16 - ], - [ - "u8", - "src_address", - 16 - ], - [ - "u32", - "count" - ], - [ - "vl_api_fib_path_t", - "path", - 0, - "count" - ], - { - "crc": "0xe02dcb4b" - } - ], - [ - "ip_address_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "ip", - 16 - ], - [ - "u8", - "prefix_length" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_ipv6" - ], - { - "crc": "0xbc7442f2" - } - ], - [ - "ip_address_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_ipv6" - ], - { - "crc": "0x6b7bcd0a" - } - ], - [ - "ip_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "is_ipv6" - ], - { - "crc": "0x452ffc5a" - } - ], - [ - "ip_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "is_ipv6" - ], - { - "crc": "0xde883da4" - } - ], - [ - "mfib_signal_dump", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - { - "crc": "0x51077d14" - } - ], - [ - "mfib_signal_details", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u32", - "table_id" - ], - [ - "u16", - "grp_address_len" - ], - [ - "u8", - "grp_address", - 16 - ], - [ - "u8", - "src_address", - 16 - ], - [ - "u16", - "ip_packet_len" - ], - [ - "u8", - "ip_packet_data", - 256 - ], - { - "crc": "0x791bbeab" - } - ], - [ - "ip_punt_police", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "policer_index" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "is_ip6" - ], - { - "crc": "0x38691592" - } - ], - [ - "ip_punt_police_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_punt_redirect", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "rx_sw_if_index" - ], - [ - "u32", - "tx_sw_if_index" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "is_ip6" - ], - [ - "u8", - "nh", - 16 - ], - { - "crc": "0x996b6603" - } - ], - [ - "ip_punt_redirect_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_container_proxy_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "ip", - 16 - ], - [ - "u8", - "is_ip4" - ], - [ - "u8", - "plen" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_add" - ], - { - "crc": "0x0a355d39" - } - ], - [ - "ip_container_proxy_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_source_and_port_range_check_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "mask_length" - ], - [ - "u8", - "address", - 16 - ], - [ - "u8", - "number_of_ranges" - ], - [ - "u16", - "low_ports", - 32 - ], - [ - "u16", - "high_ports", - 32 - ], - [ - "u32", - "vrf_id" - ], - { - "crc": "0x03d6b03a" - } - ], - [ - "ip_source_and_port_range_check_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_source_and_port_range_check_interface_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "is_add" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u32", - "tcp_in_vrf_id" - ], - [ - "u32", - "tcp_out_vrf_id" - ], - [ - "u32", - "udp_in_vrf_id" - ], - [ - "u32", - "udp_out_vrf_id" - ], - { - "crc": "0x6966bc44" - } - ], - [ - "ip_source_and_port_range_check_interface_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "want_ip4_arp_events", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "enable_disable" - ], - [ - "u32", - "pid" - ], - [ - "u32", - "address" - ], - { - "crc": "0x77e06379" - } - ], - [ - "want_ip4_arp_events_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip4_arp_event", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "address" - ], - [ - "u32", - "pid" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "new_mac", - 6 - ], - [ - "u8", - "mac_ip" - ], - { - "crc": "0xef7235f7" - } - ], - [ - "want_ip6_nd_events", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "enable_disable" - ], - [ - "u32", - "pid" - ], - [ - "u8", - "address", - 16 - ], - { - "crc": "0x1cf65fbb" - } - ], - [ - "want_ip6_nd_events_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip6_nd_event", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "pid" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "address", - 16 - ], - [ - "u8", - "new_mac", - 6 - ], - [ - "u8", - "mac_ip" - ], - { - "crc": "0x96ab2fdd" - } - ], - [ - "proxy_arp_add_del", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "vrf_id" - ], - [ - "u8", - "is_add" - ], - [ - "u8", - "low_address", - 4 - ], - [ - "u8", - "hi_address", - 4 - ], - { - "crc": "0xc2442918" - } - ], - [ - "proxy_arp_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "proxy_arp_intfc_enable_disable", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "sw_if_index" - ], - [ - "u8", - "enable_disable" - ], - { - "crc": "0x69d24598" - } - ], - [ - "proxy_arp_intfc_enable_disable_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "reset_fib", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u32", - "vrf_id" - ], - [ - "u8", - "is_ipv6" - ], - { - "crc": "0x8553ebd9" - } - ], - [ - "reset_fib_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "set_arp_neighbor_limit", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u32", - "arp_neighbor_limit" - ], - { - "crc": "0x97d01fd6" - } - ], - [ - "set_arp_neighbor_limit_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ioam_enable", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u16", - "id" - ], - [ - "u8", - "seqno" - ], - [ - "u8", - "analyse" - ], - [ - "u8", - "pot_enable" - ], - [ - "u8", - "trace_enable" - ], - [ - "u32", - "node_id" - ], - { - "crc": "0x9392e032" - } - ], - [ - "ioam_enable_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ioam_disable", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], - [ - "u32", - "context" - ], - [ - "u16", - "id" - ], - { - "crc": "0x6b16a45e" - } - ], - [ - "ioam_disable_reply", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" - ], - [ - "i32", - "retval" - ], - { - "crc": "0xe8d4e804" - } - ] - ], - "types": [ - [ - "fib_path", - [ - "u32", - "sw_if_index" - ], - [ - "u32", - "table_id" - ], - [ - "u8", - "weight" - ], - [ - "u8", - "preference" - ], - [ - "u8", - "is_local" - ], - [ - "u8", - "is_drop" - ], - [ - "u8", - "is_unreach" - ], - [ - "u8", - "is_prohibit" - ], - [ - "u8", - "afi" - ], - [ - "u8", - "next_hop", - 16 - ], - { - "crc": "0xcd899e0a" - } - ] - ] -} diff --git a/cmd/binapi-generator/types.go b/cmd/binapi-generator/types.go deleted file mode 100644 index 90c890f..0000000 --- a/cmd/binapi-generator/types.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2019 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 ( - "fmt" - "strconv" - "strings" - - "github.com/sirupsen/logrus" -) - -// toApiType returns name that is used as type reference in VPP binary API -func toApiType(name string) string { - return fmt.Sprintf("vl_api_%s_t", name) -} - -// binapiTypes is a set of types used VPP binary API for translation to Go types -var binapiTypes = map[string]string{ - "u8": "uint8", - "i8": "int8", - "u16": "uint16", - "i16": "int16", - "u32": "uint32", - "i32": "int32", - "u64": "uint64", - "i64": "int64", - "f64": "float64", -} - -func getBinapiTypeSize(binapiType string) int { - if _, ok := binapiTypes[binapiType]; ok { - b, err := strconv.Atoi(strings.TrimLeft(binapiType, "uif")) - if err == nil { - return b / 8 - } - } - return -1 -} - -// convertToGoType translates the VPP binary API type into Go type -func convertToGoType(ctx *context, binapiType string) (typ string) { - if t, ok := binapiTypes[binapiType]; ok { - // basic types - typ = t - } else if r, ok := ctx.packageData.RefMap[binapiType]; ok { - // specific types (enums/types/unions) - typ = camelCaseName(r) - } else { - switch binapiType { - case "bool", "string": - typ = binapiType - default: - // fallback type - logrus.Warnf("found unknown VPP binary API type %q, using byte", binapiType) - typ = "byte" - } - } - return typ -} - -func getSizeOfType(ctx *context, typ *Type) (size int) { - for _, field := range typ.Fields { - enum := getEnumByRef(ctx, field.Type) - if enum != nil { - size += getSizeOfBinapiTypeLength(enum.Type, field.Length) - continue - } - size += getSizeOfBinapiTypeLength(field.Type, field.Length) - } - return size -} - -func getSizeOfBinapiTypeLength(typ string, length int) (size int) { - if n := getBinapiTypeSize(typ); n > 0 { - if length > 0 { - return n * length - } else { - return n - } - } - - return -} - -func getEnumByRef(ctx *context, ref string) *Enum { - for _, typ := range ctx.packageData.Enums { - if ref == toApiType(typ.Name) { - return &typ - } - } - return nil -} - -func getTypeByRef(ctx *context, ref string) *Type { - for _, typ := range ctx.packageData.Types { - if ref == toApiType(typ.Name) { - return &typ - } - } - return nil -} - -func getAliasByRef(ctx *context, ref string) *Alias { - for _, alias := range ctx.packageData.Aliases { - if ref == toApiType(alias.Name) { - return &alias - } - } - return nil -} - -func getUnionSize(ctx *context, union *Union) (maxSize int) { - for _, field := range union.Fields { - typ := getTypeByRef(ctx, field.Type) - if typ != nil { - if size := getSizeOfType(ctx, typ); size > maxSize { - maxSize = size - } - continue - } - alias := getAliasByRef(ctx, field.Type) - if alias != nil { - if size := getSizeOfBinapiTypeLength(alias.Type, alias.Length); size > maxSize { - maxSize = size - } - continue - } else { - logf("no type or alias found for union %s field type %q", union.Name, field.Type) - continue - } - } - logf("getUnionSize: %s %+v max=%v", union.Name, union.Fields, maxSize) - return -} diff --git a/cmd/binapi-generator/util.go b/cmd/binapi-generator/util.go new file mode 100644 index 0000000..8738963 --- /dev/null +++ b/cmd/binapi-generator/util.go @@ -0,0 +1,81 @@ +// 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 main + +import ( + "io/ioutil" + "os/exec" + "path" + "strings" + + "github.com/sirupsen/logrus" +) + +const ( + versionScriptPath = "./src/scripts/version" + defaultVppApiDir = "/usr/share/vpp/api" +) + +func ResolveVppVersion(inputDir string) string { + // assuming VPP package is installed + if inputDir == defaultVppApiDir { + // resolve VPP version using dpkg + cmd := exec.Command("dpkg-query", "-f", "${Version}", "-W", "vpp") + out, err := cmd.CombinedOutput() + if err != nil { + logrus.Warnf("resolving VPP version from installed package failed: %v", err) + logrus.Warnf("command output: %s", out) + } else { + version := strings.TrimSpace(string(out)) + logrus.Debugf("resolved VPP version from installed package: %v", version) + return version + } + } + // check if inside VPP git repo + if inputDir != "" { + repo := findVppGitRepo(inputDir) + if repo != "" { + cmd := exec.Command(versionScriptPath) + cmd.Dir = repo + out, err := cmd.CombinedOutput() + if err != nil { + logrus.Warnf("resolving VPP version from version script failed: %v", err) + logrus.Warnf("command output: %s", out) + } else { + version := strings.TrimSpace(string(out)) + logrus.Debugf("resolved VPP version from version script: %v", version) + return version + } + } + file, err := ioutil.ReadFile(path.Join(inputDir, "VPP_VERSION")) + if err == nil { + return strings.TrimSpace(string(file)) + } + } + logrus.Warnf("VPP version could not be resolved, you can set it manually using VPP_API_VERSION env var") + return "unknown" +} + +func findVppGitRepo(dir string) string { + cmd := exec.Command("git", "rev-parse", "--show-toplevel") + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + logrus.Warnf("checking VPP git repo failed: %v", err) + logrus.Warnf("command output: %s", out) + return "" + } + return strings.TrimSpace(string(out)) +} |