blob: 095bfc3d469fef03edc57c13eea5886ad3830485 [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33 The code generator for the plugin for the Google protocol buffer compiler.
34 It generates Go code from the protocol buffer description files read by the
35 main routine.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070036*/
37package generator
38
39import (
40 "bytes"
41 "fmt"
David Symondsb1d55a02011-04-08 09:55:06 +100042 "go/parser"
43 "go/printer"
44 "go/token"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070045 "log"
46 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070047 "path"
David Symonds79eae332010-10-16 11:33:20 +110048 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070049 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070050
51 "goprotobuf.googlecode.com/hg/proto"
David Symonds9f402812011-04-28 18:08:44 +100052 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070053 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
54)
55
56// A Plugin provides functionality to add to the output during Go code generation,
57// such as to produce RPC stubs.
58type Plugin interface {
59 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070060 Name() string
61 // Init is called once after data structures are built but before
62 // code generation begins.
63 Init(g *Generator)
64 // Generate produces the code generated by the plugin for this file,
65 // except for the imports, by calling the generator's methods P, In, and Out.
66 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070067 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070068 // It is called after Generate.
69 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070070}
71
72var plugins []Plugin
73
74// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
75// It is typically called during initialization.
76func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110077 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070078}
79
80// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
81// a pointer to the FileDescriptorProto that represents it. These types achieve that
82// wrapping by placing each Proto inside a struct with the pointer to its File. The
83// structs have the same names as their contents, with "Proto" removed.
84// FileDescriptor is used to store the things that it points to.
85
86// The file and package name method are common to messages and enums.
87type common struct {
David Symonds4decd802011-08-04 11:27:07 +100088 file *descriptor.FileDescriptorProto // File this object comes from.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070089}
90
91// PackageName is name in the package clause in the generated file.
David Symonds4decd802011-08-04 11:27:07 +100092func (c *common) PackageName() string { return uniquePackageOf(c.file) }
93
94func (c *common) File() *descriptor.FileDescriptorProto { return c.file }
Rob Pikeaf82b4e2010-04-30 15:19:25 -070095
96// Descriptor represents a protocol buffer message.
97type Descriptor struct {
98 common
99 *descriptor.DescriptorProto
100 parent *Descriptor // The containing message, if any.
101 nested []*Descriptor // Inner messages, if any.
102 ext []*ExtensionDescriptor // Extensions, if any.
103 typename []string // Cached typename vector.
104}
105
106// TypeName returns the elements of the dotted type name.
107// The package name is not part of this name.
108func (d *Descriptor) TypeName() []string {
109 if d.typename != nil {
110 return d.typename
111 }
112 n := 0
113 for parent := d; parent != nil; parent = parent.parent {
114 n++
115 }
116 s := make([]string, n, n)
117 for parent := d; parent != nil; parent = parent.parent {
118 n--
119 s[n] = proto.GetString(parent.Name)
120 }
121 d.typename = s
122 return s
123}
124
125// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
126// Otherwise it will be the descriptor of the message in which it is defined.
127type EnumDescriptor struct {
128 common
129 *descriptor.EnumDescriptorProto
130 parent *Descriptor // The containing message, if any.
131 typename []string // Cached typename vector.
132}
133
134// TypeName returns the elements of the dotted type name.
135// The package name is not part of this name.
136func (e *EnumDescriptor) TypeName() (s []string) {
137 if e.typename != nil {
138 return e.typename
139 }
140 name := proto.GetString(e.Name)
141 if e.parent == nil {
142 s = make([]string, 1)
143 } else {
144 pname := e.parent.TypeName()
145 s = make([]string, len(pname)+1)
146 copy(s, pname)
147 }
148 s[len(s)-1] = name
149 e.typename = s
150 return s
151}
152
153// Everything but the last element of the full type name, CamelCased.
154// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
155func (e *EnumDescriptor) prefix() string {
156 typeName := e.TypeName()
157 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
158 if e.parent == nil {
159 // If the enum is not part of a message, the prefix is just the type name.
160 ccPrefix = CamelCase(*e.Name) + "_"
161 }
162 return ccPrefix
163}
164
165// The integer value of the named constant in this enumerated type.
166func (e *EnumDescriptor) integerValueAsString(name string) string {
167 for _, c := range e.Value {
168 if proto.GetString(c.Name) == name {
169 return fmt.Sprint(proto.GetInt32(c.Number))
170 }
171 }
David Symonds9d0000e2011-02-03 10:48:14 +1100172 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700173 return ""
174}
175
David Symonds4decd802011-08-04 11:27:07 +1000176// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700177// Otherwise it will be the descriptor of the message in which it is defined.
178type ExtensionDescriptor struct {
179 common
180 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700181 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700182}
183
184// TypeName returns the elements of the dotted type name.
185// The package name is not part of this name.
186func (e *ExtensionDescriptor) TypeName() (s []string) {
187 name := proto.GetString(e.Name)
188 if e.parent == nil {
189 // top-level extension
190 s = make([]string, 1)
191 } else {
192 pname := e.parent.TypeName()
193 s = make([]string, len(pname)+1)
194 copy(s, pname)
195 }
196 s[len(s)-1] = name
197 return s
198}
199
David Symondse37856c2011-06-22 12:52:53 +1000200// DescName returns the variable name used for the generated descriptor.
201func (e *ExtensionDescriptor) DescName() string {
202 // The full type name.
203 typeName := e.TypeName()
204 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
205 for i, s := range typeName {
206 typeName[i] = CamelCase(s)
207 }
208 return "E_" + strings.Join(typeName, "_")
209}
210
David Symonds4decd802011-08-04 11:27:07 +1000211// ImportedDescriptor describes a type that has been publicly imported from another file.
212type ImportedDescriptor struct {
213 common
214 o Object
215}
216
217func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() }
218
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700219// FileDescriptor describes an protocol buffer descriptor file (.proto).
220// It includes slices of all the messages and enums defined within it.
221// Those slices are constructed by WrapTypes.
222type FileDescriptor struct {
223 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700224 desc []*Descriptor // All the messages defined in this file.
225 enum []*EnumDescriptor // All the enums defined in this file.
226 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds4decd802011-08-04 11:27:07 +1000227 imp []*ImportedDescriptor // All types defined in files publicly imported by this file.
David Symonds31d58a22011-01-20 18:33:21 +1100228
David Symondsb2a00c82011-08-04 16:52:58 +1000229 // The full list of symbols that are exported,
230 // as a map from the exported object to its symbols.
David Symonds31d58a22011-01-20 18:33:21 +1100231 // This is used for supporting public imports.
David Symondsb2a00c82011-08-04 16:52:58 +1000232 exported map[Object][]Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700233}
234
235// PackageName is the package name we'll use in the generated code to refer to this file.
236func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
237
238// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700239// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700240func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700241 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700242 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700243 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700244 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700245 // Use the file base name.
246 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700247}
248
David Symondsb2a00c82011-08-04 16:52:58 +1000249func (d *FileDescriptor) addExport(obj Object, symbol Symbol) {
250 d.exported[obj] = append(d.exported[obj], symbol)
David Symonds31d58a22011-01-20 18:33:21 +1100251}
252
253// Symbol is an interface representing an exported Go symbol.
254type Symbol interface {
255 // GenerateAlias should generate an appropriate alias
256 // for the symbol from the named package.
257 GenerateAlias(g *Generator, pkg string)
258}
259
260type messageSymbol struct {
261 sym string
262 hasExtensions, isMessageSet bool
263}
264
265func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
266 remoteSym := pkg + "." + ms.sym
267
268 g.P("type ", ms.sym, " ", remoteSym)
269 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
David Symondse37856c2011-06-22 12:52:53 +1000270 g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }")
David Symonds31d58a22011-01-20 18:33:21 +1100271 if ms.hasExtensions {
272 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
273 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
David Symonds1d72f7a2011-08-19 18:28:52 +1000274 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32]", g.ProtoPkg, ".Extension ",
David Symonds31d58a22011-01-20 18:33:21 +1100275 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
276 if ms.isMessageSet {
Rob Pikea17fdd92011-11-02 12:43:05 -0700277 g.P("func (this *", ms.sym, ") Marshal() ([]byte, error) ",
David Symonds31d58a22011-01-20 18:33:21 +1100278 "{ return (*", remoteSym, ")(this).Marshal() }")
Rob Pikea17fdd92011-11-02 12:43:05 -0700279 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) error ",
David Symonds31d58a22011-01-20 18:33:21 +1100280 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
281 }
282 }
283}
284
285type enumSymbol string
286
287func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
288 s := string(es)
289 g.P("type ", s, " ", pkg, ".", s)
290 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
291 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
David Symondsdd9ca042011-08-29 16:07:16 +1000292 g.P("func New", s, "(x ", s, ") *", s, " { e := ", s, "(x); return &e }")
David Symonds31d58a22011-01-20 18:33:21 +1100293}
294
295type constOrVarSymbol struct {
296 sym string
297 typ string // either "const" or "var"
298}
299
300func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
301 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
302}
303
David Symonds4decd802011-08-04 11:27:07 +1000304// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700305type Object interface {
306 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
307 TypeName() []string
David Symonds4decd802011-08-04 11:27:07 +1000308 File() *descriptor.FileDescriptorProto
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700309}
310
311// Each package name we generate must be unique. The package we're generating
David Symonds4decd802011-08-04 11:27:07 +1000312// gets its own name but every other package must have a unique name that does
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700313// not conflict in the code we generate. These names are chosen globally (although
314// they don't have to be, it simplifies things to do them globally).
315func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
316 s, ok := uniquePackageName[fd]
317 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100318 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700319 }
320 return s
321}
322
323// Generator is the type whose methods generate the output, stored in the associated response structure.
324type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000325 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700326
327 Request *plugin.CodeGeneratorRequest // The input.
328 Response *plugin.CodeGeneratorResponse // The output.
329
Rob Pikec9e7d972010-06-10 10:30:22 -0700330 Param map[string]string // Command-line parameters.
331 ImportPrefix string // String to prefix to imported package file names.
332 ImportMap map[string]string // Mapping from import name to generated name
333
334 ProtoPkg string // The name under which we import the library's package proto.
335
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700336 packageName string // What we're calling ourselves.
337 allFiles []*FileDescriptor // All files in the tree
338 genFiles []*FileDescriptor // Those files we will generate output for.
339 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000340 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700341 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
342 indent string
343}
344
345// New creates a new generator and allocates the request and response protobufs.
346func New() *Generator {
347 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000348 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100349 g.Request = new(plugin.CodeGeneratorRequest)
350 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700351 return g
352}
353
Rob Pikea17fdd92011-11-02 12:43:05 -0700354// Error reports a problem, including an error, and exits the program.
355func (g *Generator) Error(err error, msgs ...string) {
356 s := strings.Join(msgs, " ") + ":" + err.Error()
Rob Pike5194c512010-10-14 13:02:16 -0700357 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700358 g.Response.Error = proto.String(s)
359 os.Exit(1)
360}
361
362// Fail reports a problem and exits the program.
363func (g *Generator) Fail(msgs ...string) {
364 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700365 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700366 g.Response.Error = proto.String(s)
367 os.Exit(1)
368}
369
Rob Pikec9e7d972010-06-10 10:30:22 -0700370// CommandLineParameters breaks the comma-separated list of key=value pairs
371// in the parameter (a member of the request protobuf) into a key/value map.
372// It then sets file name mappings defined by those entries.
373func (g *Generator) CommandLineParameters(parameter string) {
374 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000375 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700376 if i := strings.Index(p, "="); i < 0 {
377 g.Param[p] = ""
378 } else {
379 g.Param[p[0:i]] = p[i+1:]
380 }
381 }
382
383 g.ImportMap = make(map[string]string)
384 for k, v := range g.Param {
385 if k == "import_prefix" {
386 g.ImportPrefix = v
387 } else if len(k) > 0 && k[0] == 'M' {
388 g.ImportMap[k[1:]] = v
389 }
390 }
391}
392
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700393// DefaultPackageName returns the package name printed for the object.
394// If its file is in a different package, it returns the package name we're using for this file, plus ".".
395// Otherwise it returns the empty string.
396func (g *Generator) DefaultPackageName(obj Object) string {
397 pkg := obj.PackageName()
398 if pkg == g.packageName {
399 return ""
400 }
401 return pkg + "."
402}
403
404// For each input file, the unique package name to use, underscored.
405var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700406// Package names already registered. Key is the name from the .proto file;
407// value is the name that appears in the generated code.
408var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700409
Rob Pikec9e7d972010-06-10 10:30:22 -0700410// Create and remember a guaranteed unique package name for this file descriptor.
411// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
412// has no file descriptor.
413func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000414 // Convert dots to underscores before finding a unique alias.
415 pkg = strings.Map(DotToUnderscore, pkg)
416
David Symonds79eae332010-10-16 11:33:20 +1100417 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700418 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100419 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700420 }
421 // Install it.
422 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700423 if f != nil {
424 uniquePackageName[f.FileDescriptorProto] = pkg
425 }
426 return pkg
427}
428
429// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700430// The package name must agree across all files being generated.
431// It also defines unique package names for all imported files.
432func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700433 // Register the name for this package. It will be the first name
434 // registered so is guaranteed to be unmodified.
435 pkg := g.genFiles[0].originalPackageName()
436 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
437 // Register the proto package name. It might collide with the
438 // name of a package we import.
439 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700440 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700441 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700442 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700443 if thisPkg != pkg {
444 g.Fail("inconsistent package names:", thisPkg, pkg)
445 }
446 }
447AllFiles:
448 for _, f := range g.allFiles {
449 for _, genf := range g.genFiles {
450 if f == genf {
451 // In this package already.
452 uniquePackageName[f.FileDescriptorProto] = g.packageName
453 continue AllFiles
454 }
455 }
David Symonds7d5c8242011-03-14 12:03:50 -0700456 // The file is a dependency, so we want to ignore its go_package option
457 // because that is only relevant for its specific generated output.
458 pkg := proto.GetString(f.Package)
459 if pkg == "" {
460 pkg = BaseName(*f.Name)
461 }
462 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700463 }
464}
465
466// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
467// and FileDescriptorProtos into file-referenced objects within the Generator.
468// It also creates the list of files to generate and so should be called before GenerateAllFiles.
469func (g *Generator) WrapTypes() {
470 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
471 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700472 // We must wrap the descriptors before we wrap the enums
473 descs := wrapDescriptors(f)
474 g.buildNestedDescriptors(descs)
475 enums := wrapEnumDescriptors(f, descs)
476 exts := wrapExtensions(f)
David Symonds4decd802011-08-04 11:27:07 +1000477 imps := wrapImported(f, g)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700478 g.allFiles[i] = &FileDescriptor{
479 FileDescriptorProto: f,
480 desc: descs,
481 enum: enums,
482 ext: exts,
David Symonds4decd802011-08-04 11:27:07 +1000483 imp: imps,
David Symondsb2a00c82011-08-04 16:52:58 +1000484 exported: make(map[Object][]Symbol),
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700485 }
486 }
487
488 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
489FindFiles:
490 for i, fileName := range g.Request.FileToGenerate {
491 // Search the list. This algorithm is n^2 but n is tiny.
492 for _, file := range g.allFiles {
493 if fileName == proto.GetString(file.Name) {
494 g.genFiles[i] = file
495 continue FindFiles
496 }
497 }
498 g.Fail("could not find file named", fileName)
499 }
500 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
501}
502
503// Scan the descriptors in this file. For each one, build the slice of nested descriptors
504func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
505 for _, desc := range descs {
506 if len(desc.NestedType) != 0 {
507 desc.nested = make([]*Descriptor, len(desc.NestedType))
508 n := 0
509 for _, nest := range descs {
510 if nest.parent == desc {
511 desc.nested[n] = nest
512 n++
513 }
514 }
515 if n != len(desc.NestedType) {
516 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
517 }
518 }
519 }
520}
521
522// Construct the Descriptor and add it to the slice
523func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
David Symonds4decd802011-08-04 11:27:07 +1000524 d := &Descriptor{common{file}, desc, parent, nil, nil, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700525
526 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
527 for i, field := range desc.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000528 d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700529 }
530
David Symondscc7142e2010-11-06 14:37:15 +1100531 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700532}
533
534// Return a slice of all the Descriptors defined within this file
535func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
536 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
537 for _, desc := range file.MessageType {
538 sl = wrapThisDescriptor(sl, desc, nil, file)
539 }
540 return sl
541}
542
543// Wrap this Descriptor, recursively
544func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
545 sl = addDescriptor(sl, desc, parent, file)
546 me := sl[len(sl)-1]
547 for _, nested := range desc.NestedType {
548 sl = wrapThisDescriptor(sl, nested, me, file)
549 }
550 return sl
551}
552
553// Construct the EnumDescriptor and add it to the slice
554func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symonds4decd802011-08-04 11:27:07 +1000555 return append(sl, &EnumDescriptor{common{file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700556}
557
558// Return a slice of all the EnumDescriptors defined within this file
559func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
560 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000561 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700562 for _, enum := range file.EnumType {
563 sl = addEnumDescriptor(sl, enum, nil, file)
564 }
David Symonds5256cf62010-06-27 10:33:42 +1000565 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700566 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000567 for _, enum := range nested.EnumType {
568 sl = addEnumDescriptor(sl, enum, nested, file)
569 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700570 }
571 return sl
572}
573
574// Return a slice of all the top-level ExtensionDescriptors defined within this file.
575func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
576 sl := make([]*ExtensionDescriptor, len(file.Extension))
577 for i, field := range file.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000578 sl[i] = &ExtensionDescriptor{common{file}, field, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700579 }
580 return sl
581}
582
David Symonds4decd802011-08-04 11:27:07 +1000583// Return a slice of all the types that are publicly imported into this file.
584func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
585 for _, index := range file.PublicDependency {
586 df := g.fileByName(file.Dependency[index])
587 for _, d := range df.desc {
588 sl = append(sl, &ImportedDescriptor{common{file}, d})
589 }
590 for _, e := range df.enum {
591 sl = append(sl, &ImportedDescriptor{common{file}, e})
592 }
593 for _, ext := range df.ext {
594 sl = append(sl, &ImportedDescriptor{common{file}, ext})
595 }
596 }
597 return
598}
599
Rob Pikec9e7d972010-06-10 10:30:22 -0700600// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700601// The key names for the map come from the input data, which puts a period at the beginning.
602// It should be called after SetPackageNames and before GenerateAllFiles.
603func (g *Generator) BuildTypeNameMap() {
604 g.typeNameToObject = make(map[string]Object)
605 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700606 // The names in this loop are defined by the proto world, not us, so the
607 // package name may be empty. If so, the dotted package name of X will
608 // be ".X"; otherwise it will be ".pkg.X".
609 dottedPkg := "." + proto.GetString(f.Package)
610 if dottedPkg != "." {
611 dottedPkg += "."
612 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700613 for _, enum := range f.enum {
614 name := dottedPkg + dottedSlice(enum.TypeName())
615 g.typeNameToObject[name] = enum
616 }
617 for _, desc := range f.desc {
618 name := dottedPkg + dottedSlice(desc.TypeName())
619 g.typeNameToObject[name] = desc
620 }
621 }
622}
623
624// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
625// returns the descriptor for the message or enum with that name.
626func (g *Generator) ObjectNamed(typeName string) Object {
David Symonds4decd802011-08-04 11:27:07 +1000627 o, ok := g.typeNameToObject[typeName]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700628 if !ok {
629 g.Fail("can't find object with type", typeName)
630 }
David Symonds4decd802011-08-04 11:27:07 +1000631
632 // If the file of this object isn't a direct dependency of the current file,
633 // or in the current file, then this object has been publicly imported into
634 // a dependency of the current file.
635 // We should return the ImportedDescriptor object for it instead.
636 direct := *o.File().Name == *g.file.Name
637 if !direct {
638 for _, dep := range g.file.Dependency {
639 if *g.fileByName(dep).Name == *o.File().Name {
640 direct = true
641 break
642 }
643 }
644 }
645 if !direct {
646 found := false
647 Loop:
648 for _, dep := range g.file.Dependency {
649 df := g.fileByName(*g.fileByName(dep).Name)
650 for _, td := range df.imp {
651 if td.o == o {
652 // Found it!
653 o = td
654 found = true
655 break Loop
656 }
657 }
658 }
659 if !found {
660 log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
661 }
662 }
663
664 return o
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700665}
666
667// P prints the arguments to the generated output. It handles strings and int32s, plus
668// handling indirections because they may be *string, etc.
669func (g *Generator) P(str ...interface{}) {
670 g.WriteString(g.indent)
671 for _, v := range str {
672 switch s := v.(type) {
673 case string:
674 g.WriteString(s)
675 case *string:
676 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700677 case bool:
678 g.WriteString(fmt.Sprintf("%t", s))
679 case *bool:
680 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700681 case *int32:
682 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700683 case float64:
684 g.WriteString(fmt.Sprintf("%g", s))
685 case *float64:
686 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700687 default:
688 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
689 }
690 }
691 g.WriteByte('\n')
692}
693
694// In Indents the output one tab stop.
695func (g *Generator) In() { g.indent += "\t" }
696
697// Out unindents the output one tab stop.
698func (g *Generator) Out() {
699 if len(g.indent) > 0 {
700 g.indent = g.indent[1:]
701 }
702}
703
704// GenerateAllFiles generates the output for all the files we're outputting.
705func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700706 // Initialize the plugins
707 for _, p := range plugins {
708 p.Init(g)
709 }
David Symonds31d58a22011-01-20 18:33:21 +1100710 // Generate the output. The generator runs for every file, even the files
711 // that we don't generate output for, so that we can collate the full list
712 // of exported symbols to support public imports.
713 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
714 for _, file := range g.genFiles {
715 genFileMap[file] = true
716 }
717 i := 0
718 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700719 g.Reset()
720 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100721 if _, ok := genFileMap[file]; !ok {
722 continue
723 }
David Symondsb0127532010-11-09 11:10:46 +1100724 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700725 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
726 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100727 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700728 }
729}
730
731// Run all the plugins associated with the file.
732func (g *Generator) runPlugins(file *FileDescriptor) {
733 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700734 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700735 }
736}
737
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700738// FileOf return the FileDescriptor for this FileDescriptorProto.
739func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
740 for _, file := range g.allFiles {
741 if file.FileDescriptorProto == fd {
742 return file
743 }
744 }
745 g.Fail("could not find file in table:", proto.GetString(fd.Name))
746 return nil
747}
748
749// Fill the response protocol buffer with the generated output for all the files we're
750// supposed to generate.
751func (g *Generator) generate(file *FileDescriptor) {
752 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000753 g.usedPackages = make(map[string]bool)
754
David Symonds4decd802011-08-04 11:27:07 +1000755 for _, td := range g.file.imp {
756 g.generateImported(td)
757 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700758 for _, enum := range g.file.enum {
759 g.generateEnum(enum)
760 }
761 for _, desc := range g.file.desc {
762 g.generateMessage(desc)
763 }
764 for _, ext := range g.file.ext {
765 g.generateExtension(ext)
766 }
767 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000768
Rob Pikec9e7d972010-06-10 10:30:22 -0700769 // Run the plugins before the imports so we know which imports are necessary.
770 g.runPlugins(file)
771
David Symondsf90e3382010-05-05 10:53:44 +1000772 // Generate header and imports last, though they appear first in the output.
773 rem := g.Buffer
774 g.Buffer = new(bytes.Buffer)
775 g.generateHeader()
776 g.generateImports()
777 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000778
779 // Reformat generated code.
780 fset := token.NewFileSet()
781 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
782 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -0700783 g.Fail("bad Go source code was generated:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +1000784 return
785 }
786 g.Reset()
David Symonds2ed81c42011-08-12 17:06:51 +1000787 _, err = (&printer.Config{printer.TabIndent | printer.UseSpaces, 8}).Fprint(g, fset, ast)
David Symondsb1d55a02011-04-08 09:55:06 +1000788 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -0700789 g.Fail("generated Go source code could not be reformatted:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +1000790 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700791}
792
793// Generate the header, including package definition and imports
794func (g *Generator) generateHeader() {
795 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
796 g.P("// DO NOT EDIT!")
797 g.P()
798 g.P("package ", g.file.PackageName())
799 g.P()
800}
801
David Symonds31d58a22011-01-20 18:33:21 +1100802func (g *Generator) fileByName(filename string) *FileDescriptor {
803 for _, fd := range g.allFiles {
804 if proto.GetString(fd.Name) == filename {
805 return fd
806 }
807 }
808 return nil
809}
810
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700811// Generate the header, including package definition and imports
812func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700813 // We almost always need a proto import. Rather than computing when we
814 // do, which is tricky when there's a plugin, just import it and
Rob Pikea17fdd92011-11-02 12:43:05 -0700815 // reference it later. The same argument applies to the math package,
816 // for handling bit patterns for floating-point numbers.
Rob Pike809831a2010-06-16 10:10:58 -0700817 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100818 g.P(`import "math"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700819 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100820 fd := g.fileByName(s)
821 // Do not import our own package.
822 if fd.PackageName() == g.packageName {
823 continue
824 }
825 filename := goFileName(s)
826 if substitution, ok := g.ImportMap[s]; ok {
827 filename = substitution
828 }
829 filename = g.ImportPrefix + filename
830 if strings.HasSuffix(filename, ".go") {
831 filename = filename[0 : len(filename)-3]
832 }
833 if _, ok := g.usedPackages[fd.PackageName()]; ok {
834 g.P("import ", fd.PackageName(), " ", Quote(filename))
835 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700836 // TODO: Re-enable this when we are more feature-complete.
837 // For instance, some protos use foreign field extensions, which we don't support.
838 // Until then, this is just annoying spam.
839 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
David Symonds4decd802011-08-04 11:27:07 +1000840 g.P("// discarding unused import ", fd.PackageName(), " ", Quote(filename))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700841 }
842 }
843 g.P()
844 // TODO: may need to worry about uniqueness across plugins
845 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700846 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700847 g.P()
848 }
Rob Pikea17fdd92011-11-02 12:43:05 -0700849 g.P("// Reference proto & math imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700850 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100851 g.P("var _ = math.Inf")
Rob Pikec9e7d972010-06-10 10:30:22 -0700852 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700853}
854
David Symonds4decd802011-08-04 11:27:07 +1000855func (g *Generator) generateImported(id *ImportedDescriptor) {
David Symondsb2a00c82011-08-04 16:52:58 +1000856 // Don't generate public import symbols for files that we are generating
857 // code for, since those symbols will already be in this package.
858 // We can't simply avoid creating the ImportedDescriptor objects,
859 // because g.genFiles isn't populated at that stage.
David Symonds4decd802011-08-04 11:27:07 +1000860 tn := id.TypeName()
861 sn := tn[len(tn)-1]
David Symondsb2a00c82011-08-04 16:52:58 +1000862 df := g.FileOf(id.o.File())
863 filename := *df.Name
864 for _, fd := range g.genFiles {
865 if *fd.Name == filename {
866 g.P("// Ignoring public import of ", sn, " from ", filename)
867 g.P()
868 return
869 }
870 }
871 g.P("// ", sn, " from public import ", filename)
872 g.usedPackages[df.PackageName()] = true
David Symonds4decd802011-08-04 11:27:07 +1000873
David Symondsb2a00c82011-08-04 16:52:58 +1000874 for _, sym := range df.exported[id.o] {
875 sym.GenerateAlias(g, df.PackageName())
876 }
877
878 g.P()
David Symonds4decd802011-08-04 11:27:07 +1000879}
880
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700881// Generate the enum definitions for this EnumDescriptor.
882func (g *Generator) generateEnum(enum *EnumDescriptor) {
883 // The full type name
884 typeName := enum.TypeName()
885 // The full type name, CamelCased.
886 ccTypeName := CamelCaseSlice(typeName)
887 ccPrefix := enum.prefix()
888 g.P("type ", ccTypeName, " int32")
David Symondsb2a00c82011-08-04 16:52:58 +1000889 g.file.addExport(enum, enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700890 g.P("const (")
891 g.In()
892 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100893 name := ccPrefix + *e.Name
David Symondsdd9ca042011-08-29 16:07:16 +1000894 g.P(name, " ", ccTypeName, " = ", e.Number)
David Symondsb2a00c82011-08-04 16:52:58 +1000895 g.file.addExport(enum, constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700896 }
897 g.Out()
898 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100899 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700900 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700901 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700902 for _, e := range enum.Value {
903 duplicate := ""
904 if _, present := generated[*e.Number]; present {
905 duplicate = "// Duplicate value: "
906 }
907 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
908 generated[*e.Number] = true
909 }
910 g.Out()
911 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100912 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700913 g.In()
914 for _, e := range enum.Value {
915 g.P(Quote(*e.Name), ": ", e.Number, ",")
916 }
917 g.Out()
918 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100919
David Symondsdd9ca042011-08-29 16:07:16 +1000920 g.P("func New", ccTypeName, "(x ", ccTypeName, ") *", ccTypeName, " {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700921 g.In()
922 g.P("e := ", ccTypeName, "(x)")
923 g.P("return &e")
924 g.Out()
925 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100926
927 g.P("func (x ", ccTypeName, ") String() string {")
928 g.In()
929 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
930 g.Out()
931 g.P("}")
932
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700933 g.P()
934}
935
David Symonds8935abf2011-07-04 15:53:16 +1000936// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700937// identifies details of the field for the protocol buffer marshaling and unmarshaling
938// code. The fields are:
939// wire encoding
940// protocol tag number
941// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100942// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700943// name= the original declared name
944// enum= the name of the enum type if it is an enum-typed field.
945// def= string representation of the default value, if any.
946// The default value must be in a representation that can be used at run-time
947// to generate the default value. Thus bools become 0 and 1, for instance.
948func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
949 optrepreq := ""
950 switch {
951 case isOptional(field):
952 optrepreq = "opt"
953 case isRequired(field):
954 optrepreq = "req"
955 case isRepeated(field):
956 optrepreq = "rep"
957 }
958 defaultValue := proto.GetString(field.DefaultValue)
959 if defaultValue != "" {
960 switch *field.Type {
961 case descriptor.FieldDescriptorProto_TYPE_BOOL:
962 if defaultValue == "true" {
963 defaultValue = "1"
964 } else {
965 defaultValue = "0"
966 }
967 case descriptor.FieldDescriptorProto_TYPE_STRING,
968 descriptor.FieldDescriptorProto_TYPE_BYTES:
David Symondsb79d99b2011-08-29 16:38:49 +1000969 // Nothing to do. Quoting is done for the whole tag.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700970 case descriptor.FieldDescriptorProto_TYPE_ENUM:
971 // For enums we need to provide the integer constant.
972 obj := g.ObjectNamed(proto.GetString(field.TypeName))
973 enum, ok := obj.(*EnumDescriptor)
974 if !ok {
975 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
976 }
977 defaultValue = enum.integerValueAsString(defaultValue)
978 }
979 defaultValue = ",def=" + defaultValue
980 }
981 enum := ""
982 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
David Symonds4decd802011-08-04 11:27:07 +1000983 // We avoid using obj.PackageName(), because we want to use the
984 // original (proto-world) package name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700985 obj := g.ObjectNamed(proto.GetString(field.TypeName))
David Symonds4decd802011-08-04 11:27:07 +1000986 enum = ",enum="
987 if pkg := proto.GetString(obj.File().Package); pkg != "" {
988 enum += pkg + "."
989 }
990 enum += CamelCaseSlice(obj.TypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700991 }
David Symonds5b7775e2010-12-01 10:09:04 +1100992 packed := ""
993 if field.Options != nil && proto.GetBool(field.Options.Packed) {
994 packed = ",packed"
995 }
David Symondse37856c2011-06-22 12:52:53 +1000996 fieldName := proto.GetString(field.Name)
997 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +1000998 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
999 // We must use the type name for groups instead of
1000 // the field name to preserve capitalization.
1001 // type_name in FieldDescriptorProto is fully-qualified,
1002 // but we only want the local part.
1003 name = *field.TypeName
1004 if i := strings.LastIndex(name, "."); i >= 0 {
1005 name = name[i+1:]
1006 }
David Symondse37856c2011-06-22 12:52:53 +10001007 }
1008 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001009 name = ""
1010 } else {
1011 name = ",name=" + name
1012 }
David Symonds8935abf2011-07-04 15:53:16 +10001013 return Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001014 wiretype,
1015 proto.GetInt32(field.Number),
1016 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +11001017 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001018 name,
1019 enum,
1020 defaultValue))
1021}
1022
1023func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
1024 switch typ {
1025 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1026 return false
1027 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1028 return false
1029 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1030 return false
1031 }
1032 return true
1033}
1034
1035// TypeName is the printed name appropriate for an item. If the object is in the current file,
1036// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -07001037// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001038// package name followed by the item name.
1039// The result always has an initial capital.
1040func (g *Generator) TypeName(obj Object) string {
1041 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
1042}
1043
1044// TypeNameWithPackage is like TypeName, but always includes the package
1045// name even if the object is in our own package.
1046func (g *Generator) TypeNameWithPackage(obj Object) string {
1047 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
1048}
1049
1050// GoType returns a string representing the type name, and the wire type
1051func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
1052 // TODO: Options.
1053 switch *field.Type {
1054 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
1055 typ, wire = "float64", "fixed64"
1056 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
1057 typ, wire = "float32", "fixed32"
1058 case descriptor.FieldDescriptorProto_TYPE_INT64:
1059 typ, wire = "int64", "varint"
1060 case descriptor.FieldDescriptorProto_TYPE_UINT64:
1061 typ, wire = "uint64", "varint"
1062 case descriptor.FieldDescriptorProto_TYPE_INT32:
1063 typ, wire = "int32", "varint"
1064 case descriptor.FieldDescriptorProto_TYPE_UINT32:
1065 typ, wire = "uint32", "varint"
1066 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
1067 typ, wire = "uint64", "fixed64"
1068 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
1069 typ, wire = "uint32", "fixed32"
1070 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1071 typ, wire = "bool", "varint"
1072 case descriptor.FieldDescriptorProto_TYPE_STRING:
1073 typ, wire = "string", "bytes"
1074 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1075 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1076 typ, wire = "*"+g.TypeName(desc), "group"
1077 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1078 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1079 typ, wire = "*"+g.TypeName(desc), "bytes"
1080 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1081 typ, wire = "[]byte", "bytes"
1082 case descriptor.FieldDescriptorProto_TYPE_ENUM:
1083 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1084 typ, wire = g.TypeName(desc), "varint"
1085 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
1086 typ, wire = "int32", "fixed32"
1087 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1088 typ, wire = "int64", "fixed64"
1089 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1090 typ, wire = "int32", "zigzag32"
1091 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1092 typ, wire = "int64", "zigzag64"
1093 default:
1094 g.Fail("unknown type for", proto.GetString(field.Name))
1095 }
1096 if isRepeated(field) {
1097 typ = "[]" + typ
1098 } else if needsStar(*field.Type) {
1099 typ = "*" + typ
1100 }
1101 return
1102}
1103
David Symondsf90e3382010-05-05 10:53:44 +10001104func (g *Generator) RecordTypeUse(t string) {
1105 if obj, ok := g.typeNameToObject[t]; ok {
David Symonds4decd802011-08-04 11:27:07 +10001106 // Call ObjectNamed to get the true object to record the use.
1107 obj = g.ObjectNamed(t)
David Symondsf90e3382010-05-05 10:53:44 +10001108 g.usedPackages[obj.PackageName()] = true
1109 }
1110}
1111
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001112// Generate the type and default constant definitions for this Descriptor.
1113func (g *Generator) generateMessage(message *Descriptor) {
1114 // The full type name
1115 typeName := message.TypeName()
1116 // The full type name, CamelCased.
1117 ccTypeName := CamelCaseSlice(typeName)
1118
1119 g.P("type ", ccTypeName, " struct {")
1120 g.In()
1121 for _, field := range message.Field {
1122 fieldname := CamelCase(*field.Name)
1123 typename, wiretype := g.GoType(message, field)
David Symondsb2a00c82011-08-04 16:52:58 +10001124 jsonName := *field.Name
David Symonds002ec402011-09-29 17:24:20 -07001125 tag := fmt.Sprintf("`protobuf:%s json:%q`", g.goTag(field, wiretype), jsonName+",omitempty")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001126 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001127 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001128 }
David Symonds002ec402011-09-29 17:24:20 -07001129 // TODO: Use `json:"-"` for these XXX_ fields when that makes it into a Go release.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001130 if len(message.ExtensionRange) > 0 {
David Symonds002ec402011-09-29 17:24:20 -07001131 g.P("XXX_extensions\t\tmap[int32]", g.ProtoPkg, ".Extension `json:\",omitempty\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001132 }
David Symonds002ec402011-09-29 17:24:20 -07001133 g.P("XXX_unrecognized\t[]byte `json:\",omitempty\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001134 g.Out()
1135 g.P("}")
1136
David Symondse37856c2011-06-22 12:52:53 +10001137 // Reset and String functions
1138 g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }")
1139 g.P("func (this *", ccTypeName, ") String() string { return ", g.ProtoPkg, ".CompactTextString(this) }")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001140
1141 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001142 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001143 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001144 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001145 // message_set_wire_format only makes sense when extensions are defined.
1146 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001147 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001148 g.P()
Rob Pikea17fdd92011-11-02 12:43:05 -07001149 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, error) {")
David Symonds4fee3b12010-11-11 10:00:13 +11001150 g.In()
1151 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1152 g.Out()
1153 g.P("}")
Rob Pikea17fdd92011-11-02 12:43:05 -07001154 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) error {")
David Symonds4fee3b12010-11-11 10:00:13 +11001155 g.In()
1156 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1157 g.Out()
1158 g.P("}")
1159 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1160 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1161 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1162 }
1163
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001164 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001165 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001166 g.In()
1167 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001168 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
David Symonds1d72f7a2011-08-19 18:28:52 +10001169 g.P("{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001170 }
1171 g.Out()
1172 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001173 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001174 g.In()
1175 g.P("return extRange_", ccTypeName)
1176 g.Out()
1177 g.P("}")
David Symonds1d72f7a2011-08-19 18:28:52 +10001178 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32]", g.ProtoPkg, ".Extension {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001179 g.In()
1180 g.P("if this.XXX_extensions == nil {")
1181 g.In()
David Symonds1d72f7a2011-08-19 18:28:52 +10001182 g.P("this.XXX_extensions = make(map[int32]", g.ProtoPkg, ".Extension)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001183 g.Out()
1184 g.P("}")
1185 g.P("return this.XXX_extensions")
1186 g.Out()
1187 g.P("}")
1188 }
1189
David Symondsb2a00c82011-08-04 16:52:58 +10001190 g.file.addExport(message, messageSymbol{ccTypeName, hasExtensions, isMessageSet})
David Symonds31d58a22011-01-20 18:33:21 +11001191
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001192 // Default constants
1193 for _, field := range message.Field {
1194 def := proto.GetString(field.DefaultValue)
1195 if def == "" {
1196 continue
1197 }
1198 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1199 typename, _ := g.GoType(message, field)
1200 if typename[0] == '*' {
1201 typename = typename[1:]
1202 }
1203 kind := "const "
1204 switch {
1205 case typename == "bool":
1206 case typename == "string":
1207 def = Quote(def)
1208 case typename == "[]byte":
1209 def = "[]byte(" + Quote(def) + ")"
1210 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001211 case def == "inf", def == "-inf", def == "nan":
1212 // These names are known to, and defined by, the protocol language.
1213 switch def {
1214 case "inf":
1215 def = "math.Inf(1)"
1216 case "-inf":
1217 def = "math.Inf(-1)"
1218 case "nan":
1219 def = "math.NaN()"
1220 }
1221 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1222 def = "float32(" + def + ")"
1223 }
1224 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001225 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1226 // Must be an enum. Need to construct the prefixed name.
1227 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1228 enum, ok := obj.(*EnumDescriptor)
1229 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001230 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001231 continue
1232 }
Rob Pike87af39e2010-07-19 10:48:02 -07001233 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001234 }
1235 g.P(kind, fieldname, " ", typename, " = ", def)
David Symondsb2a00c82011-08-04 16:52:58 +10001236 g.file.addExport(message, constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001237 }
1238 g.P()
1239
1240 for _, ext := range message.ext {
1241 g.generateExtension(ext)
1242 }
1243}
1244
1245func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001246 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001247
1248 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1249 field := ext.FieldDescriptorProto
1250 fieldType, wireType := g.GoType(ext.parent, field)
1251 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001252 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001253 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1254 // foreign extension type
1255 g.RecordTypeUse(*n)
1256 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001257
Rob Pikec9e7d972010-06-10 10:30:22 -07001258 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001259 g.In()
1260 g.P("ExtendedType: (", extendedType, ")(nil),")
1261 g.P("ExtensionType: (", fieldType, ")(nil),")
1262 g.P("Field: ", field.Number, ",")
David Symonds1d72f7a2011-08-19 18:28:52 +10001263 g.P(`Name: "`, g.packageName, ".", strings.Join(ext.TypeName(), "."), `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001264 g.P("Tag: ", tag, ",")
1265
1266 g.Out()
1267 g.P("}")
1268 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001269
David Symondsb2a00c82011-08-04 16:52:58 +10001270 g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001271}
1272
1273func (g *Generator) generateInitFunction() {
1274 g.P("func init() {")
1275 g.In()
1276 for _, enum := range g.file.enum {
1277 g.generateEnumRegistration(enum)
1278 }
David Symondse37856c2011-06-22 12:52:53 +10001279 for _, d := range g.file.desc {
1280 for _, ext := range d.ext {
1281 g.generateExtensionRegistration(ext)
1282 }
1283 }
1284 for _, ext := range g.file.ext {
1285 g.generateExtensionRegistration(ext)
1286 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001287 g.Out()
1288 g.P("}")
1289}
1290
1291func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
David Symonds4decd802011-08-04 11:27:07 +10001292 // // We always print the full (proto-world) package name here.
1293 pkg := proto.GetString(enum.File().Package)
1294 if pkg != "" {
1295 pkg += "."
1296 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001297 // The full type name
1298 typeName := enum.TypeName()
1299 // The full type name, CamelCased.
1300 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001301 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001302}
1303
David Symondse37856c2011-06-22 12:52:53 +10001304func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
1305 g.P(g.ProtoPkg+".RegisterExtension(", ext.DescName(), ")")
1306}
1307
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001308// And now lots of helper functions.
1309
Rob Pike2c7bafc2010-06-10 16:07:14 -07001310// Is c an ASCII lower-case letter?
1311func isASCIILower(c byte) bool {
1312 return 'a' <= c && c <= 'z'
1313}
1314
1315// Is c an ASCII digit?
1316func isASCIIDigit(c byte) bool {
1317 return '0' <= c && c <= '9'
1318}
1319
1320// CamelCase returns the CamelCased name.
1321// If there is an interior underscore followed by a lower case letter,
1322// drop the underscore and convert the letter to upper case.
1323// There is a remote possibility of this rewrite causing a name collision,
1324// but it's so remote we're prepared to pretend it's nonexistent - since the
1325// C++ generator lowercases names, it's extremely unlikely to have two fields
1326// with different capitalizations.
1327// In short, _my_field_name_2 becomes XMyFieldName2.
1328func CamelCase(s string) string {
1329 if s == "" {
1330 return ""
1331 }
1332 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001333 i := 0
1334 if s[0] == '_' {
1335 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001336 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001337 i++
1338 }
1339 // Invariant: if the next letter is lower case, it must be converted
1340 // to upper case.
1341 // That is, we process a word at a time, where words are marked by _ or
1342 // upper case letter. Digits are treated as words.
1343 for ; i < len(s); i++ {
1344 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001345 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1346 continue // Skip the underscore in s.
1347 }
1348 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001349 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001350 continue
1351 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001352 // Assume we have a letter now - if not, it's a bogus identifier.
1353 // The next word is a sequence of characters that must start upper case.
1354 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001355 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001356 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001357 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001358 // Accept lower case sequence that follows.
1359 for i+1 < len(s) && isASCIILower(s[i+1]) {
1360 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001361 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001362 }
1363 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001364 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001365}
1366
1367// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1368// be joined with "_".
1369func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1370
1371// dottedSlice turns a sliced name into a dotted name.
1372func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1373
1374// Quote returns a Go-source quoted string representation of s.
1375func Quote(s string) string { return fmt.Sprintf("%q", s) }
1376
1377// Given a .proto file name, return the output name for the generated Go program.
1378func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001379 ext := path.Ext(name)
1380 if ext == ".proto" || ext == ".protodevel" {
1381 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001382 }
1383 return name + ".pb.go"
1384}
1385
1386// Is this field optional?
1387func isOptional(field *descriptor.FieldDescriptorProto) bool {
1388 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1389}
1390
1391// Is this field required?
1392func isRequired(field *descriptor.FieldDescriptorProto) bool {
1393 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1394}
1395
1396// Is this field repeated?
1397func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1398 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1399}
1400
1401// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001402// which can be dotted in the input .proto file. It maps dots to underscores.
1403// Because we also get here from package names generated from file names, it also maps
1404// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001405func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001406 switch rune {
1407 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001408 return '_'
1409 }
1410 return rune
1411}
Rob Pikec9e7d972010-06-10 10:30:22 -07001412
1413// BaseName returns the last path element of the name, with the last dotted suffix removed.
1414func BaseName(name string) string {
1415 // First, find the last element
1416 if i := strings.LastIndex(name, "/"); i >= 0 {
1417 name = name[i+1:]
1418 }
1419 // Now drop the suffix
1420 if i := strings.LastIndex(name, "."); i >= 0 {
1421 name = name[0:i]
1422 }
1423 return name
1424}