Refactor GoVPP
[govpp.git] / cmd / binapi-generator / main.go
1 // Copyright (c) 2018 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 main
16
17 import (
18         "bufio"
19         "encoding/json"
20         "flag"
21         "fmt"
22         "io/ioutil"
23         "log"
24         "os"
25         "os/exec"
26         "path/filepath"
27         "strings"
28
29         "github.com/bennyscetbun/jsongo"
30 )
31
32 var (
33         inputFile       = flag.String("input-file", "", "Input JSON file.")
34         inputDir        = flag.String("input-dir", ".", "Input directory with JSON files.")
35         outputDir       = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
36         includeAPIVer   = flag.Bool("include-apiver", false, "Whether to include VlAPIVersion in generated file.")
37         debug           = flag.Bool("debug", false, "Turn on debug mode.")
38         continueOnError = flag.Bool("continue-onerror", false, "Wheter to continue with next file on error.")
39 )
40
41 func logf(f string, v ...interface{}) {
42         if *debug {
43                 log.Printf(f, v...)
44         }
45 }
46
47 func main() {
48         flag.Parse()
49
50         if *inputFile == "" && *inputDir == "" {
51                 fmt.Fprintln(os.Stderr, "ERROR: input-file or input-dir must be specified")
52                 os.Exit(1)
53         }
54
55         if *inputFile != "" {
56                 // process one input file
57                 if err := generateFromFile(*inputFile, *outputDir); err != nil {
58                         fmt.Fprintf(os.Stderr, "ERROR: code generation from %s failed: %v\n", *inputFile, err)
59                         os.Exit(1)
60                 }
61         } else {
62                 // process all files in specified directory
63                 files, err := getInputFiles(*inputDir)
64                 if err != nil {
65                         fmt.Fprintf(os.Stderr, "ERROR: code generation failed: %v\n", err)
66                         os.Exit(1)
67                 }
68                 for _, file := range files {
69                         if err := generateFromFile(file, *outputDir); err != nil {
70                                 fmt.Fprintf(os.Stderr, "ERROR: code generation from %s failed: %v\n", file, err)
71                                 if *continueOnError {
72                                         continue
73                                 }
74                                 os.Exit(1)
75                         }
76                 }
77         }
78 }
79
80 // getInputFiles returns all input files located in specified directory
81 func getInputFiles(inputDir string) (res []string, err error) {
82         files, err := ioutil.ReadDir(inputDir)
83         if err != nil {
84                 return nil, fmt.Errorf("reading directory %s failed: %v", inputDir, err)
85         }
86         for _, f := range files {
87                 if strings.HasSuffix(f.Name(), inputFileExt) {
88                         res = append(res, filepath.Join(inputDir, f.Name()))
89                 }
90         }
91         return res, nil
92 }
93
94 // generateFromFile generates Go package from one input JSON file
95 func generateFromFile(inputFile, outputDir string) error {
96         logf("generating from file: %q", inputFile)
97         defer logf("--------------------------------------")
98
99         ctx, err := getContext(inputFile, outputDir)
100         if err != nil {
101                 return err
102         }
103
104         // read input file contents
105         ctx.inputData, err = readFile(inputFile)
106         if err != nil {
107                 return err
108         }
109         // parse JSON data into objects
110         jsonRoot, err := parseJSON(ctx.inputData)
111         if err != nil {
112                 return err
113         }
114         ctx.packageData, err = parsePackage(ctx, jsonRoot)
115         if err != nil {
116                 return err
117         }
118
119         // create output directory
120         packageDir := filepath.Dir(ctx.outputFile)
121         if err := os.MkdirAll(packageDir, 0777); err != nil {
122                 return fmt.Errorf("creating output directory %q failed: %v", packageDir, err)
123         }
124         // open output file
125         f, err := os.Create(ctx.outputFile)
126         if err != nil {
127                 return fmt.Errorf("creating output file %q failed: %v", ctx.outputFile, err)
128         }
129         defer f.Close()
130
131         // generate Go package code
132         w := bufio.NewWriter(f)
133         if err := generatePackage(ctx, w); err != nil {
134                 return err
135         }
136
137         // go format the output file (fail probably means the output is not compilable)
138         cmd := exec.Command("gofmt", "-w", ctx.outputFile)
139         if output, err := cmd.CombinedOutput(); err != nil {
140                 return fmt.Errorf("gofmt failed: %v\n%s", err, string(output))
141         }
142
143         // count number of lines in generated output file
144         cmd = exec.Command("wc", "-l", ctx.outputFile)
145         if output, err := cmd.CombinedOutput(); err != nil {
146                 log.Printf("wc command failed: %v\n%s", err, string(output))
147         } else {
148                 logf("generated lines: %s", output)
149         }
150
151         return nil
152 }
153
154 // readFile reads content of a file into memory
155 func readFile(inputFile string) ([]byte, error) {
156         inputData, err := ioutil.ReadFile(inputFile)
157         if err != nil {
158                 return nil, fmt.Errorf("reading data from file failed: %v", err)
159         }
160
161         return inputData, nil
162 }
163
164 // parseJSON parses a JSON data into an in-memory tree
165 func parseJSON(inputData []byte) (*jsongo.JSONNode, error) {
166         root := jsongo.JSONNode{}
167
168         if err := json.Unmarshal(inputData, &root); err != nil {
169                 return nil, fmt.Errorf("unmarshalling JSON failed: %v", err)
170         }
171
172         return &root, nil
173 }