Support imported type aliases
[govpp.git] / cmd / binapi-generator / main.go
index c66fc4f..e0e2f08 100644 (file)
@@ -32,9 +32,10 @@ import (
 )
 
 var (
-       theInputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.")
-       theInputDir  = flag.String("input-dir", "/usr/share/vpp/api", "Input directory with VPP API files in JSON format.")
-       theOutputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
+       theInputFile  = flag.String("input-file", "", "Input file with VPP API in JSON format.")
+       theInputTypes = flag.String("input-types", "", "Types input file with VPP API in JSON format. (split by comma)")
+       theInputDir   = flag.String("input-dir", "/usr/share/vpp/api", "Input directory with VPP API files in JSON format.")
+       theOutputDir  = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
 
        includeAPIVer      = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.")
        includeServices    = flag.Bool("include-services", true, "Include RPC service api and client implementation.")
@@ -84,14 +85,23 @@ func main() {
        }
 }
 
-func run(inputFile, inputDir string, outputDir string, continueErr bool) error {
+func run(inputFile, inputDir string, outputDir string, continueErr bool) (err error) {
        if inputFile == "" && inputDir == "" {
                return fmt.Errorf("input-file or input-dir must be specified")
        }
 
+       var typesPkgs []*context
+       if *theInputTypes != "" {
+               types := strings.Split(*theInputTypes, ",")
+               typesPkgs, err = loadTypesPackages(types...)
+               if err != nil {
+                       return fmt.Errorf("loading types input failed: %v", err)
+               }
+       }
+
        if inputFile != "" {
                // process one input file
-               if err := generateFromFile(inputFile, outputDir); err != nil {
+               if err := generateFromFile(inputFile, outputDir, typesPkgs); err != nil {
                        return fmt.Errorf("code generation from %s failed: %v\n", inputFile, err)
                }
        } else {
@@ -107,7 +117,7 @@ func run(inputFile, inputDir string, outputDir string, continueErr bool) error {
                        return fmt.Errorf("no input files found in input directory: %v\n", dir)
                }
                for _, file := range files {
-                       if err := generateFromFile(file, outputDir); err != nil {
+                       if err := generateFromFile(file, outputDir, typesPkgs); err != nil {
                                if continueErr {
                                        logrus.Warnf("code generation from %s failed: %v (error ignored)\n", file, err)
                                        continue
@@ -151,7 +161,7 @@ func parseInputJSON(inputData []byte) (*jsongo.Node, error) {
 }
 
 // generateFromFile generates Go package from one input JSON file
-func generateFromFile(inputFile, outputDir string) error {
+func generateFromFile(inputFile, outputDir string, typesPkgs []*context) error {
        // create generator context
        ctx, err := newContext(inputFile, outputDir)
        if err != nil {
@@ -185,6 +195,13 @@ func generateFromFile(inputFile, outputDir string) error {
                return fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err)
        }
 
+       if len(typesPkgs) > 0 {
+               err = loadTypeAliases(ctx, typesPkgs)
+               if err != nil {
+                       return fmt.Errorf("loading type aliases failed: %v", err)
+               }
+       }
+
        // generate Go package code
        var buf bytes.Buffer
        if err := generatePackage(ctx, &buf); err != nil {
@@ -210,6 +227,109 @@ func generateFromFile(inputFile, outputDir string) error {
        return nil
 }
 
+func loadTypesPackages(types ...string) ([]*context, error) {
+       var ctxs []*context
+       for _, inputFile := range types {
+               // create generator context
+               ctx, err := newContext(inputFile, "")
+               if err != nil {
+                       return nil, err
+               }
+               // read API definition from input file
+               ctx.inputData, err = ioutil.ReadFile(ctx.inputFile)
+               if err != nil {
+                       return nil, fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err)
+               }
+               // parse JSON data into objects
+               jsonRoot, err := parseInputJSON(ctx.inputData)
+               if err != nil {
+                       return nil, fmt.Errorf("parsing JSON input failed: %v", err)
+               }
+               ctx.packageData, err = parsePackage(ctx, jsonRoot)
+               if err != nil {
+                       return nil, fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err)
+               }
+               ctxs = append(ctxs, ctx)
+       }
+       return ctxs, nil
+}
+
+func loadTypeAliases(ctx *context, typesCtxs []*context) error {
+       for _, t := range ctx.packageData.Types {
+               for _, c := range typesCtxs {
+                       if _, ok := ctx.packageData.Imports[t.Name]; ok {
+                               break
+                       }
+                       for _, at := range c.packageData.Types {
+                               if at.Name != t.Name {
+                                       continue
+                               }
+                               if len(at.Fields) != len(t.Fields) {
+                                       continue
+                               }
+                               ctx.packageData.Imports[t.Name] = Import{
+                                       Package: c.packageName,
+                               }
+                       }
+               }
+       }
+       for _, t := range ctx.packageData.Aliases {
+               for _, c := range typesCtxs {
+                       if _, ok := ctx.packageData.Imports[t.Name]; ok {
+                               break
+                       }
+                       for _, at := range c.packageData.Aliases {
+                               if at.Name != t.Name {
+                                       continue
+                               }
+                               if at.Length != t.Length {
+                                       continue
+                               }
+                               if at.Type != t.Type {
+                                       continue
+                               }
+                               ctx.packageData.Imports[t.Name] = Import{
+                                       Package: c.packageName,
+                               }
+                       }
+               }
+       }
+       for _, t := range ctx.packageData.Enums {
+               for _, c := range typesCtxs {
+                       if _, ok := ctx.packageData.Imports[t.Name]; ok {
+                               break
+                       }
+                       for _, at := range c.packageData.Enums {
+                               if at.Name != t.Name {
+                                       continue
+                               }
+                               if at.Type != t.Type {
+                                       continue
+                               }
+                               ctx.packageData.Imports[t.Name] = Import{
+                                       Package: c.packageName,
+                               }
+                       }
+               }
+       }
+       for _, t := range ctx.packageData.Unions {
+               for _, c := range typesCtxs {
+                       if _, ok := ctx.packageData.Imports[t.Name]; ok {
+                               break
+                       }
+                       for _, at := range c.packageData.Unions {
+                               if at.Name != t.Name {
+                                       continue
+                               }
+                               ctx.packageData.Imports[t.Name] = Import{
+                                       Package: c.packageName,
+                               }
+                       }
+               }
+       }
+       return nil
+}
+
 func logf(f string, v ...interface{}) {
        if *debugMode {
                logrus.Debugf(f, v...)