From 94620e85f0bdbb054af07ce3670fadc1f76cfdf0 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Thu, 18 Jun 2020 08:22:13 +0200 Subject: Refactored binapi generator with message encoding Change-Id: I5a6abb68b9d058866f94818169300e5c2fc43895 Signed-off-by: Ondrej Fabry --- binapigen/binapigen.go | 369 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 binapigen/binapigen.go (limited to 'binapigen/binapigen.go') 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 +} -- cgit 1.2.3-korg