1b4c7e58a30417191c9499b8a96823e00867effe
[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         "strings"
21
22         "git.fd.io/govpp.git/binapigen/vppapi"
23 )
24
25 // generatedCodeVersion indicates a version of the generated code.
26 // It is incremented whenever an incompatibility between the generated code and
27 // GoVPP api package is introduced; the generated code references
28 // a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion).
29 const generatedCodeVersion = 2
30
31 // file options
32 const (
33         optFileVersion = "version"
34 )
35
36 type File struct {
37         Desc vppapi.File
38
39         Generate       bool
40         FilenamePrefix string
41         PackageName    GoPackageName
42         GoImportPath   GoImportPath
43
44         Version string
45         Imports []string
46
47         Enums   []*Enum
48         Unions  []*Union
49         Structs []*Struct
50         Aliases []*Alias
51
52         Messages []*Message
53         Service  *Service
54 }
55
56 func newFile(gen *Generator, apifile *vppapi.File, packageName GoPackageName, importPath GoImportPath) (*File, error) {
57         file := &File{
58                 Desc:         *apifile,
59                 PackageName:  packageName,
60                 GoImportPath: importPath,
61         }
62         if apifile.Options != nil {
63                 file.Version = apifile.Options[optFileVersion]
64         }
65
66         file.FilenamePrefix = path.Join(gen.opts.OutputDir, file.Desc.Name)
67
68         for _, imp := range apifile.Imports {
69                 file.Imports = append(file.Imports, normalizeImport(imp))
70         }
71
72         for _, enumType := range apifile.EnumTypes {
73                 file.Enums = append(file.Enums, newEnum(gen, file, enumType))
74         }
75         for _, aliasType := range apifile.AliasTypes {
76                 file.Aliases = append(file.Aliases, newAlias(gen, file, aliasType))
77         }
78         for _, structType := range apifile.StructTypes {
79                 file.Structs = append(file.Structs, newStruct(gen, file, structType))
80         }
81         for _, unionType := range apifile.UnionTypes {
82                 file.Unions = append(file.Unions, newUnion(gen, file, unionType))
83         }
84
85         for _, msg := range apifile.Messages {
86                 file.Messages = append(file.Messages, newMessage(gen, file, msg))
87         }
88         if apifile.Service != nil {
89                 file.Service = newService(gen, file, *apifile.Service)
90         }
91
92         for _, t := range file.Aliases {
93                 if err := t.resolveDependencies(gen); err != nil {
94                         return nil, err
95                 }
96         }
97         for _, t := range file.Structs {
98                 if err := t.resolveDependencies(gen); err != nil {
99                         return nil, err
100                 }
101         }
102         for _, t := range file.Unions {
103                 if err := t.resolveDependencies(gen); err != nil {
104                         return nil, err
105                 }
106         }
107         for _, m := range file.Messages {
108                 if err := m.resolveDependencies(gen); err != nil {
109                         return nil, err
110                 }
111         }
112         if file.Service != nil {
113                 for _, rpc := range file.Service.RPCs {
114                         if err := rpc.resolveMessages(gen); err != nil {
115                                 return nil, err
116                         }
117                 }
118         }
119
120         return file, nil
121 }
122
123 func (file *File) isTypesFile() bool {
124         return strings.HasSuffix(file.Desc.Name, "_types")
125 }
126
127 func (file *File) hasService() bool {
128         return file.Service != nil && len(file.Service.RPCs) > 0
129 }
130
131 func (file *File) importedFiles(gen *Generator) []*File {
132         var files []*File
133         for _, imp := range file.Imports {
134                 impFile, ok := gen.FilesByName[imp]
135                 if !ok {
136                         logf("file %s import %s not found API files", file.Desc.Name, imp)
137                         continue
138                 }
139                 files = append(files, impFile)
140         }
141         return files
142 }
143
144 func (file *File) dependsOnFile(gen *Generator, dep string) bool {
145         for _, imp := range file.Imports {
146                 if imp == dep {
147                         return true
148                 }
149                 impFile, ok := gen.FilesByName[imp]
150                 if ok && impFile.dependsOnFile(gen, dep) {
151                         return true
152                 }
153         }
154         return false
155 }
156
157 func normalizeImport(imp string) string {
158         imp = path.Base(imp)
159         if idx := strings.Index(imp, "."); idx >= 0 {
160                 imp = imp[:idx]
161         }
162         return imp
163 }
164
165 const (
166         enumFlagSuffix = "_flags"
167 )
168
169 func isEnumFlag(enum *Enum) bool {
170         return strings.HasSuffix(enum.Name, enumFlagSuffix)
171 }
172
173 type Enum struct {
174         vppapi.EnumType
175
176         GoIdent
177 }
178
179 func newEnum(gen *Generator, file *File, apitype vppapi.EnumType) *Enum {
180         typ := &Enum{
181                 EnumType: apitype,
182                 GoIdent: GoIdent{
183                         GoName:       camelCaseName(apitype.Name),
184                         GoImportPath: file.GoImportPath,
185                 },
186         }
187         gen.enumsByName[typ.Name] = typ
188         return typ
189 }
190
191 type Alias struct {
192         vppapi.AliasType
193
194         GoIdent
195
196         TypeBasic  *string
197         TypeStruct *Struct
198         TypeUnion  *Union
199 }
200
201 func newAlias(gen *Generator, file *File, apitype vppapi.AliasType) *Alias {
202         typ := &Alias{
203                 AliasType: apitype,
204                 GoIdent: GoIdent{
205                         GoName:       camelCaseName(apitype.Name),
206                         GoImportPath: file.GoImportPath,
207                 },
208         }
209         gen.aliasesByName[typ.Name] = typ
210         return typ
211 }
212
213 func (a *Alias) resolveDependencies(gen *Generator) error {
214         if err := a.resolveType(gen); err != nil {
215                 return fmt.Errorf("unable to resolve field: %w", err)
216         }
217         return nil
218 }
219
220 func (a *Alias) resolveType(gen *Generator) error {
221         if _, ok := BaseTypesGo[a.Type]; ok {
222                 return nil
223         }
224         typ := fromApiType(a.Type)
225         if t, ok := gen.structsByName[typ]; ok {
226                 a.TypeStruct = t
227                 return nil
228         }
229         if t, ok := gen.unionsByName[typ]; ok {
230                 a.TypeUnion = t
231                 return nil
232         }
233         return fmt.Errorf("unknown type: %q", a.Type)
234 }
235
236 type Struct struct {
237         vppapi.StructType
238
239         GoIdent
240
241         Fields []*Field
242 }
243
244 func newStruct(gen *Generator, file *File, apitype vppapi.StructType) *Struct {
245         typ := &Struct{
246                 StructType: apitype,
247                 GoIdent: GoIdent{
248                         GoName:       camelCaseName(apitype.Name),
249                         GoImportPath: file.GoImportPath,
250                 },
251         }
252         gen.structsByName[typ.Name] = typ
253         for _, fieldType := range apitype.Fields {
254                 field := newField(gen, file, fieldType)
255                 field.ParentStruct = typ
256                 typ.Fields = append(typ.Fields, field)
257         }
258         return typ
259 }
260
261 func (m *Struct) resolveDependencies(gen *Generator) (err error) {
262         for _, field := range m.Fields {
263                 if err := field.resolveDependencies(gen); err != nil {
264                         return fmt.Errorf("unable to resolve for struct %s: %w", m.Name, err)
265                 }
266         }
267         return nil
268 }
269
270 type Union struct {
271         vppapi.UnionType
272
273         GoIdent
274
275         Fields []*Field
276 }
277
278 func newUnion(gen *Generator, file *File, apitype vppapi.UnionType) *Union {
279         typ := &Union{
280                 UnionType: apitype,
281                 GoIdent: GoIdent{
282                         GoName:       camelCaseName(apitype.Name),
283                         GoImportPath: file.GoImportPath,
284                 },
285         }
286         gen.unionsByName[typ.Name] = typ
287         for _, fieldType := range apitype.Fields {
288                 field := newField(gen, file, fieldType)
289                 field.ParentUnion = typ
290                 typ.Fields = append(typ.Fields, field)
291         }
292         return typ
293 }
294
295 func (m *Union) resolveDependencies(gen *Generator) (err error) {
296         for _, field := range m.Fields {
297                 if err := field.resolveDependencies(gen); err != nil {
298                         return err
299                 }
300         }
301         return nil
302 }
303
304 // msgType determines message header fields
305 type msgType int
306
307 const (
308         msgTypeBase    msgType = iota // msg_id
309         msgTypeRequest                // msg_id, client_index, context
310         msgTypeReply                  // msg_id, context
311         msgTypeEvent                  // msg_id, client_index
312 )
313
314 func apiMsgType(t msgType) GoIdent {
315         switch t {
316         case msgTypeRequest:
317                 return govppApiPkg.Ident("RequestMessage")
318         case msgTypeReply:
319                 return govppApiPkg.Ident("ReplyMessage")
320         case msgTypeEvent:
321                 return govppApiPkg.Ident("EventMessage")
322         default:
323                 return govppApiPkg.Ident("OtherMessage")
324         }
325 }
326
327 // message fields
328 const (
329         fieldMsgID       = "_vl_msg_id"
330         fieldClientIndex = "client_index"
331         fieldContext     = "context"
332         fieldRetval      = "retval"
333 )
334
335 // field options
336 const (
337         optFieldDefault = "default"
338 )
339
340 type Message struct {
341         vppapi.Message
342
343         CRC string
344
345         GoIdent
346
347         Fields []*Field
348
349         msgType msgType
350 }
351
352 func newMessage(gen *Generator, file *File, apitype vppapi.Message) *Message {
353         msg := &Message{
354                 Message: apitype,
355                 CRC:     strings.TrimPrefix(apitype.CRC, "0x"),
356                 GoIdent: newGoIdent(file, apitype.Name),
357         }
358         gen.messagesByName[apitype.Name] = msg
359         n := 0
360         for _, fieldType := range apitype.Fields {
361                 // skip internal fields
362                 switch strings.ToLower(fieldType.Name) {
363                 case fieldMsgID:
364                         continue
365                 case fieldClientIndex, fieldContext:
366                         if n == 0 {
367                                 continue
368                         }
369                 }
370                 n++
371                 field := newField(gen, file, fieldType)
372                 field.ParentMessage = msg
373                 msg.Fields = append(msg.Fields, field)
374         }
375         return msg
376 }
377
378 func (m *Message) resolveDependencies(gen *Generator) (err error) {
379         if m.msgType, err = getMsgType(m.Message); err != nil {
380                 return err
381         }
382         for _, field := range m.Fields {
383                 if err := field.resolveDependencies(gen); err != nil {
384                         return err
385                 }
386         }
387         return nil
388 }
389
390 func getMsgType(m vppapi.Message) (msgType, error) {
391         if len(m.Fields) == 0 {
392                 return msgType(0), fmt.Errorf("message %s has no fields", m.Name)
393         }
394         typ := msgTypeBase
395         wasClientIndex := false
396         for i, field := range m.Fields {
397                 if i == 0 {
398                         if field.Name != fieldMsgID {
399                                 return msgType(0), fmt.Errorf("message %s is missing ID field", m.Name)
400                         }
401                 } else if i == 1 {
402                         if field.Name == fieldClientIndex {
403                                 // "client_index" as the second member,
404                                 // this might be an event message or a request
405                                 typ = msgTypeEvent
406                                 wasClientIndex = true
407                         } else if field.Name == fieldContext {
408                                 // reply needs "context" as the second member
409                                 typ = msgTypeReply
410                         }
411                 } else if i == 2 {
412                         if wasClientIndex && field.Name == fieldContext {
413                                 // request needs "client_index" as the second member
414                                 // and "context" as the third member
415                                 typ = msgTypeRequest
416                         }
417                 }
418         }
419         return typ, nil
420 }
421
422 type Field struct {
423         vppapi.Field
424
425         GoName string
426
427         DefaultValue interface{}
428
429         // Reference to actual type of this field
430         TypeEnum   *Enum
431         TypeAlias  *Alias
432         TypeStruct *Struct
433         TypeUnion  *Union
434
435         // Parent in which this field is declared
436         ParentMessage *Message
437         ParentStruct  *Struct
438         ParentUnion   *Union
439
440         // Field reference for fields determining size
441         FieldSizeOf   *Field
442         FieldSizeFrom *Field
443 }
444
445 func newField(gen *Generator, file *File, apitype vppapi.Field) *Field {
446         typ := &Field{
447                 Field:  apitype,
448                 GoName: camelCaseName(apitype.Name),
449         }
450         if apitype.Meta != nil {
451                 if val, ok := apitype.Meta[optFieldDefault]; ok {
452                         typ.DefaultValue = val
453                 }
454         }
455         return typ
456 }
457
458 func (f *Field) resolveDependencies(gen *Generator) error {
459         if err := f.resolveType(gen); err != nil {
460                 return fmt.Errorf("unable to resolve field type: %w", err)
461         }
462         if err := f.resolveFields(gen); err != nil {
463                 return fmt.Errorf("unable to resolve fields: %w", err)
464         }
465         return nil
466 }
467
468 func (f *Field) resolveType(gen *Generator) error {
469         if _, ok := BaseTypesGo[f.Type]; ok {
470                 return nil
471         }
472         typ := fromApiType(f.Type)
473         if t, ok := gen.structsByName[typ]; ok {
474                 f.TypeStruct = t
475                 return nil
476         }
477         if t, ok := gen.enumsByName[typ]; ok {
478                 f.TypeEnum = t
479                 return nil
480         }
481         if t, ok := gen.aliasesByName[typ]; ok {
482                 f.TypeAlias = t
483                 return nil
484         }
485         if t, ok := gen.unionsByName[typ]; ok {
486                 f.TypeUnion = t
487                 return nil
488         }
489         return fmt.Errorf("unknown type: %q", f.Type)
490 }
491
492 func (f *Field) resolveFields(gen *Generator) error {
493         var fields []*Field
494         if f.ParentMessage != nil {
495                 fields = f.ParentMessage.Fields
496         } else if f.ParentStruct != nil {
497                 fields = f.ParentStruct.Fields
498         }
499         if f.SizeFrom != "" {
500                 for _, field := range fields {
501                         if field.Name == f.SizeFrom {
502                                 f.FieldSizeFrom = field
503                                 break
504                         }
505                 }
506         } else {
507                 for _, field := range fields {
508                         if field.SizeFrom == f.Name {
509                                 f.FieldSizeOf = field
510                                 break
511                         }
512                 }
513         }
514         return nil
515 }
516
517 type Service struct {
518         vppapi.Service
519
520         RPCs []*RPC
521 }
522
523 func newService(gen *Generator, file *File, apitype vppapi.Service) *Service {
524         svc := &Service{
525                 Service: apitype,
526         }
527         for _, rpc := range apitype.RPCs {
528                 svc.RPCs = append(svc.RPCs, newRpc(file, svc, rpc))
529         }
530         return svc
531 }
532
533 const (
534         serviceNoReply = "null"
535 )
536
537 type RPC struct {
538         VPP vppapi.RPC
539
540         GoName string
541
542         Service *Service
543
544         MsgRequest *Message
545         MsgReply   *Message
546         MsgStream  *Message
547 }
548
549 func newRpc(file *File, service *Service, apitype vppapi.RPC) *RPC {
550         rpc := &RPC{
551                 VPP:     apitype,
552                 GoName:  camelCaseName(apitype.Request),
553                 Service: service,
554         }
555         return rpc
556 }
557
558 func (rpc *RPC) resolveMessages(gen *Generator) error {
559         msg, ok := gen.messagesByName[rpc.VPP.Request]
560         if !ok {
561                 return fmt.Errorf("rpc %v: no message for request type %v", rpc.GoName, rpc.VPP.Request)
562         }
563         rpc.MsgRequest = msg
564
565         if rpc.VPP.Reply != "" && rpc.VPP.Reply != serviceNoReply {
566                 msg, ok := gen.messagesByName[rpc.VPP.Reply]
567                 if !ok {
568                         return fmt.Errorf("rpc %v: no message for reply type %v", rpc.GoName, rpc.VPP.Reply)
569                 }
570                 rpc.MsgReply = msg
571         }
572         if rpc.VPP.StreamMsg != "" {
573                 msg, ok := gen.messagesByName[rpc.VPP.StreamMsg]
574                 if !ok {
575                         return fmt.Errorf("rpc %v: no message for stream type %v", rpc.GoName, rpc.VPP.StreamMsg)
576                 }
577                 rpc.MsgStream = msg
578         }
579         return nil
580 }