017847626f105218e07498f115e98436e92a54e9
[govpp.git] / binapigen / binapigen.go
1 //  Copyright (c) 2020 Cisco and/or its affiliates.
2 //
3 //  Licensed under the Apache License, Version 2.0 (the "License");
4 //  you may not use this file except in compliance with the License.
5 //  You may obtain a copy of the License at:
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 //  Unless required by applicable law or agreed to in writing, software
10 //  distributed under the License is distributed on an "AS IS" BASIS,
11 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 //  See the License for the specific language governing permissions and
13 //  limitations under the License.
14
15 package binapigen
16
17 import (
18         "fmt"
19         "path"
20         "sort"
21         "strings"
22
23         "git.fd.io/govpp.git/binapigen/vppapi"
24 )
25
26 type File struct {
27         vppapi.File
28
29         Generate bool
30
31         PackageName string
32         Imports     []string
33
34         Enums    []*Enum
35         Unions   []*Union
36         Structs  []*Struct
37         Aliases  []*Alias
38         Messages []*Message
39
40         imports map[string]string
41         refmap  map[string]string
42 }
43
44 func newFile(gen *Generator, apifile *vppapi.File) (*File, error) {
45         file := &File{
46                 File:        *apifile,
47                 PackageName: sanitizedName(apifile.Name),
48                 imports:     make(map[string]string),
49                 refmap:      make(map[string]string),
50         }
51
52         sortFileObjects(&file.File)
53
54         for _, imp := range apifile.Imports {
55                 file.Imports = append(file.Imports, normalizeImport(imp))
56         }
57         for _, enum := range apifile.EnumTypes {
58                 file.Enums = append(file.Enums, newEnum(gen, file, enum))
59         }
60         for _, alias := range apifile.AliasTypes {
61                 file.Aliases = append(file.Aliases, newAlias(gen, file, alias))
62         }
63         for _, structType := range apifile.StructTypes {
64                 file.Structs = append(file.Structs, newStruct(gen, file, structType))
65         }
66         for _, union := range apifile.UnionTypes {
67                 file.Unions = append(file.Unions, newUnion(gen, file, union))
68         }
69         for _, msg := range apifile.Messages {
70                 file.Messages = append(file.Messages, newMessage(gen, file, msg))
71         }
72
73         return file, nil
74 }
75
76 func (file *File) isTypes() bool {
77         return strings.HasSuffix(file.File.Name, "_types")
78 }
79
80 func (file *File) hasService() bool {
81         return file.Service != nil && len(file.Service.RPCs) > 0
82 }
83
84 func (file *File) addRef(typ string, name string, ref interface{}) {
85         apiName := toApiType(name)
86         if _, ok := file.refmap[apiName]; ok {
87                 logf("%s type %v already in refmap", typ, apiName)
88                 return
89         }
90         file.refmap[apiName] = name
91 }
92
93 func (file *File) importedFiles(gen *Generator) []*File {
94         var files []*File
95         for _, imp := range file.Imports {
96                 impFile, ok := gen.FilesByName[imp]
97                 if !ok {
98                         logf("file %s import %s not found API files", file.Name, imp)
99                         continue
100                 }
101                 //if gen.ImportTypes || impFile.Generate {
102                 files = append(files, impFile)
103                 //}
104         }
105         return files
106 }
107
108 func (file *File) loadTypeImports(gen *Generator, typeFiles []*File) {
109         if len(typeFiles) == 0 {
110                 return
111         }
112         for _, t := range file.Structs {
113                 for _, imp := range typeFiles {
114                         if _, ok := file.imports[t.Name]; ok {
115                                 break
116                         }
117                         for _, at := range imp.File.StructTypes {
118                                 if at.Name != t.Name {
119                                         continue
120                                 }
121                                 if len(at.Fields) != len(t.Fields) {
122                                         continue
123                                 }
124                                 file.imports[t.Name] = imp.PackageName
125                         }
126                 }
127         }
128         for _, t := range file.AliasTypes {
129                 for _, imp := range typeFiles {
130                         if _, ok := file.imports[t.Name]; ok {
131                                 break
132                         }
133                         for _, at := range imp.File.AliasTypes {
134                                 if at.Name != t.Name {
135                                         continue
136                                 }
137                                 if at.Length != t.Length {
138                                         continue
139                                 }
140                                 if at.Type != t.Type {
141                                         continue
142                                 }
143                                 file.imports[t.Name] = imp.PackageName
144                         }
145                 }
146         }
147         for _, t := range file.EnumTypes {
148                 for _, imp := range typeFiles {
149                         if _, ok := file.imports[t.Name]; ok {
150                                 break
151                         }
152                         for _, at := range imp.File.EnumTypes {
153                                 if at.Name != t.Name {
154                                         continue
155                                 }
156                                 if at.Type != t.Type {
157                                         continue
158                                 }
159                                 file.imports[t.Name] = imp.PackageName
160                         }
161                 }
162         }
163         for _, t := range file.UnionTypes {
164                 for _, imp := range typeFiles {
165                         if _, ok := file.imports[t.Name]; ok {
166                                 break
167                         }
168                         for _, at := range imp.File.UnionTypes {
169                                 if at.Name != t.Name {
170                                         continue
171                                 }
172                                 file.imports[t.Name] = imp.PackageName
173                                 /*if gen.ImportTypes {
174                                         imp.Generate = true
175                                 }*/
176                         }
177                 }
178         }
179 }
180
181 type Enum struct {
182         vppapi.EnumType
183
184         GoName string
185 }
186
187 func newEnum(gen *Generator, file *File, apitype vppapi.EnumType) *Enum {
188         typ := &Enum{
189                 EnumType: apitype,
190                 GoName:   camelCaseName(apitype.Name),
191         }
192         gen.enumsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
193         file.addRef("enum", typ.Name, typ)
194         return typ
195 }
196
197 type Alias struct {
198         vppapi.AliasType
199
200         GoName string
201 }
202
203 func newAlias(gen *Generator, file *File, apitype vppapi.AliasType) *Alias {
204         typ := &Alias{
205                 AliasType: apitype,
206                 GoName:    camelCaseName(apitype.Name),
207         }
208         gen.aliasesByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
209         file.addRef("alias", typ.Name, typ)
210         return typ
211 }
212
213 type Struct struct {
214         vppapi.StructType
215
216         GoName string
217
218         Fields []*Field
219 }
220
221 func newStruct(gen *Generator, file *File, apitype vppapi.StructType) *Struct {
222         typ := &Struct{
223                 StructType: apitype,
224                 GoName:     camelCaseName(apitype.Name),
225         }
226         for _, fieldType := range apitype.Fields {
227                 field := newField(gen, file, fieldType)
228                 field.ParentStruct = typ
229                 typ.Fields = append(typ.Fields, field)
230         }
231         gen.structsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
232         file.addRef("struct", typ.Name, typ)
233         return typ
234 }
235
236 type Union struct {
237         vppapi.UnionType
238
239         GoName string
240
241         Fields []*Field
242 }
243
244 func newUnion(gen *Generator, file *File, apitype vppapi.UnionType) *Union {
245         typ := &Union{
246                 UnionType: apitype,
247                 GoName:    camelCaseName(apitype.Name),
248         }
249         gen.unionsByName[fmt.Sprintf("%s.%s", file.Name, typ.Name)] = typ
250         for _, fieldType := range apitype.Fields {
251                 field := newField(gen, file, fieldType)
252                 field.ParentUnion = typ
253                 typ.Fields = append(typ.Fields, field)
254         }
255         file.addRef("union", typ.Name, typ)
256         return typ
257 }
258
259 type Message struct {
260         vppapi.Message
261
262         GoName string
263
264         Fields []*Field
265 }
266
267 func newMessage(gen *Generator, file *File, apitype vppapi.Message) *Message {
268         msg := &Message{
269                 Message: apitype,
270                 GoName:  camelCaseName(apitype.Name),
271         }
272         for _, fieldType := range apitype.Fields {
273                 field := newField(gen, file, fieldType)
274                 field.ParentMessage = msg
275                 msg.Fields = append(msg.Fields, field)
276         }
277         return msg
278 }
279
280 type Field struct {
281         vppapi.Field
282
283         GoName string
284
285         // Field parent
286         ParentMessage *Message
287         ParentStruct  *Struct
288         ParentUnion   *Union
289
290         // Type reference
291         Enum   *Enum
292         Alias  *Alias
293         Struct *Struct
294         Union  *Union
295 }
296
297 func newField(gen *Generator, file *File, apitype vppapi.Field) *Field {
298         typ := &Field{
299                 Field:  apitype,
300                 GoName: camelCaseName(apitype.Name),
301         }
302         return typ
303 }
304
305 func (f *Field) resolveType(gen *Generator) error {
306         switch {
307
308         }
309         return nil
310 }
311
312 type Service = vppapi.Service
313 type RPC = vppapi.RPC
314
315 func sortFileObjects(file *vppapi.File) {
316         // sort imports
317         sort.SliceStable(file.Imports, func(i, j int) bool {
318                 return file.Imports[i] < file.Imports[j]
319         })
320         // sort enum types
321         sort.SliceStable(file.EnumTypes, func(i, j int) bool {
322                 return file.EnumTypes[i].Name < file.EnumTypes[j].Name
323         })
324         // sort alias types
325         sort.Slice(file.AliasTypes, func(i, j int) bool {
326                 return file.AliasTypes[i].Name < file.AliasTypes[j].Name
327         })
328         // sort struct types
329         sort.SliceStable(file.StructTypes, func(i, j int) bool {
330                 return file.StructTypes[i].Name < file.StructTypes[j].Name
331         })
332         // sort union types
333         sort.SliceStable(file.UnionTypes, func(i, j int) bool {
334                 return file.UnionTypes[i].Name < file.UnionTypes[j].Name
335         })
336         // sort messages
337         sort.SliceStable(file.Messages, func(i, j int) bool {
338                 return file.Messages[i].Name < file.Messages[j].Name
339         })
340         // sort services
341         if file.Service != nil {
342                 sort.Slice(file.Service.RPCs, func(i, j int) bool {
343                         // dumps first
344                         if file.Service.RPCs[i].Stream != file.Service.RPCs[j].Stream {
345                                 return file.Service.RPCs[i].Stream
346                         }
347                         return file.Service.RPCs[i].RequestMsg < file.Service.RPCs[j].RequestMsg
348                 })
349         }
350 }
351
352 func sanitizedName(name string) string {
353         switch name {
354         case "interface":
355                 return "interfaces"
356         case "map":
357                 return "maps"
358         default:
359                 return name
360         }
361 }
362
363 func normalizeImport(imp string) string {
364         imp = path.Base(imp)
365         if idx := strings.Index(imp, "."); idx >= 0 {
366                 imp = imp[:idx]
367         }
368         return imp
369 }