Binapi generator: improved file input
[govpp.git] / binapigen / vppapi / vppapi.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 vppapi
16
17 import (
18         "fmt"
19         "io/ioutil"
20         "path/filepath"
21         "strings"
22 )
23
24 const (
25         // DefaultDir is default location of API files.
26         DefaultDir = "/usr/share/vpp/api"
27
28         // APIFileExtension is a VPP API file extension suffix
29         APIFileExtension = ".api.json"
30 )
31
32 // FindFiles finds API files located in dir or in a nested directory that is not nested deeper than deep.
33 func FindFiles(dir string, deep int) (files []string, err error) {
34         entries, err := ioutil.ReadDir(dir)
35         if err != nil {
36                 return nil, fmt.Errorf("reading directory %s failed: %v", dir, err)
37         }
38         for _, e := range entries {
39                 if e.IsDir() && deep > 0 {
40                         nestedDir := filepath.Join(dir, e.Name())
41                         if nested, err := FindFiles(nestedDir, deep-1); err != nil {
42                                 return nil, err
43                         } else {
44                                 files = append(files, nested...)
45                         }
46                 } else if !e.IsDir() && strings.HasSuffix(e.Name(), APIFileExtension) {
47                         files = append(files, filepath.Join(dir, e.Name()))
48                 }
49         }
50         return files, nil
51 }
52
53 // Parse parses API files in directory DefaultDir.
54 func Parse() ([]*File, error) {
55         return ParseDir(DefaultDir)
56 }
57
58 // ParseDir finds and parses API files in given directory and returns parsed files.
59 // Supports API files in JSON format (.api.json) only.
60 func ParseDir(apiDir string) ([]*File, error) {
61         list, err := FindFiles(apiDir, 1)
62         if err != nil {
63                 return nil, err
64         }
65
66         logf("found %d files in API dir %q", len(list), apiDir)
67
68         var files []*File
69         for _, file := range list {
70                 module, err := ParseFile(file)
71                 if err != nil {
72                         return nil, err
73                 }
74                 files = append(files, module)
75         }
76         return files, nil
77 }
78
79 // ParseFile parses API file and returns File.
80 func ParseFile(apiFile string) (*File, error) {
81         if !strings.HasSuffix(apiFile, APIFileExtension) {
82                 return nil, fmt.Errorf("unsupported file format: %q", apiFile)
83         }
84
85         data, err := ioutil.ReadFile(apiFile)
86         if err != nil {
87                 return nil, fmt.Errorf("reading file %s failed: %v", apiFile, err)
88         }
89
90         base := filepath.Base(apiFile)
91         name := base[:strings.Index(base, ".")]
92
93         logf("parsing file %q", base)
94
95         module, err := ParseRaw(data)
96         if err != nil {
97                 return nil, fmt.Errorf("parsing file %s failed: %v", base, err)
98         }
99         module.Name = name
100         module.Path = apiFile
101
102         return module, nil
103 }
104
105 // ParseRaw parses raw API file data and returns File.
106 func ParseRaw(data []byte) (file *File, err error) {
107         defer func() {
108                 if e := recover(); e != nil {
109                         err = fmt.Errorf("panic occurred: %v", e)
110                 }
111         }()
112
113         file, err = parseJSON(data)
114         if err != nil {
115                 return nil, err
116         }
117
118         return file, nil
119 }