diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/binapi-generator/doc.go | 3 | ||||
-rw-r--r-- | cmd/binapi-generator/generate.go | 197 | ||||
-rw-r--r-- | cmd/binapi-generator/main.go | 84 | ||||
-rw-r--r-- | cmd/binapi-generator/objects.go | 59 | ||||
-rw-r--r-- | cmd/binapi-generator/parse.go | 82 | ||||
-rw-r--r-- | cmd/binapi-generator/types.go | 4 |
6 files changed, 260 insertions, 169 deletions
diff --git a/cmd/binapi-generator/doc.go b/cmd/binapi-generator/doc.go index e8556ec..d74d47b 100644 --- a/cmd/binapi-generator/doc.go +++ b/cmd/binapi-generator/doc.go @@ -7,7 +7,6 @@ // where each Go package will be placed into its own separate directory, // for example: // -// binapi-generator --input-file=examples/bin_api/acl.api.json --output-dir=examples/bin_api -// binapi-generator --input-dir=examples/bin_api --output-dir=examples/bin_api +// binapi-generator --input-file=/usr/share/vpp/api/core/interface.api.json --output-dir=. // package main diff --git a/cmd/binapi-generator/generate.go b/cmd/binapi-generator/generate.go index 4ffe88e..d9555e7 100644 --- a/cmd/binapi-generator/generate.go +++ b/cmd/binapi-generator/generate.go @@ -15,7 +15,6 @@ package main import ( - "bufio" "bytes" "fmt" "io" @@ -25,12 +24,23 @@ import ( "unicode" ) +// generatedCodeVersion indicates a version of the generated code. +// It is incremented whenever an incompatibility between the generated code and +// GoVPP api package is introduced; the generated code references +// a constant, api.GoVppAPIPackageIsVersionN (where N is generatedCodeVersion). +const generatedCodeVersion = 1 + const ( inputFileExt = ".api.json" // file extension of the VPP API files outputFileExt = ".ba.go" // file extension of the Go generated files govppApiImportPath = "git.fd.io/govpp.git/api" // import path of the govpp API package - constAPIVersionCrc = "APIVersionCrc" // name for the API version CRC constant + + constModuleName = "ModuleName" // module name constant + constAPIVersion = "APIVersion" // API version constant + constVersionCrc = "VersionCrc" // version CRC constant + + unionDataField = "XXX_UnionData" // name for the union data field ) // context is a structure storing data for code generation @@ -40,9 +50,10 @@ type context struct { inputData []byte // contents of the input file - includeAPIVersionCrc bool // include constant with API version CRC string - includeComments bool // include parts of original source in comments - includeBinapiNames bool // include binary API names as struct tag + includeAPIVersion bool // include constant with API version string + includeComments bool // include parts of original source in comments + includeBinapiNames bool // include binary API names as struct tag + includeServices bool // include service interface with client implementation moduleName string // name of the source VPP module packageName string // name of the Go package being generated @@ -83,25 +94,28 @@ func getContext(inputFile, outputDir string) (*context, error) { } // generatePackage generates code for the parsed package data and writes it into w -func generatePackage(ctx *context, w *bufio.Writer) error { +func generatePackage(ctx *context, w io.Writer) error { logf("generating package %q", ctx.packageName) // generate file header generateHeader(ctx, w) generateImports(ctx, w) - if ctx.includeAPIVersionCrc { - fmt.Fprintf(w, "// %s defines API version CRC of the VPP binary API module.\n", constAPIVersionCrc) - fmt.Fprintf(w, "const %s = %v\n", constAPIVersionCrc, ctx.packageData.APIVersion) - fmt.Fprintln(w) - } - - // generate services - if len(ctx.packageData.Services) > 0 { - generateServices(ctx, w, ctx.packageData.Services) + // generate module desc + fmt.Fprintln(w, "const (") + fmt.Fprintf(w, "\t// %s is the name of this module.\n", constModuleName) + fmt.Fprintf(w, "\t%s = \"%s\"\n", constModuleName, ctx.moduleName) - // TODO: generate implementation for Services interface + if ctx.includeAPIVersion { + if ctx.packageData.Version != "" { + fmt.Fprintf(w, "\t// %s is the API version of this module.\n", constAPIVersion) + fmt.Fprintf(w, "\t%s = \"%s\"\n", constAPIVersion, ctx.packageData.Version) + } + fmt.Fprintf(w, "\t// %s is the CRC of this module.\n", constVersionCrc) + fmt.Fprintf(w, "\t%s = %v\n", constVersionCrc, ctx.packageData.CRC) } + fmt.Fprintln(w, ")") + fmt.Fprintln(w) // generate enums if len(ctx.packageData.Enums) > 0 { @@ -156,17 +170,23 @@ func generatePackage(ctx *context, w *bufio.Writer) error { fmt.Fprintln(w, "}") fmt.Fprintln(w) - fmt.Fprintln(w, "var Messages = []api.Message{") + // generate list of messages + fmt.Fprintf(w, "// Messages returns list of all messages in this module.\n") + fmt.Fprintln(w, "func AllMessages() []api.Message {") + fmt.Fprintln(w, "\treturn []api.Message{") for _, msg := range ctx.packageData.Messages { name := camelCaseName(msg.Name) fmt.Fprintf(w, "\t(*%s)(nil),\n", name) } fmt.Fprintln(w, "}") + fmt.Fprintln(w, "}") } - // flush the data: - if err := w.Flush(); err != nil { - return fmt.Errorf("flushing data to %s failed: %v", ctx.outputFile, err) + if ctx.includeServices { + // generate services + if len(ctx.packageData.Services) > 0 { + generateServices(ctx, w, ctx.packageData.Services) + } } return nil @@ -175,17 +195,18 @@ func generatePackage(ctx *context, w *bufio.Writer) error { // generateHeader writes generated package header into w func generateHeader(ctx *context, w io.Writer) { fmt.Fprintln(w, "// Code generated by GoVPP binapi-generator. DO NOT EDIT.") - fmt.Fprintf(w, "// source: %s\n", ctx.inputFile) + fmt.Fprintf(w, "// source: %s\n", ctx.inputFile) fmt.Fprintln(w) fmt.Fprintln(w, "/*") - fmt.Fprintf(w, " Package %s is a generated from VPP binary API module '%s'.\n", ctx.packageName, ctx.moduleName) + fmt.Fprintf(w, "Package %s is a generated from VPP binary API module '%s'.\n", ctx.packageName, ctx.moduleName) fmt.Fprintln(w) - fmt.Fprintln(w, " It contains following objects:") + fmt.Fprintf(w, " The %s module consists of:\n", ctx.moduleName) var printObjNum = func(obj string, num int) { if num > 0 { if num > 1 { if strings.HasSuffix(obj, "s") { + obj += "es" } else { obj += "s" @@ -195,13 +216,14 @@ func generateHeader(ctx *context, w io.Writer) { } } - printObjNum("service", len(ctx.packageData.Services)) printObjNum("enum", len(ctx.packageData.Enums)) printObjNum("alias", len(ctx.packageData.Aliases)) printObjNum("type", len(ctx.packageData.Types)) printObjNum("union", len(ctx.packageData.Unions)) printObjNum("message", len(ctx.packageData.Messages)) + printObjNum("service", len(ctx.packageData.Services)) fmt.Fprintln(w, "*/") + fmt.Fprintf(w, "package %s\n", ctx.packageName) fmt.Fprintln(w) } @@ -209,28 +231,32 @@ func generateHeader(ctx *context, w io.Writer) { // generateImports writes generated package imports into w func generateImports(ctx *context, w io.Writer) { fmt.Fprintf(w, "import api \"%s\"\n", govppApiImportPath) - fmt.Fprintf(w, "import struc \"%s\"\n", "github.com/lunixbochs/struc") fmt.Fprintf(w, "import bytes \"%s\"\n", "bytes") + fmt.Fprintf(w, "import context \"%s\"\n", "context") + fmt.Fprintf(w, "import strconv \"%s\"\n", "strconv") + fmt.Fprintf(w, "import struc \"%s\"\n", "github.com/lunixbochs/struc") fmt.Fprintln(w) fmt.Fprintf(w, "// Reference imports to suppress errors if they are not otherwise used.\n") fmt.Fprintf(w, "var _ = api.RegisterMessage\n") - fmt.Fprintf(w, "var _ = struc.Pack\n") fmt.Fprintf(w, "var _ = bytes.NewBuffer\n") + fmt.Fprintf(w, "var _ = context.Background\n") + fmt.Fprintf(w, "var _ = strconv.Itoa\n") + fmt.Fprintf(w, "var _ = struc.Pack\n") fmt.Fprintln(w) - /*fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file") + fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file") fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.") fmt.Fprintln(w, "// A compilation error at this line likely means your copy of the") fmt.Fprintln(w, "// GoVPP api package needs to be updated.") - fmt.Fprintln(w, "const _ = api.GoVppAPIPackageIsVersion1 // please upgrade the GoVPP api package") - fmt.Fprintln(w)*/ + fmt.Fprintf(w, "const _ = api.GoVppAPIPackageIsVersion%d // please upgrade the GoVPP api package\n", generatedCodeVersion) + fmt.Fprintln(w) } // generateComment writes generated comment for the object into w func generateComment(ctx *context, w io.Writer, goName string, vppName string, objKind string) { if objKind == "service" { - fmt.Fprintf(w, "// %s represents VPP binary API services:\n", goName) + fmt.Fprintf(w, "// %s represents VPP binary API services in %s module.\n", ctx.moduleName, goName) } else { fmt.Fprintf(w, "// %s represents VPP binary API %s '%s':\n", goName, objKind, vppName) } @@ -290,22 +316,69 @@ func generateComment(ctx *context, w io.Writer, goName string, vppName string, o } // generateServices writes generated code for the Services interface into w -func generateServices(ctx *context, w *bufio.Writer, services []Service) { +func generateServices(ctx *context, w io.Writer, services []Service) { + const apiName = "Service" + const implName = "service" + // generate services comment - generateComment(ctx, w, "Services", "services", "service") + generateComment(ctx, w, apiName, "services", "service") // generate interface - fmt.Fprintf(w, "type %s interface {\n", "Services") + fmt.Fprintf(w, "type %s interface {\n", apiName) for _, svc := range services { - generateService(ctx, w, &svc) + generateServiceMethod(ctx, w, &svc) + fmt.Fprintln(w) } fmt.Fprintln(w, "}") + fmt.Fprintln(w) + + // generate client implementation + fmt.Fprintf(w, "type %s struct {\n", implName) + fmt.Fprintf(w, "\tch api.Channel\n") + fmt.Fprintln(w, "}") + fmt.Fprintln(w) + + fmt.Fprintf(w, "func New%[1]s(ch api.Channel) %[1]s {\n", apiName) + fmt.Fprintf(w, "\treturn &%s{ch}\n", implName) + fmt.Fprintln(w, "}") + fmt.Fprintln(w) + + for _, svc := range services { + fmt.Fprintf(w, "func (c *%s) ", implName) + generateServiceMethod(ctx, w, &svc) + fmt.Fprintln(w, " {") + if svc.Stream { + // TODO: stream responses + //fmt.Fprintf(w, "\tstream := make(chan *%s)\n", camelCaseName(svc.ReplyType)) + replyTyp := camelCaseName(svc.ReplyType) + fmt.Fprintf(w, "\tvar dump []*%s\n", replyTyp) + fmt.Fprintf(w, "\treq := c.ch.SendMultiRequest(in)\n") + fmt.Fprintf(w, "\tfor {\n") + fmt.Fprintf(w, "\tm := new(%s)\n", replyTyp) + fmt.Fprintf(w, "\tstop, err := req.ReceiveReply(m)\n") + fmt.Fprintf(w, "\tif stop { break }\n") + fmt.Fprintf(w, "\tif err != nil { return nil, err }\n") + fmt.Fprintf(w, "\tdump = append(dump, m)\n") + fmt.Fprintln(w, "}") + fmt.Fprintf(w, "\treturn dump, nil\n") + } else if replyTyp := camelCaseName(svc.ReplyType); replyTyp != "" { + fmt.Fprintf(w, "\tout := new(%s)\n", replyTyp) + fmt.Fprintf(w, "\terr:= c.ch.SendRequest(in).ReceiveReply(out)\n") + fmt.Fprintf(w, "\tif err != nil { return nil, err }\n") + fmt.Fprintf(w, "\treturn out, nil\n") + } else { + fmt.Fprintf(w, "\tc.ch.SendRequest(in)\n") + fmt.Fprintf(w, "\treturn nil\n") + } + fmt.Fprintln(w, "}") + fmt.Fprintln(w) + } fmt.Fprintln(w) } -// generateService writes generated code for the service into w -func generateService(ctx *context, w io.Writer, svc *Service) { +// generateServiceMethod writes generated code for the service into w +func generateServiceMethod(ctx *context, w io.Writer, svc *Service) { reqTyp := camelCaseName(svc.RequestType) // method name is same as parameter type name by default @@ -317,17 +390,19 @@ func generateService(ctx *context, w io.Writer, svc *Service) { } } - params := fmt.Sprintf("*%s", reqTyp) + params := fmt.Sprintf("in *%s", reqTyp) returns := "error" if replyType := camelCaseName(svc.ReplyType); replyType != "" { - repTyp := fmt.Sprintf("*%s", replyType) + replyTyp := fmt.Sprintf("*%s", replyType) if svc.Stream { - repTyp = fmt.Sprintf("[]%s", repTyp) + // TODO: stream responses + //replyTyp = fmt.Sprintf("<-chan %s", replyTyp) + replyTyp = fmt.Sprintf("[]%s", replyTyp) } - returns = fmt.Sprintf("(%s, error)", repTyp) + returns = fmt.Sprintf("(%s, error)", replyTyp) } - fmt.Fprintf(w, "\t%s(%s) %s\n", method, params, returns) + fmt.Fprintf(w, "\t%s(ctx context.Context, %s) %s", method, params, returns) } // generateEnum writes generated code for the enum into w @@ -344,15 +419,34 @@ func generateEnum(ctx *context, w io.Writer, enum *Enum) { fmt.Fprintf(w, "type %s %s\n", name, typ) fmt.Fprintln(w) - fmt.Fprintln(w, "const (") - // generate enum entries + fmt.Fprintln(w, "const (") for _, entry := range enum.Entries { fmt.Fprintf(w, "\t%s %s = %v\n", entry.Name, name, entry.Value) } - fmt.Fprintln(w, ")") + fmt.Fprintln(w) + // generate enum conversion maps + fmt.Fprintf(w, "var %s_name = map[%s]string{\n", name, typ) + for _, entry := range enum.Entries { + fmt.Fprintf(w, "\t%v: \"%s\",\n", entry.Value, entry.Name) + } + fmt.Fprintln(w, "}") + fmt.Fprintln(w) + + fmt.Fprintf(w, "var %s_value = map[string]%s{\n", name, typ) + for _, entry := range enum.Entries { + fmt.Fprintf(w, "\t\"%s\": %v,\n", entry.Name, entry.Value) + } + fmt.Fprintln(w, "}") + fmt.Fprintln(w) + + fmt.Fprintf(w, "func (x %s) String() string {\n", name) + fmt.Fprintf(w, "\ts, ok := %s_name[%s(x)]\n", name, typ) + fmt.Fprintf(w, "\tif ok { return s }\n") + fmt.Fprintf(w, "\treturn strconv.Itoa(int(x))\n") + fmt.Fprintln(w, "}") fmt.Fprintln(w) } @@ -394,8 +488,7 @@ func generateUnion(ctx *context, w io.Writer, union *Union) { maxSize := getUnionSize(ctx, union) // generate data field - fieldName := "Union_data" - fmt.Fprintf(w, "\t%s [%d]byte\n", fieldName, maxSize) + fmt.Fprintf(w, "\t%s [%d]byte\n", unionDataField, maxSize) // generate end of the struct fmt.Fprintln(w, "}") @@ -420,9 +513,9 @@ func generateUnion(ctx *context, w io.Writer, union *Union) { } // generateUnionMethods generates methods that implement struc.Custom -// interface to allow having Union_data field unexported +// interface to allow having XXX_uniondata field unexported // TODO: do more testing when unions are actually used in some messages -func generateUnionMethods(w io.Writer, structName string) { +/*func generateUnionMethods(w io.Writer, structName string) { // generate struc.Custom implementation for union fmt.Fprintf(w, ` func (u *%[1]s) Pack(p []byte, opt *struc.Options) (int, error) { @@ -443,7 +536,7 @@ func (u *%[1]s) String() string { return string(u.union_data[:]) } `, structName) -} +}*/ func generateUnionGetterSetter(w io.Writer, structName string, getterField, getterStruct string) { fmt.Fprintf(w, ` @@ -456,14 +549,14 @@ func (u *%[1]s) Set%[2]s(a %[3]s) { if err := struc.Pack(b, &a); err != nil { return } - copy(u.Union_data[:], b.Bytes()) + copy(u.%[4]s[:], b.Bytes()) } func (u *%[1]s) Get%[2]s() (a %[3]s) { - var b = bytes.NewReader(u.Union_data[:]) + var b = bytes.NewReader(u.%[4]s[:]) struc.Unpack(b, &a) return } -`, structName, getterField, getterStruct) +`, structName, getterField, getterStruct, unionDataField) } // generateType writes generated code for the type into w diff --git a/cmd/binapi-generator/main.go b/cmd/binapi-generator/main.go index faed54f..75926e1 100644 --- a/cmd/binapi-generator/main.go +++ b/cmd/binapi-generator/main.go @@ -15,7 +15,7 @@ package main import ( - "bufio" + "bytes" "encoding/json" "flag" "fmt" @@ -33,21 +33,16 @@ var ( inputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.") inputDir = flag.String("input-dir", ".", "Input directory with VPP API files in JSON format.") outputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.") - includeAPIVer = flag.Bool("include-apiver", false, "Include APIVersion constant for each module.") + includeAPIVer = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.") includeComments = flag.Bool("include-comments", false, "Include JSON API source in comments for each object.") includeBinapiNames = flag.Bool("include-binapi-names", false, "Include binary API names in struct tag.") + includeServices = flag.Bool("include-services", false, "Include service interface with client implementation.") continueOnError = flag.Bool("continue-onerror", false, "Continue with next file on error.") debug = flag.Bool("debug", debugMode, "Enable debug mode.") ) var debugMode = os.Getenv("DEBUG_BINAPI_GENERATOR") != "" -var log = logrus.Logger{ - Level: logrus.InfoLevel, - Formatter: &logrus.TextFormatter{}, - Out: os.Stdout, -} - func main() { flag.Parse() if *debug { @@ -100,49 +95,51 @@ func getInputFiles(inputDir string) (res []string, err error) { // generateFromFile generates Go package from one input JSON file func generateFromFile(inputFile, outputDir string) error { - logf("generating from file: %q", inputFile) - defer logf("--------------------------------------") + logf("generating from file: %s", inputFile) + logf("------------------------------------------------------------") + defer logf("------------------------------------------------------------") ctx, err := getContext(inputFile, outputDir) if err != nil { return err } - ctx.includeAPIVersionCrc = *includeAPIVer + // prepare options + ctx.includeAPIVersion = *includeAPIVer ctx.includeComments = *includeComments ctx.includeBinapiNames = *includeBinapiNames + ctx.includeServices = *includeServices - // read input file contents - ctx.inputData, err = readFile(inputFile) + // read API definition from input file + ctx.inputData, err = ioutil.ReadFile(ctx.inputFile) if err != nil { - return err + return fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err) } + // parse JSON data into objects - jsonRoot, err := parseJSON(ctx.inputData) - if err != nil { - return err + jsonRoot := new(jsongo.JSONNode) + if err := json.Unmarshal(ctx.inputData, jsonRoot); err != nil { + return fmt.Errorf("unmarshalling JSON failed: %v", err) } ctx.packageData, err = parsePackage(ctx, jsonRoot) if err != nil { - return err + return fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err) + } + + // generate Go package code + var buf bytes.Buffer + if err := generatePackage(ctx, &buf); err != nil { + return fmt.Errorf("generating code for package %s failed: %v", ctx.packageName, err) } // create output directory packageDir := filepath.Dir(ctx.outputFile) - if err := os.MkdirAll(packageDir, 0777); err != nil { - return fmt.Errorf("creating output directory %q failed: %v", packageDir, err) + if err := os.MkdirAll(packageDir, 06); err != nil { + return fmt.Errorf("creating output dir %s failed: %v", packageDir, err) } - // open output file - f, err := os.Create(ctx.outputFile) - if err != nil { - return fmt.Errorf("creating output file %q failed: %v", ctx.outputFile, err) - } - defer f.Close() - - // generate Go package code - w := bufio.NewWriter(f) - if err := generatePackage(ctx, w); err != nil { - return err + // write generated code to output file + if err := ioutil.WriteFile(ctx.outputFile, buf.Bytes(), 0666); err != nil { + return fmt.Errorf("writing to output file %s failed: %v", ctx.outputFile, err) } // go format the output file (fail probably means the output is not compilable) @@ -154,35 +151,14 @@ func generateFromFile(inputFile, outputDir string) error { // count number of lines in generated output file cmd = exec.Command("wc", "-l", ctx.outputFile) if output, err := cmd.CombinedOutput(); err != nil { - log.Warnf("wc command failed: %v\n%s", err, string(output)) + logf("wc command failed: %v\n%s", err, string(output)) } else { - logf("generated lines: %s", output) + logf("number of generated lines: %s", output) } return nil } -// readFile reads content of a file into memory -func readFile(inputFile string) ([]byte, error) { - inputData, err := ioutil.ReadFile(inputFile) - if err != nil { - return nil, fmt.Errorf("reading data from file failed: %v", err) - } - - return inputData, nil -} - -// parseJSON parses a JSON data into an in-memory tree -func parseJSON(inputData []byte) (*jsongo.JSONNode, error) { - root := jsongo.JSONNode{} - - if err := json.Unmarshal(inputData, &root); err != nil { - return nil, fmt.Errorf("unmarshalling JSON failed: %v", err) - } - - return &root, nil -} - func logf(f string, v ...interface{}) { if *debug { logrus.Debugf(f, v...) diff --git a/cmd/binapi-generator/objects.go b/cmd/binapi-generator/objects.go index 8f5e8ef..e3270de 100644 --- a/cmd/binapi-generator/objects.go +++ b/cmd/binapi-generator/objects.go @@ -1,15 +1,18 @@ package main +import "fmt" + // Package represents collection of objects parsed from VPP binary API JSON data type Package struct { - APIVersion string - Services []Service - Enums []Enum - Aliases []Alias - Types []Type - Unions []Union - Messages []Message - RefMap map[string]string + Version string + CRC string + Services []Service + Enums []Enum + Aliases []Alias + Types []Type + Unions []Union + Messages []Message + RefMap map[string]string } // Service represents VPP binary API service @@ -85,3 +88,43 @@ const ( eventMessage // VPP event message otherMessage // other VPP message ) + +// printPackage prints all loaded objects for package +func printPackage(pkg *Package) { + if len(pkg.Enums) > 0 { + logf("loaded %d enums:", len(pkg.Enums)) + for k, enum := range pkg.Enums { + logf(" - enum #%d\t%+v", k, enum) + } + } + if len(pkg.Unions) > 0 { + logf("loaded %d unions:", len(pkg.Unions)) + for k, union := range pkg.Unions { + logf(" - union #%d\t%+v", k, union) + } + } + if len(pkg.Types) > 0 { + logf("loaded %d types:", len(pkg.Types)) + for _, typ := range pkg.Types { + logf(" - type: %q (%d fields)", typ.Name, len(typ.Fields)) + } + } + if len(pkg.Messages) > 0 { + logf("loaded %d messages:", len(pkg.Messages)) + for _, msg := range pkg.Messages { + logf(" - message: %q (%d fields)", msg.Name, len(msg.Fields)) + } + } + if len(pkg.Services) > 0 { + logf("loaded %d services:", len(pkg.Services)) + for _, svc := range pkg.Services { + var info string + if svc.Stream { + info = "(STREAM)" + } else if len(svc.Events) > 0 { + info = fmt.Sprintf("(EVENTS: %v)", svc.Events) + } + logf(" - service: %s - %q -> %q %s", svc.Name, svc.RequestType, svc.ReplyType, info) + } + } +} diff --git a/cmd/binapi-generator/parse.go b/cmd/binapi-generator/parse.go index 662ed34..562abab 100644 --- a/cmd/binapi-generator/parse.go +++ b/cmd/binapi-generator/parse.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/bennyscetbun/jsongo" + "github.com/sirupsen/logrus" ) // top level objects @@ -32,6 +33,7 @@ const ( objServices = "services" objAliases = "aliases" vlAPIVersion = "vl_api_version" + objOptions = "options" ) // various object fields @@ -64,11 +66,32 @@ const ( fieldMetaLimit = "limit" ) +// module options +const ( + versionOption = "version" +) + // parsePackage parses provided JSON data into objects prepared for code generation func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { - logf(" %s (version: %s) contains: %d services, %d messages, %d types, %d enums, %d unions, %d aliases", + pkg := Package{ + RefMap: make(map[string]string), + } + + // parse CRC for API version + if crc := jsonRoot.At(vlAPIVersion); crc.GetType() == jsongo.TypeValue { + pkg.CRC = crc.Get().(string) + } + + // parse version string + if opt := jsonRoot.Map(objOptions); opt.GetType() == jsongo.TypeMap { + if ver := opt.Map(versionOption); ver.GetType() == jsongo.TypeValue { + pkg.Version = ver.Get().(string) + } + } + + logf("parsing package %s (version: %s, CRC: %s) contains: %d services, %d messages, %d types, %d enums, %d unions, %d aliases", ctx.packageName, - jsonRoot.Map(vlAPIVersion).Get(), + pkg.Version, pkg.CRC, jsonRoot.Map(objServices).Len(), jsonRoot.Map(objMessages).Len(), jsonRoot.Map(objTypes).Len(), @@ -77,11 +100,6 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { jsonRoot.Map(objAliases).Len(), ) - pkg := Package{ - APIVersion: jsonRoot.Map(vlAPIVersion).Get().(string), - RefMap: make(map[string]string), - } - // parse enums enums := jsonRoot.Map(objEnums) pkg.Enums = make([]Enum, enums.Len()) @@ -201,46 +219,6 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) { return &pkg, nil } -// printPackage prints all loaded objects for package -func printPackage(pkg *Package) { - if len(pkg.Enums) > 0 { - logf("loaded %d enums:", len(pkg.Enums)) - for k, enum := range pkg.Enums { - logf(" - enum #%d\t%+v", k, enum) - } - } - if len(pkg.Unions) > 0 { - logf("loaded %d unions:", len(pkg.Unions)) - for k, union := range pkg.Unions { - logf(" - union #%d\t%+v", k, union) - } - } - if len(pkg.Types) > 0 { - logf("loaded %d types:", len(pkg.Types)) - for _, typ := range pkg.Types { - logf(" - type: %q (%d fields)", typ.Name, len(typ.Fields)) - } - } - if len(pkg.Messages) > 0 { - logf("loaded %d messages:", len(pkg.Messages)) - for _, msg := range pkg.Messages { - logf(" - message: %q (%d fields)", msg.Name, len(msg.Fields)) - } - } - if len(pkg.Services) > 0 { - logf("loaded %d services:", len(pkg.Services)) - for _, svc := range pkg.Services { - var info string - if svc.Stream { - info = "(STREAM)" - } else if len(svc.Events) > 0 { - info = fmt.Sprintf("(EVENTS: %v)", svc.Events) - } - logf(" - service: %q -> %q %s", svc.RequestType, svc.ReplyType, info) - } - } -} - // parseEnum parses VPP binary API enum object from JSON node func parseEnum(ctx *context, enumNode *jsongo.JSONNode) (*Enum, error) { if enumNode.Len() == 0 || enumNode.At(0).GetType() != jsongo.TypeValue { @@ -466,7 +444,7 @@ func parseField(ctx *context, field *jsongo.JSONNode) (*Field, error) { case fieldMetaLimit: f.Meta.Limit = int(metaNode.Get().(float64)) default: - log.Warnf("unknown meta info (%s) for field (%s)", metaName, fieldName) + logrus.Warnf("unknown meta info (%s) for field (%s)", metaName, fieldName) } } } else { @@ -491,7 +469,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv } svc := Service{ - Name: ctx.moduleName + "." + svcName, + Name: svcName, RequestType: svcName, } @@ -526,7 +504,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv if len(svc.Events) > 0 { // EVENT service if !strings.HasPrefix(svc.RequestType, serviceEventPrefix) { - log.Debugf("unusual EVENTS service: %+v\n"+ + logrus.Debugf("unusual EVENTS service: %+v\n"+ "- events service %q does not have %q prefix in request.", svc, svc.Name, serviceEventPrefix) } @@ -534,7 +512,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv // STREAM service if !strings.HasSuffix(svc.RequestType, serviceDumpSuffix) || !strings.HasSuffix(svc.ReplyType, serviceDetailsSuffix) { - log.Debugf("unusual STREAM service: %+v\n"+ + logrus.Debugf("unusual STREAM service: %+v\n"+ "- stream service %q does not have %q suffix in request or reply does not have %q suffix.", svc, svc.Name, serviceDumpSuffix, serviceDetailsSuffix) } @@ -542,7 +520,7 @@ func parseService(ctx *context, svcName string, svcNode *jsongo.JSONNode) (*Serv // REQUEST service // some messages might have `null` reply (for example: memclnt) if !strings.HasSuffix(svc.ReplyType, serviceReplySuffix) { - log.Debugf("unusual REQUEST service: %+v\n"+ + logrus.Debugf("unusual REQUEST service: %+v\n"+ "- service %q does not have %q suffix in reply.", svc, svc.Name, serviceReplySuffix) } diff --git a/cmd/binapi-generator/types.go b/cmd/binapi-generator/types.go index d056251..90c890f 100644 --- a/cmd/binapi-generator/types.go +++ b/cmd/binapi-generator/types.go @@ -18,6 +18,8 @@ import ( "fmt" "strconv" "strings" + + "github.com/sirupsen/logrus" ) // toApiType returns name that is used as type reference in VPP binary API @@ -62,7 +64,7 @@ func convertToGoType(ctx *context, binapiType string) (typ string) { typ = binapiType default: // fallback type - log.Warnf("found unknown VPP binary API type %q, using byte", binapiType) + logrus.Warnf("found unknown VPP binary API type %q, using byte", binapiType) typ = "byte" } } |