|
@@ -0,0 +1,1030 @@
|
|
|
|
|
+// Go support for Protocol Buffers - Google's data interchange format
|
|
|
|
|
+//
|
|
|
|
|
+// Copyright 2010 Google Inc. All rights reserved.
|
|
|
|
|
+// http://code.google.com/p/goprotobuf/
|
|
|
|
|
+//
|
|
|
|
|
+// Redistribution and use in source and binary forms, with or without
|
|
|
|
|
+// modification, are permitted provided that the following conditions are
|
|
|
|
|
+// met:
|
|
|
|
|
+//
|
|
|
|
|
+// * Redistributions of source code must retain the above copyright
|
|
|
|
|
+// notice, this list of conditions and the following disclaimer.
|
|
|
|
|
+// * Redistributions in binary form must reproduce the above
|
|
|
|
|
+// copyright notice, this list of conditions and the following disclaimer
|
|
|
|
|
+// in the documentation and/or other materials provided with the
|
|
|
|
|
+// distribution.
|
|
|
|
|
+// * Neither the name of Google Inc. nor the names of its
|
|
|
|
|
+// contributors may be used to endorse or promote products derived from
|
|
|
|
|
+// this software without specific prior written permission.
|
|
|
|
|
+//
|
|
|
|
|
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
|
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
|
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
|
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
|
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
|
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
|
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
|
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+ The code generator for the plugin for the Google protocol buffer compiler.
|
|
|
|
|
+ It generates Go code from the protocol buffer description files read by the
|
|
|
|
|
+ main routine.
|
|
|
|
|
+
|
|
|
|
|
+ Not supported yet:
|
|
|
|
|
+ services
|
|
|
|
|
+ options
|
|
|
|
|
+*/
|
|
|
|
|
+package generator
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "bytes"
|
|
|
|
|
+ "fmt"
|
|
|
|
|
+ "log"
|
|
|
|
|
+ "os"
|
|
|
|
|
+ "strings"
|
|
|
|
|
+ "unicode"
|
|
|
|
|
+
|
|
|
|
|
+ "goprotobuf.googlecode.com/hg/proto"
|
|
|
|
|
+ plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
|
|
|
|
|
+ descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// A Plugin provides functionality to add to the output during Go code generation,
|
|
|
|
|
+// such as to produce RPC stubs.
|
|
|
|
|
+type Plugin interface {
|
|
|
|
|
+ // Name identifies the plugin.
|
|
|
|
|
+ Name() string
|
|
|
|
|
+ // Generate produces the code generated by the plugin for this file, except for the imports,
|
|
|
|
|
+ // by calling the generator's methods P, In, and Out.
|
|
|
|
|
+ Generate(g *Generator, file *FileDescriptor)
|
|
|
|
|
+ // GenerateImports produces the import declarations for this file.
|
|
|
|
|
+ GenerateImports(g *Generator, file *FileDescriptor)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+var plugins []Plugin
|
|
|
|
|
+
|
|
|
|
|
+// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
|
|
|
|
|
+// It is typically called during initialization.
|
|
|
|
|
+func RegisterPlugin(p Plugin) {
|
|
|
|
|
+ n := len(plugins)
|
|
|
|
|
+ if cap(plugins) == n {
|
|
|
|
|
+ nplugins := make([]Plugin, n, n+10) // very unlikely to need more than this
|
|
|
|
|
+ copy(nplugins, plugins)
|
|
|
|
|
+ plugins = nplugins
|
|
|
|
|
+ }
|
|
|
|
|
+ plugins = plugins[0:n+1]
|
|
|
|
|
+ plugins[n] = p
|
|
|
|
|
+ log.Stderr("installed plugin:", p.Name())
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
|
|
|
|
|
+// a pointer to the FileDescriptorProto that represents it. These types achieve that
|
|
|
|
|
+// wrapping by placing each Proto inside a struct with the pointer to its File. The
|
|
|
|
|
+// structs have the same names as their contents, with "Proto" removed.
|
|
|
|
|
+// FileDescriptor is used to store the things that it points to.
|
|
|
|
|
+
|
|
|
|
|
+// The file and package name method are common to messages and enums.
|
|
|
|
|
+type common struct {
|
|
|
|
|
+ File *descriptor.FileDescriptorProto // File this object comes from.
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// PackageName is name in the package clause in the generated file.
|
|
|
|
|
+func (c *common) PackageName() string { return uniquePackageOf(c.File) }
|
|
|
|
|
+
|
|
|
|
|
+// Descriptor represents a protocol buffer message.
|
|
|
|
|
+type Descriptor struct {
|
|
|
|
|
+ common
|
|
|
|
|
+ *descriptor.DescriptorProto
|
|
|
|
|
+ parent *Descriptor // The containing message, if any.
|
|
|
|
|
+ nested []*Descriptor // Inner messages, if any.
|
|
|
|
|
+ ext []*ExtensionDescriptor // Extensions, if any.
|
|
|
|
|
+ typename []string // Cached typename vector.
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// TypeName returns the elements of the dotted type name.
|
|
|
|
|
+// The package name is not part of this name.
|
|
|
|
|
+func (d *Descriptor) TypeName() []string {
|
|
|
|
|
+ if d.typename != nil {
|
|
|
|
|
+ return d.typename
|
|
|
|
|
+ }
|
|
|
|
|
+ n := 0
|
|
|
|
|
+ for parent := d; parent != nil; parent = parent.parent {
|
|
|
|
|
+ n++
|
|
|
|
|
+ }
|
|
|
|
|
+ s := make([]string, n, n)
|
|
|
|
|
+ for parent := d; parent != nil; parent = parent.parent {
|
|
|
|
|
+ n--
|
|
|
|
|
+ s[n] = proto.GetString(parent.Name)
|
|
|
|
|
+ }
|
|
|
|
|
+ d.typename = s
|
|
|
|
|
+ return s
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
|
|
|
|
|
+// Otherwise it will be the descriptor of the message in which it is defined.
|
|
|
|
|
+type EnumDescriptor struct {
|
|
|
|
|
+ common
|
|
|
|
|
+ *descriptor.EnumDescriptorProto
|
|
|
|
|
+ parent *Descriptor // The containing message, if any.
|
|
|
|
|
+ typename []string // Cached typename vector.
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// TypeName returns the elements of the dotted type name.
|
|
|
|
|
+// The package name is not part of this name.
|
|
|
|
|
+func (e *EnumDescriptor) TypeName() (s []string) {
|
|
|
|
|
+ if e.typename != nil {
|
|
|
|
|
+ return e.typename
|
|
|
|
|
+ }
|
|
|
|
|
+ name := proto.GetString(e.Name)
|
|
|
|
|
+ if e.parent == nil {
|
|
|
|
|
+ s = make([]string, 1)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ pname := e.parent.TypeName()
|
|
|
|
|
+ s = make([]string, len(pname)+1)
|
|
|
|
|
+ copy(s, pname)
|
|
|
|
|
+ }
|
|
|
|
|
+ s[len(s)-1] = name
|
|
|
|
|
+ e.typename = s
|
|
|
|
|
+ return s
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Everything but the last element of the full type name, CamelCased.
|
|
|
|
|
+// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
|
|
|
|
|
+func (e *EnumDescriptor) prefix() string {
|
|
|
|
|
+ typeName := e.TypeName()
|
|
|
|
|
+ ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
|
|
|
|
|
+ if e.parent == nil {
|
|
|
|
|
+ // If the enum is not part of a message, the prefix is just the type name.
|
|
|
|
|
+ ccPrefix = CamelCase(*e.Name) + "_"
|
|
|
|
|
+ }
|
|
|
|
|
+ return ccPrefix
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// The integer value of the named constant in this enumerated type.
|
|
|
|
|
+func (e *EnumDescriptor) integerValueAsString(name string) string {
|
|
|
|
|
+ for _, c := range e.Value {
|
|
|
|
|
+ if proto.GetString(c.Name) == name {
|
|
|
|
|
+ return fmt.Sprint(proto.GetInt32(c.Number))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ log.Exit("cannot find value for enum constant")
|
|
|
|
|
+ return ""
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
|
|
|
|
|
+// Otherwise it will be the descriptor of the message in which it is defined.
|
|
|
|
|
+type ExtensionDescriptor struct {
|
|
|
|
|
+ common
|
|
|
|
|
+ *descriptor.FieldDescriptorProto
|
|
|
|
|
+ parent *Descriptor // The containing message, if any.
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// TypeName returns the elements of the dotted type name.
|
|
|
|
|
+// The package name is not part of this name.
|
|
|
|
|
+func (e *ExtensionDescriptor) TypeName() (s []string) {
|
|
|
|
|
+ name := proto.GetString(e.Name)
|
|
|
|
|
+ if e.parent == nil {
|
|
|
|
|
+ // top-level extension
|
|
|
|
|
+ s = make([]string, 1)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ pname := e.parent.TypeName()
|
|
|
|
|
+ s = make([]string, len(pname)+1)
|
|
|
|
|
+ copy(s, pname)
|
|
|
|
|
+ }
|
|
|
|
|
+ s[len(s)-1] = name
|
|
|
|
|
+ return s
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// FileDescriptor describes an protocol buffer descriptor file (.proto).
|
|
|
|
|
+// It includes slices of all the messages and enums defined within it.
|
|
|
|
|
+// Those slices are constructed by WrapTypes.
|
|
|
|
|
+type FileDescriptor struct {
|
|
|
|
|
+ *descriptor.FileDescriptorProto
|
|
|
|
|
+ desc []*Descriptor // All the messages defined in this file.
|
|
|
|
|
+ enum []*EnumDescriptor // All the enums defined in this file.
|
|
|
|
|
+ ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// PackageName is the package name we'll use in the generated code to refer to this file.
|
|
|
|
|
+func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
|
|
|
|
|
+
|
|
|
|
|
+// The package named defined in the input for this file, possibly dotted.
|
|
|
|
|
+func (d *FileDescriptor) originalPackageName() string {
|
|
|
|
|
+ return proto.GetString(d.Package)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Whether the proto library needs importing.
|
|
|
|
|
+// This will be true if there are any enums, extensions, or messages with extension ranges.
|
|
|
|
|
+func (d *FileDescriptor) needProtoImport() bool {
|
|
|
|
|
+ if len(d.enum) > 0 || len(d.ext) > 0 {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, desc := range d.desc {
|
|
|
|
|
+ if len(desc.ext) > 0 || len(desc.ExtensionRange) > 0 {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Object is an interface abstracting the abilities shared by enums and messages.
|
|
|
|
|
+type Object interface {
|
|
|
|
|
+ PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
|
|
|
|
|
+ TypeName() []string
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Each package name we generate must be unique. The package we're generating
|
|
|
|
|
+// gets its own name but every other package must have a unqiue name that does
|
|
|
|
|
+// not conflict in the code we generate. These names are chosen globally (although
|
|
|
|
|
+// they don't have to be, it simplifies things to do them globally).
|
|
|
|
|
+func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
|
|
|
|
|
+ s, ok := uniquePackageName[fd]
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
|
|
|
|
|
+ }
|
|
|
|
|
+ return s
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Generator is the type whose methods generate the output, stored in the associated response structure.
|
|
|
|
|
+type Generator struct {
|
|
|
|
|
+ bytes.Buffer
|
|
|
|
|
+
|
|
|
|
|
+ Request *plugin.CodeGeneratorRequest // The input.
|
|
|
|
|
+ Response *plugin.CodeGeneratorResponse // The output.
|
|
|
|
|
+
|
|
|
|
|
+ packageName string // What we're calling ourselves.
|
|
|
|
|
+ allFiles []*FileDescriptor // All files in the tree
|
|
|
|
|
+ genFiles []*FileDescriptor // Those files we will generate output for.
|
|
|
|
|
+ file *FileDescriptor // The file we are compiling now.
|
|
|
|
|
+ typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
|
|
|
|
|
+ indent string
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// New creates a new generator and allocates the request and response protobufs.
|
|
|
|
|
+func New() *Generator {
|
|
|
|
|
+ g := new(Generator)
|
|
|
|
|
+ g.Request = plugin.NewCodeGeneratorRequest()
|
|
|
|
|
+ g.Response = plugin.NewCodeGeneratorResponse()
|
|
|
|
|
+ return g
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Error reports a problem, including an os.Error, and exits the program.
|
|
|
|
|
+func (g *Generator) Error(err os.Error, msgs ...string) {
|
|
|
|
|
+ s := strings.Join(msgs, " ") + ":" + err.String()
|
|
|
|
|
+ log.Stderr("protoc-gen-go: error: ", s)
|
|
|
|
|
+ g.Response.Error = proto.String(s)
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Fail reports a problem and exits the program.
|
|
|
|
|
+func (g *Generator) Fail(msgs ...string) {
|
|
|
|
|
+ s := strings.Join(msgs, " ")
|
|
|
|
|
+ log.Stderr("protoc-gen-go: error: ", s)
|
|
|
|
|
+ g.Response.Error = proto.String(s)
|
|
|
|
|
+ os.Exit(1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// DefaultPackageName returns the package name printed for the object.
|
|
|
|
|
+// If its file is in a different package, it returns the package name we're using for this file, plus ".".
|
|
|
|
|
+// Otherwise it returns the empty string.
|
|
|
|
|
+func (g *Generator) DefaultPackageName(obj Object) string {
|
|
|
|
|
+ pkg := obj.PackageName()
|
|
|
|
|
+ if pkg == g.packageName {
|
|
|
|
|
+ return ""
|
|
|
|
|
+ }
|
|
|
|
|
+ return pkg + "."
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// For each input file, the unique package name to use, underscored.
|
|
|
|
|
+var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
|
|
|
|
|
+
|
|
|
|
|
+// SetPackageNames Sets the package name for this run.
|
|
|
|
|
+// The package name must agree across all files being generated.
|
|
|
|
|
+// It also defines unique package names for all imported files.
|
|
|
|
|
+func (g *Generator) SetPackageNames() {
|
|
|
|
|
+ inUse := make(map[string]bool)
|
|
|
|
|
+ pkg := proto.GetString(g.genFiles[0].Package)
|
|
|
|
|
+ g.packageName = strings.Map(DotToUnderscore, pkg)
|
|
|
|
|
+ inUse[pkg] = true
|
|
|
|
|
+ for _, f := range g.genFiles {
|
|
|
|
|
+ thisPkg := proto.GetString(f.Package)
|
|
|
|
|
+ if thisPkg != pkg {
|
|
|
|
|
+ g.Fail("inconsistent package names:", thisPkg, pkg)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+AllFiles:
|
|
|
|
|
+ for _, f := range g.allFiles {
|
|
|
|
|
+ for _, genf := range g.genFiles {
|
|
|
|
|
+ if f == genf {
|
|
|
|
|
+ // In this package already.
|
|
|
|
|
+ uniquePackageName[f.FileDescriptorProto] = g.packageName
|
|
|
|
|
+ continue AllFiles
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ truePkg := proto.GetString(f.Package)
|
|
|
|
|
+ pkg := truePkg
|
|
|
|
|
+ for {
|
|
|
|
|
+ _, present := inUse[pkg]
|
|
|
|
|
+ if present {
|
|
|
|
|
+ // It's a duplicate; must rename.
|
|
|
|
|
+ pkg += "X"
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ // Install it.
|
|
|
|
|
+ inUse[pkg] = true
|
|
|
|
|
+ uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
|
|
|
|
|
+// and FileDescriptorProtos into file-referenced objects within the Generator.
|
|
|
|
|
+// It also creates the list of files to generate and so should be called before GenerateAllFiles.
|
|
|
|
|
+func (g *Generator) WrapTypes() {
|
|
|
|
|
+ g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
|
|
|
|
|
+ for i, f := range g.Request.ProtoFile {
|
|
|
|
|
+ pkg := proto.GetString(f.Package)
|
|
|
|
|
+ if pkg == "" {
|
|
|
|
|
+ g.Fail(proto.GetString(f.Name), "is missing a package declaration")
|
|
|
|
|
+ }
|
|
|
|
|
+ // We must wrap the descriptors before we wrap the enums
|
|
|
|
|
+ descs := wrapDescriptors(f)
|
|
|
|
|
+ g.buildNestedDescriptors(descs)
|
|
|
|
|
+ enums := wrapEnumDescriptors(f, descs)
|
|
|
|
|
+ exts := wrapExtensions(f)
|
|
|
|
|
+ g.allFiles[i] = &FileDescriptor{
|
|
|
|
|
+ FileDescriptorProto: f,
|
|
|
|
|
+ desc: descs,
|
|
|
|
|
+ enum: enums,
|
|
|
|
|
+ ext: exts,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
|
|
|
|
|
+FindFiles:
|
|
|
|
|
+ for i, fileName := range g.Request.FileToGenerate {
|
|
|
|
|
+ // Search the list. This algorithm is n^2 but n is tiny.
|
|
|
|
|
+ for _, file := range g.allFiles {
|
|
|
|
|
+ if fileName == proto.GetString(file.Name) {
|
|
|
|
|
+ g.genFiles[i] = file
|
|
|
|
|
+ continue FindFiles
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Fail("could not find file named", fileName)
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Scan the descriptors in this file. For each one, build the slice of nested descriptors
|
|
|
|
|
+func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
|
|
|
|
|
+ for _, desc := range descs {
|
|
|
|
|
+ if len(desc.NestedType) != 0 {
|
|
|
|
|
+ desc.nested = make([]*Descriptor, len(desc.NestedType))
|
|
|
|
|
+ n := 0
|
|
|
|
|
+ for _, nest := range descs {
|
|
|
|
|
+ if nest.parent == desc {
|
|
|
|
|
+ desc.nested[n] = nest
|
|
|
|
|
+ n++
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if n != len(desc.NestedType) {
|
|
|
|
|
+ g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Construct the Descriptor and add it to the slice
|
|
|
|
|
+func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
|
|
|
|
|
+ d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
|
|
|
|
|
+
|
|
|
|
|
+ d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
|
|
|
|
|
+ for i, field := range desc.Extension {
|
|
|
|
|
+ d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if len(sl) == cap(sl) {
|
|
|
|
|
+ nsl := make([]*Descriptor, len(sl), 2*len(sl))
|
|
|
|
|
+ copy(nsl, sl)
|
|
|
|
|
+ sl = nsl
|
|
|
|
|
+ }
|
|
|
|
|
+ sl = sl[0 : len(sl)+1]
|
|
|
|
|
+ sl[len(sl)-1] = d
|
|
|
|
|
+ return sl
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Return a slice of all the Descriptors defined within this file
|
|
|
|
|
+func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
|
|
|
|
|
+ sl := make([]*Descriptor, 0, len(file.MessageType)+10)
|
|
|
|
|
+ for _, desc := range file.MessageType {
|
|
|
|
|
+ sl = wrapThisDescriptor(sl, desc, nil, file)
|
|
|
|
|
+ }
|
|
|
|
|
+ return sl
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Wrap this Descriptor, recursively
|
|
|
|
|
+func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
|
|
|
|
|
+ sl = addDescriptor(sl, desc, parent, file)
|
|
|
|
|
+ me := sl[len(sl)-1]
|
|
|
|
|
+ for _, nested := range desc.NestedType {
|
|
|
|
|
+ sl = wrapThisDescriptor(sl, nested, me, file)
|
|
|
|
|
+ }
|
|
|
|
|
+ return sl
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Construct the EnumDescriptor and add it to the slice
|
|
|
|
|
+func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
|
|
|
|
|
+ if len(sl) == cap(sl) {
|
|
|
|
|
+ nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
|
|
|
|
|
+ copy(nsl, sl)
|
|
|
|
|
+ sl = nsl
|
|
|
|
|
+ }
|
|
|
|
|
+ sl = sl[0 : len(sl)+1]
|
|
|
|
|
+ sl[len(sl)-1] = &EnumDescriptor{common{File: file}, desc, parent, nil}
|
|
|
|
|
+ return sl
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Return a slice of all the EnumDescriptors defined within this file
|
|
|
|
|
+func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
|
|
|
|
|
+ sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
|
|
|
|
|
+ for _, enum := range file.EnumType {
|
|
|
|
|
+ sl = addEnumDescriptor(sl, enum, nil, file)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, nested := range descs {
|
|
|
|
|
+ sl = wrapEnumDescriptorsInMessage(sl, nested, file)
|
|
|
|
|
+ }
|
|
|
|
|
+ return sl
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Wrap this EnumDescriptor, recursively
|
|
|
|
|
+func wrapEnumDescriptorsInMessage(sl []*EnumDescriptor, desc *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
|
|
|
|
|
+ for _, enum := range desc.EnumType {
|
|
|
|
|
+ sl = addEnumDescriptor(sl, enum, desc, file)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, nested := range desc.nested {
|
|
|
|
|
+ sl = wrapEnumDescriptorsInMessage(sl, nested, file)
|
|
|
|
|
+ }
|
|
|
|
|
+ return sl
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Return a slice of all the top-level ExtensionDescriptors defined within this file.
|
|
|
|
|
+func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
|
|
|
|
|
+ sl := make([]*ExtensionDescriptor, len(file.Extension))
|
|
|
|
|
+ for i, field := range file.Extension {
|
|
|
|
|
+ sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
|
|
|
|
|
+ }
|
|
|
|
|
+ return sl
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// BuildTypeNameMap builds the map from fully qualified type names to objects.
|
|
|
|
|
+// The key names for the map come from the input data, which puts a period at the beginning.
|
|
|
|
|
+// It should be called after SetPackageNames and before GenerateAllFiles.
|
|
|
|
|
+func (g *Generator) BuildTypeNameMap() {
|
|
|
|
|
+ g.typeNameToObject = make(map[string]Object)
|
|
|
|
|
+ for _, f := range g.allFiles {
|
|
|
|
|
+ dottedPkg := "." + f.originalPackageName() + "."
|
|
|
|
|
+ for _, enum := range f.enum {
|
|
|
|
|
+ name := dottedPkg + dottedSlice(enum.TypeName())
|
|
|
|
|
+ g.typeNameToObject[name] = enum
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, desc := range f.desc {
|
|
|
|
|
+ name := dottedPkg + dottedSlice(desc.TypeName())
|
|
|
|
|
+ g.typeNameToObject[name] = desc
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
|
|
|
|
|
+// returns the descriptor for the message or enum with that name.
|
|
|
|
|
+func (g *Generator) ObjectNamed(typeName string) Object {
|
|
|
|
|
+ f, ok := g.typeNameToObject[typeName]
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ g.Fail("can't find object with type", typeName)
|
|
|
|
|
+ }
|
|
|
|
|
+ return f
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// P prints the arguments to the generated output. It handles strings and int32s, plus
|
|
|
|
|
+// handling indirections because they may be *string, etc.
|
|
|
|
|
+func (g *Generator) P(str ...interface{}) {
|
|
|
|
|
+ g.WriteString(g.indent)
|
|
|
|
|
+ for _, v := range str {
|
|
|
|
|
+ switch s := v.(type) {
|
|
|
|
|
+ case string:
|
|
|
|
|
+ g.WriteString(s)
|
|
|
|
|
+ case *string:
|
|
|
|
|
+ g.WriteString(*s)
|
|
|
|
|
+ case *int32:
|
|
|
|
|
+ g.WriteString(fmt.Sprintf("%d", *s))
|
|
|
|
|
+ default:
|
|
|
|
|
+ g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ g.WriteByte('\n')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// In Indents the output one tab stop.
|
|
|
|
|
+func (g *Generator) In() { g.indent += "\t" }
|
|
|
|
|
+
|
|
|
|
|
+// Out unindents the output one tab stop.
|
|
|
|
|
+func (g *Generator) Out() {
|
|
|
|
|
+ if len(g.indent) > 0 {
|
|
|
|
|
+ g.indent = g.indent[1:]
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GenerateAllFiles generates the output for all the files we're outputting.
|
|
|
|
|
+func (g *Generator) GenerateAllFiles() {
|
|
|
|
|
+ for i, file := range g.genFiles {
|
|
|
|
|
+ g.Reset()
|
|
|
|
|
+ g.generate(file)
|
|
|
|
|
+ g.runPlugins(file)
|
|
|
|
|
+ g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
|
|
|
|
|
+ g.Response.File[i].Name = proto.String(goFileName(*file.Name))
|
|
|
|
|
+ g.Response.File[i].Content = proto.String(g.String())
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Run all the plugins associated with the file.
|
|
|
|
|
+func (g *Generator) runPlugins(file *FileDescriptor) {
|
|
|
|
|
+ for _, p := range plugins {
|
|
|
|
|
+ p.Generate(g, file)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// FileOf return the FileDescriptor for this FileDescriptorProto.
|
|
|
|
|
+func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
|
|
|
|
|
+ for _, file := range g.allFiles {
|
|
|
|
|
+ if file.FileDescriptorProto == fd {
|
|
|
|
|
+ return file
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Fail("could not find file in table:", proto.GetString(fd.Name))
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Fill the response protocol buffer with the generated output for all the files we're
|
|
|
|
|
+// supposed to generate.
|
|
|
|
|
+func (g *Generator) generate(file *FileDescriptor) {
|
|
|
|
|
+ g.file = g.FileOf(file.FileDescriptorProto)
|
|
|
|
|
+ g.generateHeader()
|
|
|
|
|
+ g.generateImports()
|
|
|
|
|
+ for _, enum := range g.file.enum {
|
|
|
|
|
+ g.generateEnum(enum)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, desc := range g.file.desc {
|
|
|
|
|
+ g.generateMessage(desc)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, ext := range g.file.ext {
|
|
|
|
|
+ g.generateExtension(ext)
|
|
|
|
|
+ }
|
|
|
|
|
+ g.generateInitFunction()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Generate the header, including package definition and imports
|
|
|
|
|
+func (g *Generator) generateHeader() {
|
|
|
|
|
+ g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
|
|
|
|
|
+ g.P("// DO NOT EDIT!")
|
|
|
|
|
+ g.P()
|
|
|
|
|
+ g.P("package ", g.file.PackageName())
|
|
|
|
|
+ g.P()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Generate the header, including package definition and imports
|
|
|
|
|
+func (g *Generator) generateImports() {
|
|
|
|
|
+ if g.file.needProtoImport() {
|
|
|
|
|
+ g.P(`import "goprotobuf.googlecode.com/hg/proto"`)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, s := range g.file.Dependency {
|
|
|
|
|
+ // Need to find the descriptor for this file
|
|
|
|
|
+ for _, fd := range g.allFiles {
|
|
|
|
|
+ if proto.GetString(fd.Name) == s {
|
|
|
|
|
+ filename := goFileName(s)
|
|
|
|
|
+ if strings.HasSuffix(filename, ".go") {
|
|
|
|
|
+ filename = filename[0:len(filename)-3]
|
|
|
|
|
+ }
|
|
|
|
|
+ g.P("import ", fd.PackageName(), " ", Quote(filename))
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ g.P()
|
|
|
|
|
+ // TODO: may need to worry about uniqueness across plugins
|
|
|
|
|
+ for _, p := range plugins {
|
|
|
|
|
+ p.GenerateImports(g, g.file)
|
|
|
|
|
+ g.P()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Generate the enum definitions for this EnumDescriptor.
|
|
|
|
|
+func (g *Generator) generateEnum(enum *EnumDescriptor) {
|
|
|
|
|
+ // The full type name
|
|
|
|
|
+ typeName := enum.TypeName()
|
|
|
|
|
+ // The full type name, CamelCased.
|
|
|
|
|
+ ccTypeName := CamelCaseSlice(typeName)
|
|
|
|
|
+ ccPrefix := enum.prefix()
|
|
|
|
|
+ g.P("type ", ccTypeName, " int32")
|
|
|
|
|
+ g.P("const (")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ for _, e := range enum.Value {
|
|
|
|
|
+ g.P(ccPrefix+*e.Name, " = ", e.Number)
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P(")")
|
|
|
|
|
+ g.P("var ", ccTypeName, "_name = map[int32] string {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ generated := make(map[int32] bool) // avoid duplicate values
|
|
|
|
|
+ for _, e := range enum.Value {
|
|
|
|
|
+ duplicate := ""
|
|
|
|
|
+ if _, present := generated[*e.Number]; present {
|
|
|
|
|
+ duplicate = "// Duplicate value: "
|
|
|
|
|
+ }
|
|
|
|
|
+ g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
|
|
|
|
|
+ generated[*e.Number] = true
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P("var ", ccTypeName, "_value = map[string] int32 {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ for _, e := range enum.Value {
|
|
|
|
|
+ g.P(Quote(*e.Name), ": ", e.Number, ",")
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ g.P("e := ", ccTypeName, "(x)")
|
|
|
|
|
+ g.P("return &e")
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
|
|
|
|
|
+// identifies details of the field for the protocol buffer marshaling and unmarshaling
|
|
|
|
|
+// code. The fields are:
|
|
|
|
|
+// wire encoding
|
|
|
|
|
+// protocol tag number
|
|
|
|
|
+// opt,req,rep for optional, required, or repeated
|
|
|
|
|
+// name= the original declared name
|
|
|
|
|
+// enum= the name of the enum type if it is an enum-typed field.
|
|
|
|
|
+// def= string representation of the default value, if any.
|
|
|
|
|
+// The default value must be in a representation that can be used at run-time
|
|
|
|
|
+// to generate the default value. Thus bools become 0 and 1, for instance.
|
|
|
|
|
+func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
|
|
|
|
|
+ optrepreq := ""
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case isOptional(field):
|
|
|
|
|
+ optrepreq = "opt"
|
|
|
|
|
+ case isRequired(field):
|
|
|
|
|
+ optrepreq = "req"
|
|
|
|
|
+ case isRepeated(field):
|
|
|
|
|
+ optrepreq = "rep"
|
|
|
|
|
+ }
|
|
|
|
|
+ defaultValue := proto.GetString(field.DefaultValue)
|
|
|
|
|
+ if defaultValue != "" {
|
|
|
|
|
+ switch *field.Type {
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
|
|
|
|
+ if defaultValue == "true" {
|
|
|
|
|
+ defaultValue = "1"
|
|
|
|
|
+ } else {
|
|
|
|
|
+ defaultValue = "0"
|
|
|
|
|
+ }
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_STRING,
|
|
|
|
|
+ descriptor.FieldDescriptorProto_TYPE_BYTES:
|
|
|
|
|
+ // Protect frogs.
|
|
|
|
|
+ defaultValue = Quote(defaultValue)
|
|
|
|
|
+ // Don't need the quotes
|
|
|
|
|
+ defaultValue = defaultValue[1 : len(defaultValue)-1]
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_ENUM:
|
|
|
|
|
+ // For enums we need to provide the integer constant.
|
|
|
|
|
+ obj := g.ObjectNamed(proto.GetString(field.TypeName))
|
|
|
|
|
+ enum, ok := obj.(*EnumDescriptor)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
|
|
|
|
|
+ }
|
|
|
|
|
+ defaultValue = enum.integerValueAsString(defaultValue)
|
|
|
|
|
+ }
|
|
|
|
|
+ defaultValue = ",def=" + defaultValue
|
|
|
|
|
+ }
|
|
|
|
|
+ enum := ""
|
|
|
|
|
+ if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
|
|
|
|
|
+ obj := g.ObjectNamed(proto.GetString(field.TypeName))
|
|
|
|
|
+ enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
|
|
|
|
|
+ }
|
|
|
|
|
+ name := proto.GetString(field.Name)
|
|
|
|
|
+ if name == CamelCase(name) {
|
|
|
|
|
+ name = ""
|
|
|
|
|
+ } else {
|
|
|
|
|
+ name = ",name=" + name
|
|
|
|
|
+ }
|
|
|
|
|
+ return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
|
|
|
|
|
+ wiretype,
|
|
|
|
|
+ proto.GetInt32(field.Number),
|
|
|
|
|
+ optrepreq,
|
|
|
|
|
+ name,
|
|
|
|
|
+ enum,
|
|
|
|
|
+ defaultValue))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
|
|
|
|
|
+ switch typ {
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_GROUP:
|
|
|
|
|
+ return false
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
|
|
|
|
+ return false
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// TypeName is the printed name appropriate for an item. If the object is in the current file,
|
|
|
|
|
+// TypeName drops the package name and underscores the rest.
|
|
|
|
|
+// Otherwise the object is from another package; and the result is the underscored
|
|
|
|
|
+// package name followed by the item name.
|
|
|
|
|
+// The result always has an initial capital.
|
|
|
|
|
+func (g *Generator) TypeName(obj Object) string {
|
|
|
|
|
+ return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// TypeNameWithPackage is like TypeName, but always includes the package
|
|
|
|
|
+// name even if the object is in our own package.
|
|
|
|
|
+func (g *Generator) TypeNameWithPackage(obj Object) string {
|
|
|
|
|
+ return obj.PackageName() + CamelCaseSlice(obj.TypeName())
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GoType returns a string representing the type name, and the wire type
|
|
|
|
|
+func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
|
|
|
|
|
+ // TODO: Options.
|
|
|
|
|
+ switch *field.Type {
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
|
|
|
|
|
+ typ, wire = "float64", "fixed64"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_FLOAT:
|
|
|
|
|
+ typ, wire = "float32", "fixed32"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_INT64:
|
|
|
|
|
+ typ, wire = "int64", "varint"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_UINT64:
|
|
|
|
|
+ typ, wire = "uint64", "varint"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_INT32:
|
|
|
|
|
+ typ, wire = "int32", "varint"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_UINT32:
|
|
|
|
|
+ typ, wire = "uint32", "varint"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_FIXED64:
|
|
|
|
|
+ typ, wire = "uint64", "fixed64"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_FIXED32:
|
|
|
|
|
+ typ, wire = "uint32", "fixed32"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
|
|
|
|
+ typ, wire = "bool", "varint"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_STRING:
|
|
|
|
|
+ typ, wire = "string", "bytes"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_GROUP:
|
|
|
|
|
+ desc := g.ObjectNamed(proto.GetString(field.TypeName))
|
|
|
|
|
+ typ, wire = "*"+g.TypeName(desc), "group"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
|
|
|
|
+ desc := g.ObjectNamed(proto.GetString(field.TypeName))
|
|
|
|
|
+ typ, wire = "*"+g.TypeName(desc), "bytes"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
|
|
|
|
+ typ, wire = "[]byte", "bytes"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_ENUM:
|
|
|
|
|
+ desc := g.ObjectNamed(proto.GetString(field.TypeName))
|
|
|
|
|
+ typ, wire = g.TypeName(desc), "varint"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
|
|
|
|
|
+ typ, wire = "int32", "fixed32"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
|
|
|
|
|
+ typ, wire = "int64", "fixed64"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_SINT32:
|
|
|
|
|
+ typ, wire = "int32", "zigzag32"
|
|
|
|
|
+ case descriptor.FieldDescriptorProto_TYPE_SINT64:
|
|
|
|
|
+ typ, wire = "int64", "zigzag64"
|
|
|
|
|
+ default:
|
|
|
|
|
+ g.Fail("unknown type for", proto.GetString(field.Name))
|
|
|
|
|
+ }
|
|
|
|
|
+ if isRepeated(field) {
|
|
|
|
|
+ typ = "[]" + typ
|
|
|
|
|
+ } else if needsStar(*field.Type) {
|
|
|
|
|
+ typ = "*" + typ
|
|
|
|
|
+ }
|
|
|
|
|
+ return
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Generate the type and default constant definitions for this Descriptor.
|
|
|
|
|
+func (g *Generator) generateMessage(message *Descriptor) {
|
|
|
|
|
+ // The full type name
|
|
|
|
|
+ typeName := message.TypeName()
|
|
|
|
|
+ // The full type name, CamelCased.
|
|
|
|
|
+ ccTypeName := CamelCaseSlice(typeName)
|
|
|
|
|
+
|
|
|
|
|
+ g.P("type ", ccTypeName, " struct {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ for _, field := range message.Field {
|
|
|
|
|
+ fieldname := CamelCase(*field.Name)
|
|
|
|
|
+ typename, wiretype := g.GoType(message, field)
|
|
|
|
|
+ tag := g.goTag(field, wiretype)
|
|
|
|
|
+ g.P(fieldname, "\t", typename, "\t", tag)
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(message.ExtensionRange) > 0 {
|
|
|
|
|
+ g.P("XXX_extensions\t\tmap[int32][]byte")
|
|
|
|
|
+ }
|
|
|
|
|
+ g.P("XXX_unrecognized\t[]byte")
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+
|
|
|
|
|
+ // Reset and New functions
|
|
|
|
|
+ g.P("func (this *", ccTypeName, ") Reset() {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ g.P("*this = ", ccTypeName, "{}")
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P("func New", ccTypeName, "() *", ccTypeName, " {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ g.P("return new(", ccTypeName, ")")
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+
|
|
|
|
|
+ // Extension support methods
|
|
|
|
|
+ if len(message.ExtensionRange) > 0 {
|
|
|
|
|
+ g.P()
|
|
|
|
|
+ g.P("var extRange_", ccTypeName, " = []proto.ExtensionRange{")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ for _, r := range message.ExtensionRange {
|
|
|
|
|
+ end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
|
|
|
|
|
+ g.P("proto.ExtensionRange{", r.Start, ", ", end, "},")
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P("func (*", ccTypeName, ") ExtensionRangeArray() []proto.ExtensionRange {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ g.P("return extRange_", ccTypeName)
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ g.P("if this.XXX_extensions == nil {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ g.P("this.XXX_extensions = make(map[int32][]byte)")
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P("return this.XXX_extensions")
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Default constants
|
|
|
|
|
+ for _, field := range message.Field {
|
|
|
|
|
+ def := proto.GetString(field.DefaultValue)
|
|
|
|
|
+ if def == "" {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
|
|
|
|
|
+ typename, _ := g.GoType(message, field)
|
|
|
|
|
+ if typename[0] == '*' {
|
|
|
|
|
+ typename = typename[1:]
|
|
|
|
|
+ }
|
|
|
|
|
+ kind := "const "
|
|
|
|
|
+ switch {
|
|
|
|
|
+ case typename == "bool":
|
|
|
|
|
+ case typename == "string":
|
|
|
|
|
+ def = Quote(def)
|
|
|
|
|
+ case typename == "[]byte":
|
|
|
|
|
+ def = "[]byte(" + Quote(def) + ")"
|
|
|
|
|
+ kind = "var "
|
|
|
|
|
+ case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
|
|
|
|
|
+ // Must be an enum. Need to construct the prefixed name.
|
|
|
|
|
+ obj := g.ObjectNamed(proto.GetString(field.TypeName))
|
|
|
|
|
+ enum, ok := obj.(*EnumDescriptor)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ log.Stderr("don't know how to generate constant for", fieldname)
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ def = enum.prefix() + def
|
|
|
|
|
+ }
|
|
|
|
|
+ g.P(kind, fieldname, " ", typename, " = ", def)
|
|
|
|
|
+ }
|
|
|
|
|
+ g.P()
|
|
|
|
|
+
|
|
|
|
|
+ for _, ext := range message.ext {
|
|
|
|
|
+ g.generateExtension(ext)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
|
|
|
|
|
+ // The full type name
|
|
|
|
|
+ typeName := ext.TypeName()
|
|
|
|
|
+ // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
|
|
|
|
|
+ for i, s := range typeName {
|
|
|
|
|
+ typeName[i] = CamelCase(s)
|
|
|
|
|
+ }
|
|
|
|
|
+ ccTypeName := "E_" + strings.Join(typeName, "_")
|
|
|
|
|
+
|
|
|
|
|
+ extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
|
|
|
|
|
+ field := ext.FieldDescriptorProto
|
|
|
|
|
+ fieldType, wireType := g.GoType(ext.parent, field)
|
|
|
|
|
+ tag := g.goTag(field, wireType)
|
|
|
|
|
+
|
|
|
|
|
+ g.P("var ", ccTypeName, " = &proto.ExtensionDesc{")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ g.P("ExtendedType: (", extendedType, ")(nil),")
|
|
|
|
|
+ g.P("ExtensionType: (", fieldType, ")(nil),")
|
|
|
|
|
+ g.P("Field: ", field.Number, ",")
|
|
|
|
|
+ g.P("Tag: ", tag, ",")
|
|
|
|
|
+
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+ g.P()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (g *Generator) generateInitFunction() {
|
|
|
|
|
+ g.P("func init() {")
|
|
|
|
|
+ g.In()
|
|
|
|
|
+ for _, enum := range g.file.enum {
|
|
|
|
|
+ g.generateEnumRegistration(enum)
|
|
|
|
|
+ }
|
|
|
|
|
+ g.Out()
|
|
|
|
|
+ g.P("}")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
|
|
|
|
|
+ pkg := g.packageName + "." // We always print the full package name here.
|
|
|
|
|
+ // The full type name
|
|
|
|
|
+ typeName := enum.TypeName()
|
|
|
|
|
+ // The full type name, CamelCased.
|
|
|
|
|
+ ccTypeName := CamelCaseSlice(typeName)
|
|
|
|
|
+ g.P("proto.RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// And now lots of helper functions.
|
|
|
|
|
+
|
|
|
|
|
+// CamelCase returns the CamelCased name. Given foo_bar_Baz, the result is FooBar_Baz.
|
|
|
|
|
+func CamelCase(name string) string {
|
|
|
|
|
+ elems := strings.Split(name, "_", 0)
|
|
|
|
|
+ for i, e := range elems {
|
|
|
|
|
+ if e == "" {
|
|
|
|
|
+ elems[i] = "_"
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ runes := []int(e)
|
|
|
|
|
+ if unicode.IsLower(runes[0]) {
|
|
|
|
|
+ runes[0] = unicode.ToUpper(runes[0])
|
|
|
|
|
+ elems[i] = string(runes)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if i > 0 {
|
|
|
|
|
+ elems[i] = "_" + e
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ s := strings.Join(elems, "")
|
|
|
|
|
+ // Name must not begin with an underscore.
|
|
|
|
|
+ if len(s) > 0 && s[0] == '_' {
|
|
|
|
|
+ s = "X" + s[1:]
|
|
|
|
|
+ }
|
|
|
|
|
+ return s
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
|
|
|
|
|
+// be joined with "_".
|
|
|
|
|
+func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
|
|
|
|
|
+
|
|
|
|
|
+// dottedSlice turns a sliced name into a dotted name.
|
|
|
|
|
+func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
|
|
|
|
|
+
|
|
|
|
|
+// Quote returns a Go-source quoted string representation of s.
|
|
|
|
|
+func Quote(s string) string { return fmt.Sprintf("%q", s) }
|
|
|
|
|
+
|
|
|
|
|
+// Given a .proto file name, return the output name for the generated Go program.
|
|
|
|
|
+func goFileName(name string) string {
|
|
|
|
|
+ if strings.HasSuffix(name, ".proto") {
|
|
|
|
|
+ name = name[0 : len(name)-6]
|
|
|
|
|
+ }
|
|
|
|
|
+ return name + ".pb.go"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Is this field optional?
|
|
|
|
|
+func isOptional(field *descriptor.FieldDescriptorProto) bool {
|
|
|
|
|
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Is this field required?
|
|
|
|
|
+func isRequired(field *descriptor.FieldDescriptorProto) bool {
|
|
|
|
|
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Is this field repeated?
|
|
|
|
|
+func isRepeated(field *descriptor.FieldDescriptorProto) bool {
|
|
|
|
|
+ return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// DotToUnderscore is the mapping function used to generate Go names from package names,
|
|
|
|
|
+// which can be dotted in the input .proto file.
|
|
|
|
|
+func DotToUnderscore(rune int) int {
|
|
|
|
|
+ if rune == '.' {
|
|
|
|
|
+ return '_'
|
|
|
|
|
+ }
|
|
|
|
|
+ return rune
|
|
|
|
|
+}
|