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