diff options
author | Ondrej Fabry <ofabry@cisco.com> | 2018-12-13 10:21:49 +0100 |
---|---|---|
committer | Ondrej Fabry <ofabry@cisco.com> | 2018-12-13 10:21:49 +0100 |
commit | 868b541e296dc47748ad03b8f0174c828d996529 (patch) | |
tree | a43650ea7d9c96434d536e557177b38c5f84bd34 /cmd/binapi-generator/generate.go | |
parent | 1768c495416997c7a6769c6a92b33b37c89eed25 (diff) |
Add support for aliases and boolean type
- aliases are now generated as new types or arrays (if length > 0)
- bool is recognized as a boolean type and generated as Go bool
- comment with original JSON is now prepended for each object type
- interface Services is now generated at the top of the file to provide
overview of what RPC services does the current module consists of
- dump services now correctly return slice of the particular details type
Change-Id: I788babc1c0f2de33e0febd87e5b200d54065b244
Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
Diffstat (limited to 'cmd/binapi-generator/generate.go')
-rw-r--r-- | cmd/binapi-generator/generate.go | 157 |
1 files changed, 106 insertions, 51 deletions
diff --git a/cmd/binapi-generator/generate.go b/cmd/binapi-generator/generate.go index 7cfe338..22b4af6 100644 --- a/cmd/binapi-generator/generate.go +++ b/cmd/binapi-generator/generate.go @@ -35,9 +35,7 @@ type context struct { inputFile string // input file with VPP API in JSON outputFile string // output file with generated Go package - inputData []byte // contents of the input file - inputBuff *bytes.Buffer // contents of the input file currently being read - inputLine int // currently processed line in the input file + inputData []byte // contents of the input file moduleName string // name of the source VPP module packageName string // name of the Go package being generated @@ -87,28 +85,40 @@ func generatePackage(ctx *context, w *bufio.Writer) error { if *includeAPIVer { const APIVerConstName = "VlAPIVersion" - fmt.Fprintf(w, "// %s represents version of the API.\n", APIVerConstName) + fmt.Fprintf(w, "// %s represents version of the binary API module.\n", APIVerConstName) fmt.Fprintf(w, "const %s = %v\n", APIVerConstName, ctx.packageData.APIVersion) fmt.Fprintln(w) } + // generate services + if len(ctx.packageData.Services) > 0 { + generateServices(ctx, w, ctx.packageData.Services) + } + + // TODO: generate implementation for Services interface + // generate enums if len(ctx.packageData.Enums) > 0 { fmt.Fprintf(w, "/* Enums */\n\n") - ctx.inputBuff = bytes.NewBuffer(ctx.inputData) - ctx.inputLine = 0 for _, enum := range ctx.packageData.Enums { generateEnum(ctx, w, &enum) } } + // generate aliases + if len(ctx.packageData.Aliases) > 0 { + fmt.Fprintf(w, "/* Aliases */\n\n") + + for _, alias := range ctx.packageData.Aliases { + generateAlias(ctx, w, &alias) + } + } + // generate types if len(ctx.packageData.Types) > 0 { fmt.Fprintf(w, "/* Types */\n\n") - ctx.inputBuff = bytes.NewBuffer(ctx.inputData) - ctx.inputLine = 0 for _, typ := range ctx.packageData.Types { generateType(ctx, w, &typ) } @@ -118,8 +128,6 @@ func generatePackage(ctx *context, w *bufio.Writer) error { if len(ctx.packageData.Unions) > 0 { fmt.Fprintf(w, "/* Unions */\n\n") - ctx.inputBuff = bytes.NewBuffer(ctx.inputData) - ctx.inputLine = 0 for _, union := range ctx.packageData.Unions { generateUnion(ctx, w, &union) } @@ -129,28 +137,11 @@ func generatePackage(ctx *context, w *bufio.Writer) error { if len(ctx.packageData.Messages) > 0 { fmt.Fprintf(w, "/* Messages */\n\n") - ctx.inputBuff = bytes.NewBuffer(ctx.inputData) - ctx.inputLine = 0 for _, msg := range ctx.packageData.Messages { generateMessage(ctx, w, &msg) } } - // generate services - if len(ctx.packageData.Services) > 0 { - fmt.Fprintf(w, "/* Services */\n\n") - - ctx.inputBuff = bytes.NewBuffer(ctx.inputData) - ctx.inputLine = 0 - fmt.Fprintf(w, "type %s interface {\n", "Services") - for _, svc := range ctx.packageData.Services { - generateService(ctx, w, &svc) - } - fmt.Fprintln(w, "}") - } - - // TODO: generate implementation for Services interface - // generate message registrations fmt.Fprintln(w) fmt.Fprintln(w, "func init() {") @@ -181,13 +172,18 @@ func generateHeader(ctx *context, w io.Writer) { var printObjNum = func(obj string, num int) { if num > 0 { if num > 1 { - obj += "s" + if strings.HasSuffix(obj, "s") { + obj += "es" + } else { + obj += "s" + } } fmt.Fprintf(w, "\t%3d %s\n", num, obj) } } printObjNum("message", len(ctx.packageData.Messages)) printObjNum("type", len(ctx.packageData.Types)) + printObjNum("alias", len(ctx.packageData.Aliases)) printObjNum("enum", len(ctx.packageData.Enums)) printObjNum("union", len(ctx.packageData.Unions)) printObjNum("service", len(ctx.packageData.Services)) @@ -213,44 +209,96 @@ func generateImports(ctx *context, w io.Writer) { // generateComment writes generated comment for the object into w func generateComment(ctx *context, w io.Writer, goName string, vppName string, objKind string) { - fmt.Fprintf(w, "// %s represents the VPP binary API %s '%s'.\n", goName, objKind, vppName) + if objKind == "service" { + fmt.Fprintf(w, "// %s represents VPP binary API services:\n", goName) + } else { + fmt.Fprintf(w, "// %s represents VPP binary API %s '%s':\n", goName, objKind, vppName) + } var isNotSpace = func(r rune) bool { return !unicode.IsSpace(r) } // print out the source of the generated object + mapType := false objFound := false objTitle := fmt.Sprintf(`"%s",`, vppName) + switch objKind { + case "alias", "service": + objTitle = fmt.Sprintf(`"%s": {`, vppName) + mapType = true + } + + inputBuff := bytes.NewBuffer(ctx.inputData) + inputLine := 0 + + var trimIndent string var indent int for { - line, err := ctx.inputBuff.ReadString('\n') + line, err := inputBuff.ReadString('\n') if err != nil { break } - ctx.inputLine++ + inputLine++ + noSpaceAt := strings.IndexFunc(line, isNotSpace) if !objFound { indent = strings.Index(line, objTitle) if indent == -1 { continue } + trimIndent = line[:indent] // If no other non-whitespace character then we are at the message header. if trimmed := strings.TrimSpace(line); trimmed == objTitle { objFound = true fmt.Fprintln(w, "//") } - } else { - if strings.IndexFunc(line, isNotSpace) < indent { - break // end of the object definition in JSON - } + } else if noSpaceAt < indent { + break // end of the definition in JSON for array types + } else if objFound && mapType && noSpaceAt <= indent { + fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent)) + break // end of the definition in JSON for map types (aliases, services) } - fmt.Fprint(w, "//", line) + fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent)) } fmt.Fprintln(w, "//") } +// generateServices writes generated code for the Services interface into w +func generateServices(ctx *context, w *bufio.Writer, services []Service) { + // generate services comment + generateComment(ctx, w, "Services", "services", "service") + + // generate interface + fmt.Fprintf(w, "type %s interface {\n", "Services") + for _, svc := range ctx.packageData.Services { + generateService(ctx, w, &svc) + } + fmt.Fprintln(w, "}") + + fmt.Fprintln(w) +} + +// generateService writes generated code for the service into w +func generateService(ctx *context, w io.Writer, svc *Service) { + reqTyp := camelCaseName(svc.RequestType) + + // method name is same as parameter type name by default + method := svc.MethodName() + params := fmt.Sprintf("*%s", reqTyp) + returns := "error" + if replyType := camelCaseName(svc.ReplyType); replyType != "" { + repTyp := fmt.Sprintf("*%s", replyType) + if svc.Stream { + repTyp = fmt.Sprintf("[]%s", repTyp) + } + returns = fmt.Sprintf("(%s, error)", repTyp) + } + + fmt.Fprintf(w, "\t%s(%s) %s\n", method, params, returns) +} + // generateEnum writes generated code for the enum into w func generateEnum(ctx *context, w io.Writer, enum *Enum) { name := camelCaseName(enum.Name) @@ -277,6 +325,28 @@ func generateEnum(ctx *context, w io.Writer, enum *Enum) { fmt.Fprintln(w) } +// generateAlias writes generated code for the alias into w +func generateAlias(ctx *context, w io.Writer, alias *Alias) { + name := camelCaseName(alias.Name) + + logf(" writing type %q (%s), length: %d", alias.Name, name, alias.Length) + + // generate struct comment + generateComment(ctx, w, name, alias.Name, "alias") + + // generate struct definition + fmt.Fprintf(w, "type %s ", name) + + if alias.Length > 0 { + fmt.Fprintf(w, "[%d]", alias.Length) + } + + dataType := convertToGoType(ctx, alias.Type) + fmt.Fprintf(w, "%s\n", dataType) + + fmt.Fprintln(w) +} + // generateType writes generated code for the type into w func generateType(ctx *context, w io.Writer, typ *Type) { name := camelCaseName(typ.Name) @@ -494,21 +564,6 @@ func generateField(ctx *context, w io.Writer, fields []Field, i int) { fmt.Fprintln(w) } -// generateService writes generated code for the service into w -func generateService(ctx *context, w io.Writer, svc *Service) { - reqTyp := camelCaseName(svc.RequestType) - - // method name is same as parameter type name by default - method := svc.MethodName() - params := fmt.Sprintf("*%s", reqTyp) - returns := "error" - if replyTyp := camelCaseName(svc.ReplyType); replyTyp != "" { - returns = fmt.Sprintf("(*%s, error)", replyTyp) - } - - fmt.Fprintf(w, "\t%s(%s) %s\n", method, params, returns) -} - // generateMessageNameGetter generates getter for original VPP message name into the provider writer func generateMessageNameGetter(w io.Writer, structName, msgName string) { fmt.Fprintf(w, `func (*%s) GetMessageName() string { |