aboutsummaryrefslogtreecommitdiffstats
path: root/binapigen/binapigen.go
diff options
context:
space:
mode:
Diffstat (limited to 'binapigen/binapigen.go')
-rw-r--r--binapigen/binapigen.go369
1 files changed, 369 insertions, 0 deletions
diff --git a/binapigen/binapigen.go b/binapigen/binapigen.go
new file mode 100644
index 0000000..0178476
--- /dev/null
+++ b/binapigen/binapigen.go
@@ -0,0 +1,369 @@
+// Copyright (c) 2020 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package binapigen
+
+import (
+ "fmt"
+ "path"
+ "sort"
+ "strings"
+
+ "git.fd.io/govpp.git/binapigen/vppapi"
+)
+
+type File struct {
+ vppapi.File
+
+ Generate bool
+
+ PackageName string
+ Imports []string
+
+ Enums []*Enum
+ Unions []*Union
+ Structs []*Struct
+ Aliases []*Alias
+ Messages []*Message
+
+ imports map[string]string
+ refmap map[string]string
+}
+
+func newFile(gen *Generator, apifile *vppapi.File) (*File, error) {
+ file := &File{
+ File: *apifile,
+ PackageName: sanitizedName(apifile.Name),
+ imports: make(map[string]string),
+ refmap: make(map[string]string),
+ }
+
+ sortFileObjects(&file.File)
+
+ for _, imp := range apifile.Imports {
+ file.Imports = append(file.Imports, normalizeImport(imp))
+ }
+ for _, enum := range apifile.EnumTypes {
+ file.Enums = append(file.Enums, newEnum(gen, file, enum))
+ }
+ for _, alias := range apifile.AliasTypes {
+ file.Aliases = append(file.Aliases, newAlias(gen, file, alias))
+ }
+ for _, structType := range apifile.StructTypes {
+ file.Structs = append(file.Structs, newStruct(gen, file, structType))
+ }
+ for _, union := range apifile.UnionTypes {
+ file.Unions = append(file.Unions, newUnion(gen, file, union))
+ }
+ for _, msg := range apifile.Messages {
+ file.Messages = append(file.Messages, newMessage(gen, file, msg))
+ }
+
+ return file, nil
+}
+
+func (file *File) isTypes() bool {
+ return strings.HasSuffix(file.File.Name, "_types")
+}
+
+func (file *File) hasService() bool {
+ return file.Service != nil && len(file.Service.RPCs) > 0
+}
+
+func (file *File) addRef(typ string, name string, ref interface{}) {
+ apiName := toApiType(name)
+ if _, ok := file.refmap[apiName]; ok {
+ logf("%s type %v already in refmap", typ, apiName)
+ return
+ }
+ file.refmap[apiName] = name
+}
+
+func (file *File) importedFiles(gen *Generator) []*File {
+ var files []*File
+ for _, imp := range file.Imports {
+ impFile, ok := gen.FilesByName[imp]
+ if !ok {
+ logf("file %s import %s not found API files", file.Name, imp)
+ continue
+ }
+ //if gen.ImportTypes || impFile.Generate {
+ files = append(files, impFile)
+ //}
+ }
+ return files
+}
+
+func (file *File) loadTypeImports(gen *Generator, typeFiles []*File) {
+ if len(typeFiles) == 0 {
+ return
+ }
+ for _, t := range file.Structs {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.StructTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ if len(at.Fields) != len(t.Fields) {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ }
+ }
+ }
+ for _, t := range file.AliasTypes {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.AliasTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ if at.Length != t.Length {
+ continue
+ }
+ if at.Type != t.Type {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ }
+ }
+ }
+ for _, t := range file.EnumTypes {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.EnumTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ if at.Type != t.Type {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ }
+ }
+ }
+ for _, t := range file.UnionTypes {
+ for _, imp := range typeFiles {
+ if _, ok := file.imports[t.Name]; ok {
+ break
+ }
+ for _, at := range imp.File.UnionTypes {
+ if at.Name != t.Name {
+ continue
+ }
+ file.imports[t.Name] = imp.PackageName
+ /*if gen.ImportTypes {
+ imp.Generate = true
+ }*/
+ }
+ }
+ }
+}
+
+type Enum struct {
+ vppapi.EnumType
+
+ GoName string
+}
+
+func newEnum(gen *Generator, file *File, apitype vppapi.EnumType) *Enum {
+ typ := &Enum{
+ EnumType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ gen.enumsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ file.addRef("enum", typ.Name, typ)
+ return typ
+}
+
+type Alias struct {
+ vppapi.AliasType
+
+ GoName string
+}
+
+func newAlias(gen *Generator, file *File, apitype vppapi.AliasType) *Alias {
+ typ := &Alias{
+ AliasType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ gen.aliasesByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ file.addRef("alias", typ.Name, typ)
+ return typ
+}
+
+type Struct struct {
+ vppapi.StructType
+
+ GoName string
+
+ Fields []*Field
+}
+
+func newStruct(gen *Generator, file *File, apitype vppapi.StructType) *Struct {
+ typ := &Struct{
+ StructType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ for _, fieldType := range apitype.Fields {
+ field := newField(gen, file, fieldType)
+ field.ParentStruct = typ
+ typ.Fields = append(typ.Fields, field)
+ }
+ gen.structsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ file.addRef("struct", typ.Name, typ)
+ return typ
+}
+
+type Union struct {
+ vppapi.UnionType
+
+ GoName string
+
+ Fields []*Field
+}
+
+func newUnion(gen *Generator, file *File, apitype vppapi.UnionType) *Union {
+ typ := &Union{
+ UnionType: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ gen.unionsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
+ for _, fieldType := range apitype.Fields {
+ field := newField(gen, file, fieldType)
+ field.ParentUnion = typ
+ typ.Fields = append(typ.Fields, field)
+ }
+ file.addRef("union", typ.Name, typ)
+ return typ
+}
+
+type Message struct {
+ vppapi.Message
+
+ GoName string
+
+ Fields []*Field
+}
+
+func newMessage(gen *Generator, file *File, apitype vppapi.Message) *Message {
+ msg := &Message{
+ Message: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ for _, fieldType := range apitype.Fields {
+ field := newField(gen, file, fieldType)
+ field.ParentMessage = msg
+ msg.Fields = append(msg.Fields, field)
+ }
+ return msg
+}
+
+type Field struct {
+ vppapi.Field
+
+ GoName string
+
+ // Field parent
+ ParentMessage *Message
+ ParentStruct *Struct
+ ParentUnion *Union
+
+ // Type reference
+ Enum *Enum
+ Alias *Alias
+ Struct *Struct
+ Union *Union
+}
+
+func newField(gen *Generator, file *File, apitype vppapi.Field) *Field {
+ typ := &Field{
+ Field: apitype,
+ GoName: camelCaseName(apitype.Name),
+ }
+ return typ
+}
+
+func (f *Field) resolveType(gen *Generator) error {
+ switch {
+
+ }
+ return nil
+}
+
+type Service = vppapi.Service
+type RPC = vppapi.RPC
+
+func sortFileObjects(file *vppapi.File) {
+ // sort imports
+ sort.SliceStable(file.Imports, func(i, j int) bool {
+ return file.Imports[i] < file.Imports[j]
+ })
+ // sort enum types
+ sort.SliceStable(file.EnumTypes, func(i, j int) bool {
+ return file.EnumTypes[i].Name < file.EnumTypes[j].Name
+ })
+ // sort alias types
+ sort.Slice(file.AliasTypes, func(i, j int) bool {
+ return file.AliasTypes[i].Name < file.AliasTypes[j].Name
+ })
+ // sort struct types
+ sort.SliceStable(file.StructTypes, func(i, j int) bool {
+ return file.StructTypes[i].Name < file.StructTypes[j].Name
+ })
+ // sort union types
+ sort.SliceStable(file.UnionTypes, func(i, j int) bool {
+ return file.UnionTypes[i].Name < file.UnionTypes[j].Name
+ })
+ // sort messages
+ sort.SliceStable(file.Messages, func(i, j int) bool {
+ return file.Messages[i].Name < file.Messages[j].Name
+ })
+ // sort services
+ if file.Service != nil {
+ sort.Slice(file.Service.RPCs, func(i, j int) bool {
+ // dumps first
+ if file.Service.RPCs[i].Stream != file.Service.RPCs[j].Stream {
+ return file.Service.RPCs[i].Stream
+ }
+ return file.Service.RPCs[i].RequestMsg < file.Service.RPCs[j].RequestMsg
+ })
+ }
+}
+
+func sanitizedName(name string) string {
+ switch name {
+ case "interface":
+ return "interfaces"
+ case "map":
+ return "maps"
+ default:
+ return name
+ }
+}
+
+func normalizeImport(imp string) string {
+ imp = path.Base(imp)
+ if idx := strings.Index(imp, "."); idx >= 0 {
+ imp = imp[:idx]
+ }
+ return imp
+}