blob: 413f3614eb82d4a56845f502f713dc4728e443fa [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
David Symondsee6e9c52012-11-29 08:51:07 +11003// Copyright 2010 The Go Authors. All rights reserved.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07004// 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"
David Symonds162d0032012-06-28 09:44:46 -070050 "unicode"
51 "unicode/utf8"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070052
Rob Pike3f6f2d82011-12-18 13:55:35 -080053 "code.google.com/p/goprotobuf/proto"
Rob Pikeb7907bf2012-02-13 14:25:20 +110054 descriptor "code.google.com/p/goprotobuf/protoc-gen-go/descriptor"
55 plugin "code.google.com/p/goprotobuf/protoc-gen-go/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070056)
57
58// A Plugin provides functionality to add to the output during Go code generation,
59// such as to produce RPC stubs.
60type Plugin interface {
61 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070062 Name() string
63 // Init is called once after data structures are built but before
64 // code generation begins.
65 Init(g *Generator)
66 // Generate produces the code generated by the plugin for this file,
67 // except for the imports, by calling the generator's methods P, In, and Out.
68 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070069 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070070 // It is called after Generate.
71 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070072}
73
74var plugins []Plugin
75
76// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
77// It is typically called during initialization.
78func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110079 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070080}
81
82// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
83// a pointer to the FileDescriptorProto that represents it. These types achieve that
84// wrapping by placing each Proto inside a struct with the pointer to its File. The
85// structs have the same names as their contents, with "Proto" removed.
86// FileDescriptor is used to store the things that it points to.
87
88// The file and package name method are common to messages and enums.
89type common struct {
David Symonds4decd802011-08-04 11:27:07 +100090 file *descriptor.FileDescriptorProto // File this object comes from.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070091}
92
93// PackageName is name in the package clause in the generated file.
David Symonds4decd802011-08-04 11:27:07 +100094func (c *common) PackageName() string { return uniquePackageOf(c.file) }
95
96func (c *common) File() *descriptor.FileDescriptorProto { return c.file }
Rob Pikeaf82b4e2010-04-30 15:19:25 -070097
98// Descriptor represents a protocol buffer message.
99type Descriptor struct {
100 common
101 *descriptor.DescriptorProto
102 parent *Descriptor // The containing message, if any.
103 nested []*Descriptor // Inner messages, if any.
104 ext []*ExtensionDescriptor // Extensions, if any.
105 typename []string // Cached typename vector.
David Symonds69cffb22013-02-21 14:26:53 +1100106 index int // The index into the container, whether the file or another message.
David Symonds52925902013-07-26 19:20:20 +1000107 path string // The SourceCodeInfo path as comma-separated integers.
David Symonds6eaeef12012-11-07 11:42:56 +1100108 group bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700109}
110
111// TypeName returns the elements of the dotted type name.
112// The package name is not part of this name.
113func (d *Descriptor) TypeName() []string {
114 if d.typename != nil {
115 return d.typename
116 }
117 n := 0
118 for parent := d; parent != nil; parent = parent.parent {
119 n++
120 }
121 s := make([]string, n, n)
122 for parent := d; parent != nil; parent = parent.parent {
123 n--
David Symonds8bb32ca2012-06-28 10:22:09 -0700124 s[n] = parent.GetName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700125 }
126 d.typename = s
127 return s
128}
129
130// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
131// Otherwise it will be the descriptor of the message in which it is defined.
132type EnumDescriptor struct {
133 common
134 *descriptor.EnumDescriptorProto
135 parent *Descriptor // The containing message, if any.
136 typename []string // Cached typename vector.
David Symonds52925902013-07-26 19:20:20 +1000137 path string // The SourceCodeInfo path as comma-separated integers.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700138}
139
140// TypeName returns the elements of the dotted type name.
141// The package name is not part of this name.
142func (e *EnumDescriptor) TypeName() (s []string) {
143 if e.typename != nil {
144 return e.typename
145 }
David Symonds8bb32ca2012-06-28 10:22:09 -0700146 name := e.GetName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700147 if e.parent == nil {
148 s = make([]string, 1)
149 } else {
150 pname := e.parent.TypeName()
151 s = make([]string, len(pname)+1)
152 copy(s, pname)
153 }
154 s[len(s)-1] = name
155 e.typename = s
156 return s
157}
158
159// Everything but the last element of the full type name, CamelCased.
160// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
161func (e *EnumDescriptor) prefix() string {
162 typeName := e.TypeName()
163 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
164 if e.parent == nil {
165 // If the enum is not part of a message, the prefix is just the type name.
166 ccPrefix = CamelCase(*e.Name) + "_"
167 }
168 return ccPrefix
169}
170
171// The integer value of the named constant in this enumerated type.
172func (e *EnumDescriptor) integerValueAsString(name string) string {
173 for _, c := range e.Value {
David Symonds8bb32ca2012-06-28 10:22:09 -0700174 if c.GetName() == name {
175 return fmt.Sprint(c.GetNumber())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700176 }
177 }
David Symonds9d0000e2011-02-03 10:48:14 +1100178 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700179 return ""
180}
181
David Symonds4decd802011-08-04 11:27:07 +1000182// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700183// Otherwise it will be the descriptor of the message in which it is defined.
184type ExtensionDescriptor struct {
185 common
186 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700187 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700188}
189
190// TypeName returns the elements of the dotted type name.
191// The package name is not part of this name.
192func (e *ExtensionDescriptor) TypeName() (s []string) {
David Symonds8bb32ca2012-06-28 10:22:09 -0700193 name := e.GetName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700194 if e.parent == nil {
195 // top-level extension
196 s = make([]string, 1)
197 } else {
198 pname := e.parent.TypeName()
199 s = make([]string, len(pname)+1)
200 copy(s, pname)
201 }
202 s[len(s)-1] = name
203 return s
204}
205
David Symondse37856c2011-06-22 12:52:53 +1000206// DescName returns the variable name used for the generated descriptor.
207func (e *ExtensionDescriptor) DescName() string {
208 // The full type name.
209 typeName := e.TypeName()
210 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
211 for i, s := range typeName {
212 typeName[i] = CamelCase(s)
213 }
214 return "E_" + strings.Join(typeName, "_")
215}
216
David Symonds4decd802011-08-04 11:27:07 +1000217// ImportedDescriptor describes a type that has been publicly imported from another file.
218type ImportedDescriptor struct {
219 common
220 o Object
221}
222
223func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() }
224
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700225// FileDescriptor describes an protocol buffer descriptor file (.proto).
226// It includes slices of all the messages and enums defined within it.
227// Those slices are constructed by WrapTypes.
228type FileDescriptor struct {
229 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700230 desc []*Descriptor // All the messages defined in this file.
231 enum []*EnumDescriptor // All the enums defined in this file.
232 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds4decd802011-08-04 11:27:07 +1000233 imp []*ImportedDescriptor // All types defined in files publicly imported by this file.
David Symonds31d58a22011-01-20 18:33:21 +1100234
David Symonds52925902013-07-26 19:20:20 +1000235 // Comments, stored as a map of path (comma-separated integers) to the comment.
236 comments map[string]*descriptor.SourceCodeInfo_Location
237
David Symondsb2a00c82011-08-04 16:52:58 +1000238 // The full list of symbols that are exported,
239 // as a map from the exported object to its symbols.
David Symonds31d58a22011-01-20 18:33:21 +1100240 // This is used for supporting public imports.
David Symonds151cce02012-12-06 13:34:42 +1100241 exported map[Object][]symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700242}
243
244// PackageName is the package name we'll use in the generated code to refer to this file.
245func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
246
David Symonds162d0032012-06-28 09:44:46 -0700247// goPackageName returns the Go package name to use in the
248// generated Go file. The result explicit reports whether the name
249// came from an option go_package statement. If explicit is false,
250// the name was derived from the protocol buffer's package statement
251// or the input file name.
252func (d *FileDescriptor) goPackageName() (name string, explicit bool) {
David Symonds52925902013-07-26 19:20:20 +1000253 // Does the file have a "go_package" option?
254 if opts := d.Options; opts != nil {
255 if pkg := opts.GetGoPackage(); pkg != "" {
256 return pkg, true
257 }
258 }
David Symonds162d0032012-06-28 09:44:46 -0700259
Rob Pikec9e7d972010-06-10 10:30:22 -0700260 // Does the file have a package clause?
David Symonds8bb32ca2012-06-28 10:22:09 -0700261 if pkg := d.GetPackage(); pkg != "" {
David Symonds162d0032012-06-28 09:44:46 -0700262 return pkg, false
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700263 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700264 // Use the file base name.
David Symonds151cce02012-12-06 13:34:42 +1100265 return baseName(d.GetName()), false
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700266}
267
David Symonds151cce02012-12-06 13:34:42 +1100268func (d *FileDescriptor) addExport(obj Object, sym symbol) {
269 d.exported[obj] = append(d.exported[obj], sym)
David Symonds31d58a22011-01-20 18:33:21 +1100270}
271
David Symonds151cce02012-12-06 13:34:42 +1100272// symbol is an interface representing an exported Go symbol.
273type symbol interface {
David Symonds31d58a22011-01-20 18:33:21 +1100274 // GenerateAlias should generate an appropriate alias
275 // for the symbol from the named package.
276 GenerateAlias(g *Generator, pkg string)
277}
278
279type messageSymbol struct {
280 sym string
281 hasExtensions, isMessageSet bool
David Symonds381349d2012-09-18 15:11:46 +1000282 getters []getterSymbol
David Symonds31d58a22011-01-20 18:33:21 +1100283}
284
David Symonds381349d2012-09-18 15:11:46 +1000285type getterSymbol struct {
286 name string
287 typ string
288 typeName string // canonical name in proto world; empty for proto.Message and similar
289 genType bool // whether typ is a generated type (message/group/enum)
290}
291
292func (ms *messageSymbol) GenerateAlias(g *Generator, pkg string) {
David Symonds31d58a22011-01-20 18:33:21 +1100293 remoteSym := pkg + "." + ms.sym
294
295 g.P("type ", ms.sym, " ", remoteSym)
David Symonds22e7eb42013-03-23 12:17:58 +1100296 g.P("func (m *", ms.sym, ") Reset() { (*", remoteSym, ")(m).Reset() }")
297 g.P("func (m *", ms.sym, ") String() string { return (*", remoteSym, ")(m).String() }")
David Symonds9f60f432012-06-14 09:45:25 +1000298 g.P("func (*", ms.sym, ") ProtoMessage() {}")
David Symonds31d58a22011-01-20 18:33:21 +1100299 if ms.hasExtensions {
David Symonds11db5982012-08-15 10:54:05 +1000300 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.Pkg["proto"], ".ExtensionRange ",
David Symonds31d58a22011-01-20 18:33:21 +1100301 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
David Symonds22e7eb42013-03-23 12:17:58 +1100302 g.P("func (m *", ms.sym, ") ExtensionMap() map[int32]", g.Pkg["proto"], ".Extension ",
303 "{ return (*", remoteSym, ")(m).ExtensionMap() }")
David Symonds31d58a22011-01-20 18:33:21 +1100304 if ms.isMessageSet {
David Symonds22e7eb42013-03-23 12:17:58 +1100305 g.P("func (m *", ms.sym, ") Marshal() ([]byte, error) ",
306 "{ return (*", remoteSym, ")(m).Marshal() }")
307 g.P("func (m *", ms.sym, ") Unmarshal(buf []byte) error ",
308 "{ return (*", remoteSym, ")(m).Unmarshal(buf) }")
David Symonds31d58a22011-01-20 18:33:21 +1100309 }
310 }
David Symonds381349d2012-09-18 15:11:46 +1000311 for _, get := range ms.getters {
David Symondsdb7a6872013-01-30 17:08:05 +1100312 if get.typeName != "" {
313 g.RecordTypeUse(get.typeName)
314 }
David Symonds381349d2012-09-18 15:11:46 +1000315 typ := get.typ
David Symonds22e7eb42013-03-23 12:17:58 +1100316 val := "(*" + remoteSym + ")(m)." + get.name + "()"
David Symonds381349d2012-09-18 15:11:46 +1000317 if get.genType {
318 // typ will be "*pkg.T" (message/group) or "pkg.T" (enum).
David Symondsdb7a6872013-01-30 17:08:05 +1100319 // Either of those might have a "[]" prefix if it is repeated.
David Symonds381349d2012-09-18 15:11:46 +1000320 // Drop the package qualifier since we have hoisted the type into this package.
David Symondsdb7a6872013-01-30 17:08:05 +1100321 rep := strings.HasPrefix(typ, "[]")
322 if rep {
323 typ = typ[2:]
324 }
David Symonds381349d2012-09-18 15:11:46 +1000325 star := typ[0] == '*'
326 typ = typ[strings.Index(typ, ".")+1:]
327 if star {
328 typ = "*" + typ
329 }
David Symondsdb7a6872013-01-30 17:08:05 +1100330 if rep {
331 // Go does not permit conversion between slice types where both
332 // element types are named. That means we need to generate a bit
333 // of code in this situation.
334 // typ is the element type.
335 // val is the expression to get the slice from the imported type.
336
337 ctyp := typ // conversion type expression; "Foo" or "(*Foo)"
338 if star {
339 ctyp = "(" + typ + ")"
340 }
341
David Symonds22e7eb42013-03-23 12:17:58 +1100342 g.P("func (m *", ms.sym, ") ", get.name, "() []", typ, " {")
David Symondsdb7a6872013-01-30 17:08:05 +1100343 g.In()
344 g.P("o := ", val)
345 g.P("if o == nil {")
346 g.In()
347 g.P("return nil")
348 g.Out()
349 g.P("}")
350 g.P("s := make([]", typ, ", len(o))")
351 g.P("for i, x := range o {")
352 g.In()
353 g.P("s[i] = ", ctyp, "(x)")
354 g.Out()
355 g.P("}")
356 g.P("return s")
357 g.Out()
358 g.P("}")
359 continue
360 }
David Symonds381349d2012-09-18 15:11:46 +1000361 // Convert imported type into the forwarding type.
362 val = "(" + typ + ")(" + val + ")"
363 }
364
David Symonds22e7eb42013-03-23 12:17:58 +1100365 g.P("func (m *", ms.sym, ") ", get.name, "() ", typ, " { return ", val, " }")
David Symonds381349d2012-09-18 15:11:46 +1000366 }
David Symonds31d58a22011-01-20 18:33:21 +1100367}
368
369type enumSymbol string
370
371func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
372 s := string(es)
373 g.P("type ", s, " ", pkg, ".", s)
374 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
375 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
David Symondsdd9ca042011-08-29 16:07:16 +1000376 g.P("func New", s, "(x ", s, ") *", s, " { e := ", s, "(x); return &e }")
David Symonds31d58a22011-01-20 18:33:21 +1100377}
378
379type constOrVarSymbol struct {
David Symonds58a75832013-08-07 09:47:38 +1000380 sym string
381 typ string // either "const" or "var"
382 cast string // if non-empty, a type cast is required (used for enums)
David Symonds31d58a22011-01-20 18:33:21 +1100383}
384
385func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
David Symonds58a75832013-08-07 09:47:38 +1000386 v := pkg + "." + cs.sym
387 if cs.cast != "" {
388 v = cs.cast + "(" + v + ")"
389 }
390 g.P(cs.typ, " ", cs.sym, " = ", v)
David Symonds31d58a22011-01-20 18:33:21 +1100391}
392
David Symonds4decd802011-08-04 11:27:07 +1000393// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700394type Object interface {
395 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
396 TypeName() []string
David Symonds4decd802011-08-04 11:27:07 +1000397 File() *descriptor.FileDescriptorProto
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700398}
399
400// Each package name we generate must be unique. The package we're generating
David Symonds4decd802011-08-04 11:27:07 +1000401// gets its own name but every other package must have a unique name that does
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700402// not conflict in the code we generate. These names are chosen globally (although
403// they don't have to be, it simplifies things to do them globally).
404func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
405 s, ok := uniquePackageName[fd]
406 if !ok {
David Symondsd4661c52012-08-30 15:17:53 +1000407 log.Fatal("internal error: no package name defined for " + fd.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700408 }
409 return s
410}
411
412// Generator is the type whose methods generate the output, stored in the associated response structure.
413type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000414 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700415
416 Request *plugin.CodeGeneratorRequest // The input.
417 Response *plugin.CodeGeneratorResponse // The output.
418
David Symonds162d0032012-06-28 09:44:46 -0700419 Param map[string]string // Command-line parameters.
420 PackageImportPath string // Go import path of the package we're generating code for
421 ImportPrefix string // String to prefix to imported package file names.
422 ImportMap map[string]string // Mapping from import name to generated name
Rob Pikec9e7d972010-06-10 10:30:22 -0700423
David Symonds11db5982012-08-15 10:54:05 +1000424 Pkg map[string]string // The names under which we import support packages
Rob Pikec9e7d972010-06-10 10:30:22 -0700425
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700426 packageName string // What we're calling ourselves.
427 allFiles []*FileDescriptor // All files in the tree
428 genFiles []*FileDescriptor // Those files we will generate output for.
429 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000430 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700431 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
432 indent string
433}
434
435// New creates a new generator and allocates the request and response protobufs.
436func New() *Generator {
437 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000438 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100439 g.Request = new(plugin.CodeGeneratorRequest)
440 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700441 return g
442}
443
Rob Pikea17fdd92011-11-02 12:43:05 -0700444// Error reports a problem, including an error, and exits the program.
445func (g *Generator) Error(err error, msgs ...string) {
446 s := strings.Join(msgs, " ") + ":" + err.Error()
David Symondsd4661c52012-08-30 15:17:53 +1000447 log.Print("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700448 os.Exit(1)
449}
450
451// Fail reports a problem and exits the program.
452func (g *Generator) Fail(msgs ...string) {
453 s := strings.Join(msgs, " ")
David Symondsd4661c52012-08-30 15:17:53 +1000454 log.Print("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700455 os.Exit(1)
456}
457
Rob Pikec9e7d972010-06-10 10:30:22 -0700458// CommandLineParameters breaks the comma-separated list of key=value pairs
459// in the parameter (a member of the request protobuf) into a key/value map.
460// It then sets file name mappings defined by those entries.
461func (g *Generator) CommandLineParameters(parameter string) {
462 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000463 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700464 if i := strings.Index(p, "="); i < 0 {
465 g.Param[p] = ""
466 } else {
467 g.Param[p[0:i]] = p[i+1:]
468 }
469 }
470
471 g.ImportMap = make(map[string]string)
472 for k, v := range g.Param {
David Symonds162d0032012-06-28 09:44:46 -0700473 switch k {
474 case "import_prefix":
Rob Pikec9e7d972010-06-10 10:30:22 -0700475 g.ImportPrefix = v
David Symondse90896d2012-08-15 11:18:55 +1000476 case "import_path":
David Symonds162d0032012-06-28 09:44:46 -0700477 g.PackageImportPath = v
478 default:
479 if len(k) > 0 && k[0] == 'M' {
480 g.ImportMap[k[1:]] = v
481 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700482 }
483 }
484}
485
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700486// DefaultPackageName returns the package name printed for the object.
487// If its file is in a different package, it returns the package name we're using for this file, plus ".".
488// Otherwise it returns the empty string.
489func (g *Generator) DefaultPackageName(obj Object) string {
490 pkg := obj.PackageName()
491 if pkg == g.packageName {
492 return ""
493 }
494 return pkg + "."
495}
496
497// For each input file, the unique package name to use, underscored.
498var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pike3f6f2d82011-12-18 13:55:35 -0800499
Rob Pikec9e7d972010-06-10 10:30:22 -0700500// Package names already registered. Key is the name from the .proto file;
501// value is the name that appears in the generated code.
502var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700503
Rob Pikec9e7d972010-06-10 10:30:22 -0700504// Create and remember a guaranteed unique package name for this file descriptor.
505// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
506// has no file descriptor.
507func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000508 // Convert dots to underscores before finding a unique alias.
David Symonds151cce02012-12-06 13:34:42 +1100509 pkg = strings.Map(badToUnderscore, pkg)
David Symonds41389812011-07-19 14:57:40 +1000510
David Symonds79eae332010-10-16 11:33:20 +1100511 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700512 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100513 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700514 }
515 // Install it.
516 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700517 if f != nil {
518 uniquePackageName[f.FileDescriptorProto] = pkg
519 }
520 return pkg
521}
522
David Symonds162d0032012-06-28 09:44:46 -0700523var isGoKeyword = map[string]bool{
524 "break": true,
525 "case": true,
526 "chan": true,
527 "const": true,
528 "continue": true,
529 "default": true,
530 "else": true,
531 "defer": true,
532 "fallthrough": true,
533 "for": true,
534 "func": true,
535 "go": true,
536 "goto": true,
537 "if": true,
538 "import": true,
539 "interface": true,
540 "map": true,
541 "package": true,
542 "range": true,
543 "return": true,
544 "select": true,
545 "struct": true,
546 "switch": true,
547 "type": true,
548 "var": true,
549}
550
551// defaultGoPackage returns the package name to use,
552// derived from the import path of the package we're building code for.
553func (g *Generator) defaultGoPackage() string {
554 p := g.PackageImportPath
555 if i := strings.LastIndex(p, "/"); i >= 0 {
556 p = p[i+1:]
557 }
558 if p == "" {
559 return ""
560 }
561
David Symonds151cce02012-12-06 13:34:42 +1100562 p = strings.Map(badToUnderscore, p)
David Symonds162d0032012-06-28 09:44:46 -0700563 // Identifier must not be keyword: insert _.
564 if isGoKeyword[p] {
565 p = "_" + p
566 }
567 // Identifier must not begin with digit: insert _.
568 if r, _ := utf8.DecodeRuneInString(p); unicode.IsDigit(r) {
569 p = "_" + p
570 }
571 return p
572}
573
Rob Pikec9e7d972010-06-10 10:30:22 -0700574// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700575// The package name must agree across all files being generated.
576// It also defines unique package names for all imported files.
577func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700578 // Register the name for this package. It will be the first name
579 // registered so is guaranteed to be unmodified.
David Symonds162d0032012-06-28 09:44:46 -0700580 pkg, explicit := g.genFiles[0].goPackageName()
581
582 // Check all files for an explicit go_package option.
583 for _, f := range g.genFiles {
584 thisPkg, thisExplicit := f.goPackageName()
585 if thisExplicit {
586 if !explicit {
587 // Let this file's go_package option serve for all input files.
588 pkg, explicit = thisPkg, true
589 } else if thisPkg != pkg {
590 g.Fail("inconsistent package names:", thisPkg, pkg)
591 }
592 }
593 }
594
595 // If we don't have an explicit go_package option but we have an
596 // import path, use that.
597 if !explicit {
598 p := g.defaultGoPackage()
599 if p != "" {
600 pkg, explicit = p, true
601 }
602 }
603
604 // If there was no go_package and no import path to use,
605 // double-check that all the inputs have the same implicit
606 // Go package name.
607 if !explicit {
608 for _, f := range g.genFiles {
609 thisPkg, _ := f.goPackageName()
610 if thisPkg != pkg {
611 g.Fail("inconsistent package names:", thisPkg, pkg)
612 }
613 }
614 }
615
Rob Pikec9e7d972010-06-10 10:30:22 -0700616 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
David Symonds162d0032012-06-28 09:44:46 -0700617
David Symonds11db5982012-08-15 10:54:05 +1000618 // Register the support package names. They might collide with the
Rob Pikec9e7d972010-06-10 10:30:22 -0700619 // name of a package we import.
David Symonds11db5982012-08-15 10:54:05 +1000620 g.Pkg = map[string]string{
621 "json": RegisterUniquePackageName("json", nil),
622 "math": RegisterUniquePackageName("math", nil),
623 "proto": RegisterUniquePackageName("proto", nil),
624 }
David Symonds162d0032012-06-28 09:44:46 -0700625
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700626AllFiles:
627 for _, f := range g.allFiles {
628 for _, genf := range g.genFiles {
629 if f == genf {
630 // In this package already.
631 uniquePackageName[f.FileDescriptorProto] = g.packageName
632 continue AllFiles
633 }
634 }
David Symonds7d5c8242011-03-14 12:03:50 -0700635 // The file is a dependency, so we want to ignore its go_package option
636 // because that is only relevant for its specific generated output.
David Symonds8bb32ca2012-06-28 10:22:09 -0700637 pkg := f.GetPackage()
David Symonds7d5c8242011-03-14 12:03:50 -0700638 if pkg == "" {
David Symonds151cce02012-12-06 13:34:42 +1100639 pkg = baseName(*f.Name)
David Symonds7d5c8242011-03-14 12:03:50 -0700640 }
641 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700642 }
643}
644
645// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
646// and FileDescriptorProtos into file-referenced objects within the Generator.
647// It also creates the list of files to generate and so should be called before GenerateAllFiles.
648func (g *Generator) WrapTypes() {
649 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
650 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700651 // We must wrap the descriptors before we wrap the enums
652 descs := wrapDescriptors(f)
653 g.buildNestedDescriptors(descs)
654 enums := wrapEnumDescriptors(f, descs)
655 exts := wrapExtensions(f)
David Symonds4decd802011-08-04 11:27:07 +1000656 imps := wrapImported(f, g)
David Symonds52925902013-07-26 19:20:20 +1000657 fd := &FileDescriptor{
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700658 FileDescriptorProto: f,
659 desc: descs,
660 enum: enums,
661 ext: exts,
David Symonds4decd802011-08-04 11:27:07 +1000662 imp: imps,
David Symonds151cce02012-12-06 13:34:42 +1100663 exported: make(map[Object][]symbol),
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700664 }
David Symonds52925902013-07-26 19:20:20 +1000665 extractComments(fd)
666 g.allFiles[i] = fd
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700667 }
668
669 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
670FindFiles:
671 for i, fileName := range g.Request.FileToGenerate {
672 // Search the list. This algorithm is n^2 but n is tiny.
673 for _, file := range g.allFiles {
David Symonds8bb32ca2012-06-28 10:22:09 -0700674 if fileName == file.GetName() {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700675 g.genFiles[i] = file
676 continue FindFiles
677 }
678 }
679 g.Fail("could not find file named", fileName)
680 }
681 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
682}
683
684// Scan the descriptors in this file. For each one, build the slice of nested descriptors
685func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
686 for _, desc := range descs {
687 if len(desc.NestedType) != 0 {
688 desc.nested = make([]*Descriptor, len(desc.NestedType))
689 n := 0
690 for _, nest := range descs {
691 if nest.parent == desc {
692 desc.nested[n] = nest
693 n++
694 }
695 }
696 if n != len(desc.NestedType) {
David Symonds8bb32ca2012-06-28 10:22:09 -0700697 g.Fail("internal error: nesting failure for", desc.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700698 }
699 }
700 }
701}
702
David Symonds52925902013-07-26 19:20:20 +1000703// Construct the Descriptor
704func newDescriptor(desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto, index int) *Descriptor {
David Symonds69cffb22013-02-21 14:26:53 +1100705 d := &Descriptor{
706 common: common{file},
707 DescriptorProto: desc,
708 parent: parent,
709 index: index,
710 }
David Symonds52925902013-07-26 19:20:20 +1000711 if parent == nil {
712 d.path = fmt.Sprintf("%d,%d", messagePath, index)
713 } else {
714 d.path = fmt.Sprintf("%s,%d,%d", parent.path, messageMessagePath, index)
715 }
David Symonds6eaeef12012-11-07 11:42:56 +1100716
717 // The only way to distinguish a group from a message is whether
718 // the containing message has a TYPE_GROUP field that matches.
719 if parent != nil {
720 parts := d.TypeName()
721 if file.Package != nil {
722 parts = append([]string{*file.Package}, parts...)
723 }
724 exp := "." + strings.Join(parts, ".")
725 for _, field := range parent.Field {
726 if field.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP && field.GetTypeName() == exp {
727 d.group = true
728 break
729 }
730 }
731 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700732
733 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
734 for i, field := range desc.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000735 d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700736 }
737
David Symonds52925902013-07-26 19:20:20 +1000738 return d
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700739}
740
741// Return a slice of all the Descriptors defined within this file
742func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
743 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
David Symonds4de8f722012-09-26 13:51:38 +1000744 for i, desc := range file.MessageType {
745 sl = wrapThisDescriptor(sl, desc, nil, file, i)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700746 }
747 return sl
748}
749
750// Wrap this Descriptor, recursively
David Symonds4de8f722012-09-26 13:51:38 +1000751func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto, index int) []*Descriptor {
David Symonds52925902013-07-26 19:20:20 +1000752 sl = append(sl, newDescriptor(desc, parent, file, index))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700753 me := sl[len(sl)-1]
David Symonds69cffb22013-02-21 14:26:53 +1100754 for i, nested := range desc.NestedType {
755 sl = wrapThisDescriptor(sl, nested, me, file, i)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700756 }
757 return sl
758}
759
David Symonds52925902013-07-26 19:20:20 +1000760// Construct the EnumDescriptor
761func newEnumDescriptor(desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto, index int) *EnumDescriptor {
762 ed := &EnumDescriptor{
763 common: common{file},
764 EnumDescriptorProto: desc,
765 parent: parent,
766 }
767 if parent == nil {
768 ed.path = fmt.Sprintf("%d,%d", enumPath, index)
769 } else {
770 ed.path = fmt.Sprintf("%s,%d,%d", parent.path, messageEnumPath, index)
771 }
772 return ed
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700773}
774
775// Return a slice of all the EnumDescriptors defined within this file
776func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
777 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000778 // Top-level enums.
David Symonds52925902013-07-26 19:20:20 +1000779 for i, enum := range file.EnumType {
780 sl = append(sl, newEnumDescriptor(enum, nil, file, i))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700781 }
David Symonds5256cf62010-06-27 10:33:42 +1000782 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700783 for _, nested := range descs {
David Symonds52925902013-07-26 19:20:20 +1000784 for i, enum := range nested.EnumType {
785 sl = append(sl, newEnumDescriptor(enum, nested, file, i))
David Symonds5256cf62010-06-27 10:33:42 +1000786 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700787 }
788 return sl
789}
790
791// Return a slice of all the top-level ExtensionDescriptors defined within this file.
792func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
793 sl := make([]*ExtensionDescriptor, len(file.Extension))
794 for i, field := range file.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000795 sl[i] = &ExtensionDescriptor{common{file}, field, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700796 }
797 return sl
798}
799
David Symonds4decd802011-08-04 11:27:07 +1000800// Return a slice of all the types that are publicly imported into this file.
801func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
802 for _, index := range file.PublicDependency {
803 df := g.fileByName(file.Dependency[index])
804 for _, d := range df.desc {
805 sl = append(sl, &ImportedDescriptor{common{file}, d})
806 }
807 for _, e := range df.enum {
808 sl = append(sl, &ImportedDescriptor{common{file}, e})
809 }
810 for _, ext := range df.ext {
811 sl = append(sl, &ImportedDescriptor{common{file}, ext})
812 }
813 }
814 return
815}
816
David Symonds52925902013-07-26 19:20:20 +1000817func extractComments(file *FileDescriptor) {
818 file.comments = make(map[string]*descriptor.SourceCodeInfo_Location)
819 for _, loc := range file.GetSourceCodeInfo().GetLocation() {
820 if loc.LeadingComments == nil {
821 continue
822 }
823 var p []string
824 for _, n := range loc.Path {
825 p = append(p, strconv.Itoa(int(n)))
826 }
827 file.comments[strings.Join(p, ",")] = loc
828 }
829}
830
Rob Pikec9e7d972010-06-10 10:30:22 -0700831// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700832// The key names for the map come from the input data, which puts a period at the beginning.
833// It should be called after SetPackageNames and before GenerateAllFiles.
834func (g *Generator) BuildTypeNameMap() {
835 g.typeNameToObject = make(map[string]Object)
836 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700837 // The names in this loop are defined by the proto world, not us, so the
838 // package name may be empty. If so, the dotted package name of X will
839 // be ".X"; otherwise it will be ".pkg.X".
David Symonds8bb32ca2012-06-28 10:22:09 -0700840 dottedPkg := "." + f.GetPackage()
Rob Pikec9e7d972010-06-10 10:30:22 -0700841 if dottedPkg != "." {
842 dottedPkg += "."
843 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700844 for _, enum := range f.enum {
845 name := dottedPkg + dottedSlice(enum.TypeName())
846 g.typeNameToObject[name] = enum
847 }
848 for _, desc := range f.desc {
849 name := dottedPkg + dottedSlice(desc.TypeName())
850 g.typeNameToObject[name] = desc
851 }
852 }
853}
854
855// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
856// returns the descriptor for the message or enum with that name.
857func (g *Generator) ObjectNamed(typeName string) Object {
David Symonds4decd802011-08-04 11:27:07 +1000858 o, ok := g.typeNameToObject[typeName]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700859 if !ok {
860 g.Fail("can't find object with type", typeName)
861 }
David Symonds4decd802011-08-04 11:27:07 +1000862
863 // If the file of this object isn't a direct dependency of the current file,
864 // or in the current file, then this object has been publicly imported into
865 // a dependency of the current file.
866 // We should return the ImportedDescriptor object for it instead.
867 direct := *o.File().Name == *g.file.Name
868 if !direct {
869 for _, dep := range g.file.Dependency {
870 if *g.fileByName(dep).Name == *o.File().Name {
871 direct = true
872 break
873 }
874 }
875 }
876 if !direct {
877 found := false
878 Loop:
879 for _, dep := range g.file.Dependency {
880 df := g.fileByName(*g.fileByName(dep).Name)
881 for _, td := range df.imp {
882 if td.o == o {
883 // Found it!
884 o = td
885 found = true
886 break Loop
887 }
888 }
889 }
890 if !found {
891 log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
892 }
893 }
894
895 return o
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700896}
897
898// P prints the arguments to the generated output. It handles strings and int32s, plus
899// handling indirections because they may be *string, etc.
900func (g *Generator) P(str ...interface{}) {
901 g.WriteString(g.indent)
902 for _, v := range str {
903 switch s := v.(type) {
904 case string:
905 g.WriteString(s)
906 case *string:
907 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700908 case bool:
909 g.WriteString(fmt.Sprintf("%t", s))
910 case *bool:
911 g.WriteString(fmt.Sprintf("%t", *s))
David Symonds4de8f722012-09-26 13:51:38 +1000912 case int:
913 g.WriteString(fmt.Sprintf("%d", s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700914 case *int32:
915 g.WriteString(fmt.Sprintf("%d", *s))
David Symonds2ce8ed42013-06-20 13:22:17 +1000916 case *int64:
917 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700918 case float64:
919 g.WriteString(fmt.Sprintf("%g", s))
920 case *float64:
921 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700922 default:
923 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
924 }
925 }
926 g.WriteByte('\n')
927}
928
929// In Indents the output one tab stop.
930func (g *Generator) In() { g.indent += "\t" }
931
932// Out unindents the output one tab stop.
933func (g *Generator) Out() {
934 if len(g.indent) > 0 {
935 g.indent = g.indent[1:]
936 }
937}
938
939// GenerateAllFiles generates the output for all the files we're outputting.
940func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700941 // Initialize the plugins
942 for _, p := range plugins {
943 p.Init(g)
944 }
David Symonds31d58a22011-01-20 18:33:21 +1100945 // Generate the output. The generator runs for every file, even the files
946 // that we don't generate output for, so that we can collate the full list
947 // of exported symbols to support public imports.
948 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
949 for _, file := range g.genFiles {
950 genFileMap[file] = true
951 }
952 i := 0
953 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700954 g.Reset()
955 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100956 if _, ok := genFileMap[file]; !ok {
957 continue
958 }
David Symondsb0127532010-11-09 11:10:46 +1100959 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700960 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
961 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100962 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700963 }
964}
965
966// Run all the plugins associated with the file.
967func (g *Generator) runPlugins(file *FileDescriptor) {
968 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700969 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700970 }
971}
972
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700973// FileOf return the FileDescriptor for this FileDescriptorProto.
974func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
975 for _, file := range g.allFiles {
976 if file.FileDescriptorProto == fd {
977 return file
978 }
979 }
David Symonds8bb32ca2012-06-28 10:22:09 -0700980 g.Fail("could not find file in table:", fd.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700981 return nil
982}
983
984// Fill the response protocol buffer with the generated output for all the files we're
985// supposed to generate.
986func (g *Generator) generate(file *FileDescriptor) {
987 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000988 g.usedPackages = make(map[string]bool)
989
David Symonds4decd802011-08-04 11:27:07 +1000990 for _, td := range g.file.imp {
991 g.generateImported(td)
992 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700993 for _, enum := range g.file.enum {
994 g.generateEnum(enum)
995 }
996 for _, desc := range g.file.desc {
997 g.generateMessage(desc)
998 }
999 for _, ext := range g.file.ext {
1000 g.generateExtension(ext)
1001 }
1002 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +10001003
Rob Pikec9e7d972010-06-10 10:30:22 -07001004 // Run the plugins before the imports so we know which imports are necessary.
1005 g.runPlugins(file)
1006
David Symondsf90e3382010-05-05 10:53:44 +10001007 // Generate header and imports last, though they appear first in the output.
1008 rem := g.Buffer
1009 g.Buffer = new(bytes.Buffer)
1010 g.generateHeader()
1011 g.generateImports()
1012 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +10001013
1014 // Reformat generated code.
1015 fset := token.NewFileSet()
1016 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
1017 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -07001018 g.Fail("bad Go source code was generated:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +10001019 return
1020 }
1021 g.Reset()
David Symonds29d5d392012-12-20 12:14:58 +11001022 err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(g, fset, ast)
David Symondsb1d55a02011-04-08 09:55:06 +10001023 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -07001024 g.Fail("generated Go source code could not be reformatted:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +10001025 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001026}
1027
David Symonds62539862012-08-04 10:06:55 +10001028// Generate the header, including package definition
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001029func (g *Generator) generateHeader() {
David Symondsd4661c52012-08-30 15:17:53 +10001030 g.P("// Code generated by protoc-gen-go.")
1031 g.P("// source: ", *g.file.Name)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001032 g.P("// DO NOT EDIT!")
1033 g.P()
1034 g.P("package ", g.file.PackageName())
1035 g.P()
1036}
1037
David Symonds52925902013-07-26 19:20:20 +10001038// PrintComments prints any comments from the source .proto file.
1039// The path is a comma-separated list of integers.
1040// See descriptor.proto for its format.
1041func (g *Generator) PrintComments(path string) {
1042 if loc, ok := g.file.comments[path]; ok {
1043 text := strings.TrimSuffix(loc.GetLeadingComments(), "\n")
1044 for _, line := range strings.Split(text, "\n") {
1045 g.P("// ", strings.TrimPrefix(line, " "))
1046 }
1047 }
1048}
1049
David Symonds31d58a22011-01-20 18:33:21 +11001050func (g *Generator) fileByName(filename string) *FileDescriptor {
1051 for _, fd := range g.allFiles {
David Symonds8bb32ca2012-06-28 10:22:09 -07001052 if fd.GetName() == filename {
David Symonds31d58a22011-01-20 18:33:21 +11001053 return fd
1054 }
1055 }
1056 return nil
1057}
1058
David Symonds9685cb02012-02-13 08:10:34 +11001059// weak returns whether the ith import of the current file is a weak import.
1060func (g *Generator) weak(i int32) bool {
1061 for _, j := range g.file.WeakDependency {
1062 if j == i {
1063 return true
1064 }
1065 }
1066 return false
1067}
1068
David Symonds62539862012-08-04 10:06:55 +10001069// Generate the imports
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001070func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -07001071 // We almost always need a proto import. Rather than computing when we
1072 // do, which is tricky when there's a plugin, just import it and
Rob Pikea17fdd92011-11-02 12:43:05 -07001073 // reference it later. The same argument applies to the math package,
David Symonds11db5982012-08-15 10:54:05 +10001074 // for handling bit patterns for floating-point numbers, and to the
1075 // json package, for symbolic names of enum values for JSON marshaling.
David Symonds151cce02012-12-06 13:34:42 +11001076 g.P("import " + g.Pkg["proto"] + " " + strconv.Quote(g.ImportPrefix+"code.google.com/p/goprotobuf/proto"))
David Symonds11db5982012-08-15 10:54:05 +10001077 g.P("import " + g.Pkg["json"] + ` "encoding/json"`)
1078 g.P("import " + g.Pkg["math"] + ` "math"`)
David Symonds9685cb02012-02-13 08:10:34 +11001079 for i, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +11001080 fd := g.fileByName(s)
1081 // Do not import our own package.
1082 if fd.PackageName() == g.packageName {
1083 continue
1084 }
1085 filename := goFileName(s)
1086 if substitution, ok := g.ImportMap[s]; ok {
1087 filename = substitution
1088 }
1089 filename = g.ImportPrefix + filename
1090 if strings.HasSuffix(filename, ".go") {
1091 filename = filename[0 : len(filename)-3]
1092 }
David Symonds9685cb02012-02-13 08:10:34 +11001093 // Skip weak imports.
1094 if g.weak(int32(i)) {
David Symonds151cce02012-12-06 13:34:42 +11001095 g.P("// skipping weak import ", fd.PackageName(), " ", strconv.Quote(filename))
David Symonds9685cb02012-02-13 08:10:34 +11001096 continue
1097 }
David Symonds31d58a22011-01-20 18:33:21 +11001098 if _, ok := g.usedPackages[fd.PackageName()]; ok {
David Symonds151cce02012-12-06 13:34:42 +11001099 g.P("import ", fd.PackageName(), " ", strconv.Quote(filename))
David Symonds31d58a22011-01-20 18:33:21 +11001100 } else {
David Symonds3fa055f2011-05-05 15:19:04 -07001101 // TODO: Re-enable this when we are more feature-complete.
1102 // For instance, some protos use foreign field extensions, which we don't support.
1103 // Until then, this is just annoying spam.
1104 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
David Symonds151cce02012-12-06 13:34:42 +11001105 g.P("// discarding unused import ", fd.PackageName(), " ", strconv.Quote(filename))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001106 }
1107 }
1108 g.P()
1109 // TODO: may need to worry about uniqueness across plugins
1110 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -07001111 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001112 g.P()
1113 }
David Symonds62539862012-08-04 10:06:55 +10001114 g.P("// Reference proto, json, and math imports to suppress error if they are not otherwise used.")
David Symonds11db5982012-08-15 10:54:05 +10001115 g.P("var _ = ", g.Pkg["proto"], ".Marshal")
David Symondsdb7a6872013-01-30 17:08:05 +11001116 g.P("var _ = &", g.Pkg["json"], ".SyntaxError{}")
1117 g.P("var _ = ", g.Pkg["math"], ".Inf")
Rob Pikec9e7d972010-06-10 10:30:22 -07001118 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001119}
1120
David Symonds4decd802011-08-04 11:27:07 +10001121func (g *Generator) generateImported(id *ImportedDescriptor) {
David Symondsb2a00c82011-08-04 16:52:58 +10001122 // Don't generate public import symbols for files that we are generating
1123 // code for, since those symbols will already be in this package.
1124 // We can't simply avoid creating the ImportedDescriptor objects,
1125 // because g.genFiles isn't populated at that stage.
David Symonds4decd802011-08-04 11:27:07 +10001126 tn := id.TypeName()
1127 sn := tn[len(tn)-1]
David Symondsb2a00c82011-08-04 16:52:58 +10001128 df := g.FileOf(id.o.File())
1129 filename := *df.Name
1130 for _, fd := range g.genFiles {
1131 if *fd.Name == filename {
1132 g.P("// Ignoring public import of ", sn, " from ", filename)
1133 g.P()
1134 return
1135 }
1136 }
1137 g.P("// ", sn, " from public import ", filename)
1138 g.usedPackages[df.PackageName()] = true
David Symonds4decd802011-08-04 11:27:07 +10001139
David Symondsb2a00c82011-08-04 16:52:58 +10001140 for _, sym := range df.exported[id.o] {
1141 sym.GenerateAlias(g, df.PackageName())
1142 }
1143
1144 g.P()
David Symonds4decd802011-08-04 11:27:07 +10001145}
1146
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001147// Generate the enum definitions for this EnumDescriptor.
1148func (g *Generator) generateEnum(enum *EnumDescriptor) {
1149 // The full type name
1150 typeName := enum.TypeName()
1151 // The full type name, CamelCased.
1152 ccTypeName := CamelCaseSlice(typeName)
1153 ccPrefix := enum.prefix()
David Symonds52925902013-07-26 19:20:20 +10001154
1155 g.PrintComments(enum.path)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001156 g.P("type ", ccTypeName, " int32")
David Symondsb2a00c82011-08-04 16:52:58 +10001157 g.file.addExport(enum, enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001158 g.P("const (")
1159 g.In()
David Symonds52925902013-07-26 19:20:20 +10001160 for i, e := range enum.Value {
1161 g.PrintComments(fmt.Sprintf("%s,%d,%d", enum.path, enumValuePath, i))
1162
David Symonds31d58a22011-01-20 18:33:21 +11001163 name := ccPrefix + *e.Name
David Symondsdd9ca042011-08-29 16:07:16 +10001164 g.P(name, " ", ccTypeName, " = ", e.Number)
David Symonds58a75832013-08-07 09:47:38 +10001165 g.file.addExport(enum, constOrVarSymbol{name, "const", ccTypeName})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001166 }
1167 g.Out()
1168 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +11001169 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001170 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -07001171 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001172 for _, e := range enum.Value {
1173 duplicate := ""
1174 if _, present := generated[*e.Number]; present {
1175 duplicate = "// Duplicate value: "
1176 }
David Symonds151cce02012-12-06 13:34:42 +11001177 g.P(duplicate, e.Number, ": ", strconv.Quote(*e.Name), ",")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001178 generated[*e.Number] = true
1179 }
1180 g.Out()
1181 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +11001182 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001183 g.In()
1184 for _, e := range enum.Value {
David Symonds151cce02012-12-06 13:34:42 +11001185 g.P(strconv.Quote(*e.Name), ": ", e.Number, ",")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001186 }
1187 g.Out()
1188 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +11001189
David Symondsefeca9a2012-05-08 10:36:04 +10001190 g.P("func (x ", ccTypeName, ") Enum() *", ccTypeName, " {")
1191 g.In()
1192 g.P("p := new(", ccTypeName, ")")
1193 g.P("*p = x")
1194 g.P("return p")
1195 g.Out()
1196 g.P("}")
1197
David Symonds940b9612011-04-01 10:45:23 +11001198 g.P("func (x ", ccTypeName, ") String() string {")
1199 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001200 g.P("return ", g.Pkg["proto"], ".EnumName(", ccTypeName, "_name, int32(x))")
David Symonds940b9612011-04-01 10:45:23 +11001201 g.Out()
1202 g.P("}")
1203
David Symonds62539862012-08-04 10:06:55 +10001204 g.P("func (x ", ccTypeName, ") MarshalJSON() ([]byte, error) {")
1205 g.In()
1206 g.P("return json.Marshal(x.String())")
1207 g.Out()
1208 g.P("}")
1209
1210 g.P("func (x *", ccTypeName, ") UnmarshalJSON(data []byte) error {")
1211 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001212 g.P("value, err := ", g.Pkg["proto"], ".UnmarshalJSONEnum(", ccTypeName, `_value, data, "`, ccTypeName, `")`)
David Symonds62539862012-08-04 10:06:55 +10001213 g.P("if err != nil {")
1214 g.In()
1215 g.P("return err")
1216 g.Out()
1217 g.P("}")
1218 g.P("*x = ", ccTypeName, "(value)")
1219 g.P("return nil")
1220 g.Out()
1221 g.P("}")
1222
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001223 g.P()
1224}
1225
David Symonds8935abf2011-07-04 15:53:16 +10001226// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001227// identifies details of the field for the protocol buffer marshaling and unmarshaling
1228// code. The fields are:
1229// wire encoding
1230// protocol tag number
1231// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +11001232// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001233// name= the original declared name
1234// enum= the name of the enum type if it is an enum-typed field.
1235// def= string representation of the default value, if any.
1236// The default value must be in a representation that can be used at run-time
1237// to generate the default value. Thus bools become 0 and 1, for instance.
1238func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
1239 optrepreq := ""
1240 switch {
1241 case isOptional(field):
1242 optrepreq = "opt"
1243 case isRequired(field):
1244 optrepreq = "req"
1245 case isRepeated(field):
1246 optrepreq = "rep"
1247 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001248 defaultValue := field.GetDefaultValue()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001249 if defaultValue != "" {
1250 switch *field.Type {
1251 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1252 if defaultValue == "true" {
1253 defaultValue = "1"
1254 } else {
1255 defaultValue = "0"
1256 }
1257 case descriptor.FieldDescriptorProto_TYPE_STRING,
1258 descriptor.FieldDescriptorProto_TYPE_BYTES:
David Symondsb79d99b2011-08-29 16:38:49 +10001259 // Nothing to do. Quoting is done for the whole tag.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001260 case descriptor.FieldDescriptorProto_TYPE_ENUM:
1261 // For enums we need to provide the integer constant.
David Symonds8bb32ca2012-06-28 10:22:09 -07001262 obj := g.ObjectNamed(field.GetTypeName())
David Symonds58a75832013-08-07 09:47:38 +10001263 if id, ok := obj.(*ImportedDescriptor); ok {
1264 // It is an enum that was publicly imported.
1265 // We need the underlying type.
1266 obj = id.o
1267 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001268 enum, ok := obj.(*EnumDescriptor)
1269 if !ok {
David Symonds58a75832013-08-07 09:47:38 +10001270 log.Printf("obj is a %T", obj)
1271 if id, ok := obj.(*ImportedDescriptor); ok {
1272 log.Printf("id.o is a %T", id.o)
1273 }
1274 g.Fail("unknown enum type", CamelCaseSlice(obj.TypeName()))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001275 }
1276 defaultValue = enum.integerValueAsString(defaultValue)
1277 }
1278 defaultValue = ",def=" + defaultValue
1279 }
1280 enum := ""
1281 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
David Symonds4decd802011-08-04 11:27:07 +10001282 // We avoid using obj.PackageName(), because we want to use the
1283 // original (proto-world) package name.
David Symonds8bb32ca2012-06-28 10:22:09 -07001284 obj := g.ObjectNamed(field.GetTypeName())
David Symonds58a75832013-08-07 09:47:38 +10001285 if id, ok := obj.(*ImportedDescriptor); ok {
1286 obj = id.o
1287 }
David Symonds4decd802011-08-04 11:27:07 +10001288 enum = ",enum="
David Symonds8bb32ca2012-06-28 10:22:09 -07001289 if pkg := obj.File().GetPackage(); pkg != "" {
David Symonds4decd802011-08-04 11:27:07 +10001290 enum += pkg + "."
1291 }
1292 enum += CamelCaseSlice(obj.TypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001293 }
David Symonds5b7775e2010-12-01 10:09:04 +11001294 packed := ""
David Symonds8bb32ca2012-06-28 10:22:09 -07001295 if field.Options != nil && field.Options.GetPacked() {
David Symonds5b7775e2010-12-01 10:09:04 +11001296 packed = ",packed"
1297 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001298 fieldName := field.GetName()
David Symondse37856c2011-06-22 12:52:53 +10001299 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +10001300 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
1301 // We must use the type name for groups instead of
1302 // the field name to preserve capitalization.
1303 // type_name in FieldDescriptorProto is fully-qualified,
1304 // but we only want the local part.
1305 name = *field.TypeName
1306 if i := strings.LastIndex(name, "."); i >= 0 {
1307 name = name[i+1:]
1308 }
David Symondse37856c2011-06-22 12:52:53 +10001309 }
1310 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001311 name = ""
1312 } else {
1313 name = ",name=" + name
1314 }
David Symonds151cce02012-12-06 13:34:42 +11001315 return strconv.Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001316 wiretype,
David Symonds8bb32ca2012-06-28 10:22:09 -07001317 field.GetNumber(),
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001318 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +11001319 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001320 name,
1321 enum,
1322 defaultValue))
1323}
1324
1325func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
1326 switch typ {
1327 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1328 return false
1329 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1330 return false
1331 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1332 return false
1333 }
1334 return true
1335}
1336
1337// TypeName is the printed name appropriate for an item. If the object is in the current file,
1338// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -07001339// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001340// package name followed by the item name.
1341// The result always has an initial capital.
1342func (g *Generator) TypeName(obj Object) string {
1343 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
1344}
1345
1346// TypeNameWithPackage is like TypeName, but always includes the package
1347// name even if the object is in our own package.
1348func (g *Generator) TypeNameWithPackage(obj Object) string {
1349 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
1350}
1351
1352// GoType returns a string representing the type name, and the wire type
1353func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
1354 // TODO: Options.
1355 switch *field.Type {
1356 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
1357 typ, wire = "float64", "fixed64"
1358 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
1359 typ, wire = "float32", "fixed32"
1360 case descriptor.FieldDescriptorProto_TYPE_INT64:
1361 typ, wire = "int64", "varint"
1362 case descriptor.FieldDescriptorProto_TYPE_UINT64:
1363 typ, wire = "uint64", "varint"
1364 case descriptor.FieldDescriptorProto_TYPE_INT32:
1365 typ, wire = "int32", "varint"
1366 case descriptor.FieldDescriptorProto_TYPE_UINT32:
1367 typ, wire = "uint32", "varint"
1368 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
1369 typ, wire = "uint64", "fixed64"
1370 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
1371 typ, wire = "uint32", "fixed32"
1372 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1373 typ, wire = "bool", "varint"
1374 case descriptor.FieldDescriptorProto_TYPE_STRING:
1375 typ, wire = "string", "bytes"
1376 case descriptor.FieldDescriptorProto_TYPE_GROUP:
David Symonds8bb32ca2012-06-28 10:22:09 -07001377 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001378 typ, wire = "*"+g.TypeName(desc), "group"
1379 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
David Symonds8bb32ca2012-06-28 10:22:09 -07001380 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001381 typ, wire = "*"+g.TypeName(desc), "bytes"
1382 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1383 typ, wire = "[]byte", "bytes"
1384 case descriptor.FieldDescriptorProto_TYPE_ENUM:
David Symonds8bb32ca2012-06-28 10:22:09 -07001385 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001386 typ, wire = g.TypeName(desc), "varint"
1387 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
1388 typ, wire = "int32", "fixed32"
1389 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1390 typ, wire = "int64", "fixed64"
1391 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1392 typ, wire = "int32", "zigzag32"
1393 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1394 typ, wire = "int64", "zigzag64"
1395 default:
David Symonds8bb32ca2012-06-28 10:22:09 -07001396 g.Fail("unknown type for", field.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001397 }
1398 if isRepeated(field) {
1399 typ = "[]" + typ
1400 } else if needsStar(*field.Type) {
1401 typ = "*" + typ
1402 }
1403 return
1404}
1405
David Symondsf90e3382010-05-05 10:53:44 +10001406func (g *Generator) RecordTypeUse(t string) {
1407 if obj, ok := g.typeNameToObject[t]; ok {
David Symonds4decd802011-08-04 11:27:07 +10001408 // Call ObjectNamed to get the true object to record the use.
1409 obj = g.ObjectNamed(t)
David Symondsf90e3382010-05-05 10:53:44 +10001410 g.usedPackages[obj.PackageName()] = true
1411 }
1412}
1413
David Symonds3ce53032012-02-13 10:55:03 +11001414// Method names that may be generated. Fields with these names get an
1415// underscore appended.
1416var methodNames = [...]string{
1417 "Reset",
1418 "String",
David Symonds9f60f432012-06-14 09:45:25 +10001419 "ProtoMessage",
David Symonds3ce53032012-02-13 10:55:03 +11001420 "Marshal",
1421 "Unmarshal",
1422 "ExtensionRangeArray",
1423 "ExtensionMap",
David Symonds4de8f722012-09-26 13:51:38 +10001424 "Descriptor",
David Symonds3ce53032012-02-13 10:55:03 +11001425}
1426
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001427// Generate the type and default constant definitions for this Descriptor.
1428func (g *Generator) generateMessage(message *Descriptor) {
1429 // The full type name
1430 typeName := message.TypeName()
1431 // The full type name, CamelCased.
1432 ccTypeName := CamelCaseSlice(typeName)
1433
David Symonds3ce53032012-02-13 10:55:03 +11001434 usedNames := make(map[string]bool)
1435 for _, n := range methodNames {
1436 usedNames[n] = true
1437 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001438 fieldNames := make(map[*descriptor.FieldDescriptorProto]string)
David Symonds22e7eb42013-03-23 12:17:58 +11001439 fieldGetterNames := make(map[*descriptor.FieldDescriptorProto]string)
David Symonds52925902013-07-26 19:20:20 +10001440
1441 g.PrintComments(message.path)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001442 g.P("type ", ccTypeName, " struct {")
1443 g.In()
David Symonds6eaeef12012-11-07 11:42:56 +11001444
David Symonds52925902013-07-26 19:20:20 +10001445 for i, field := range message.Field {
1446 g.PrintComments(fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i))
1447
David Symonds22e7eb42013-03-23 12:17:58 +11001448 fieldName := CamelCase(*field.Name)
1449 for usedNames[fieldName] {
1450 fieldName += "_"
David Symonds3ce53032012-02-13 10:55:03 +11001451 }
David Symonds22e7eb42013-03-23 12:17:58 +11001452 fieldGetterName := fieldName
1453 usedNames[fieldName] = true
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001454 typename, wiretype := g.GoType(message, field)
David Symondsb2a00c82011-08-04 16:52:58 +10001455 jsonName := *field.Name
David Symonds6eaeef12012-11-07 11:42:56 +11001456 tag := fmt.Sprintf("protobuf:%s json:%q", g.goTag(field, wiretype), jsonName+",omitempty")
David Symonds22e7eb42013-03-23 12:17:58 +11001457 fieldNames[field] = fieldName
1458 fieldGetterNames[field] = fieldGetterName
1459 g.P(fieldName, "\t", typename, "\t`", tag, "`")
David Symonds8bb32ca2012-06-28 10:22:09 -07001460 g.RecordTypeUse(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001461 }
1462 if len(message.ExtensionRange) > 0 {
David Symonds11db5982012-08-15 10:54:05 +10001463 g.P("XXX_extensions\t\tmap[int32]", g.Pkg["proto"], ".Extension `json:\"-\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001464 }
David Symonds22e7eb42013-03-23 12:17:58 +11001465 g.P("XXX_unrecognized\t[]byte `json:\"-\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001466 g.Out()
1467 g.P("}")
1468
David Symonds9f60f432012-06-14 09:45:25 +10001469 // Reset, String and ProtoMessage methods.
David Symonds22e7eb42013-03-23 12:17:58 +11001470 g.P("func (m *", ccTypeName, ") Reset() { *m = ", ccTypeName, "{} }")
David Symonds4f4ce3d2013-05-03 08:51:59 +10001471 g.P("func (m *", ccTypeName, ") String() string { return ", g.Pkg["proto"], ".CompactTextString(m) }")
1472 g.P("func (*", ccTypeName, ") ProtoMessage() {}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001473
1474 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001475 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001476 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001477 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001478 // message_set_wire_format only makes sense when extensions are defined.
David Symonds8bb32ca2012-06-28 10:22:09 -07001479 if opts := message.Options; opts != nil && opts.GetMessageSetWireFormat() {
David Symonds31d58a22011-01-20 18:33:21 +11001480 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001481 g.P()
David Symonds22e7eb42013-03-23 12:17:58 +11001482 g.P("func (m *", ccTypeName, ") Marshal() ([]byte, error) {")
David Symonds4fee3b12010-11-11 10:00:13 +11001483 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001484 g.P("return ", g.Pkg["proto"], ".MarshalMessageSet(m.ExtensionMap())")
David Symonds4fee3b12010-11-11 10:00:13 +11001485 g.Out()
1486 g.P("}")
David Symonds22e7eb42013-03-23 12:17:58 +11001487 g.P("func (m *", ccTypeName, ") Unmarshal(buf []byte) error {")
David Symonds4fee3b12010-11-11 10:00:13 +11001488 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001489 g.P("return ", g.Pkg["proto"], ".UnmarshalMessageSet(buf, m.ExtensionMap())")
David Symonds4fee3b12010-11-11 10:00:13 +11001490 g.Out()
1491 g.P("}")
1492 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
David Symonds11db5982012-08-15 10:54:05 +10001493 g.P("var _ ", g.Pkg["proto"], ".Marshaler = (*", ccTypeName, ")(nil)")
1494 g.P("var _ ", g.Pkg["proto"], ".Unmarshaler = (*", ccTypeName, ")(nil)")
David Symonds4fee3b12010-11-11 10:00:13 +11001495 }
1496
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001497 g.P()
David Symonds11db5982012-08-15 10:54:05 +10001498 g.P("var extRange_", ccTypeName, " = []", g.Pkg["proto"], ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001499 g.In()
1500 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001501 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
David Symonds1d72f7a2011-08-19 18:28:52 +10001502 g.P("{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001503 }
1504 g.Out()
1505 g.P("}")
David Symonds11db5982012-08-15 10:54:05 +10001506 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.Pkg["proto"], ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001507 g.In()
1508 g.P("return extRange_", ccTypeName)
1509 g.Out()
1510 g.P("}")
David Symonds22e7eb42013-03-23 12:17:58 +11001511 g.P("func (m *", ccTypeName, ") ExtensionMap() map[int32]", g.Pkg["proto"], ".Extension {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001512 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001513 g.P("if m.XXX_extensions == nil {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001514 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001515 g.P("m.XXX_extensions = make(map[int32]", g.Pkg["proto"], ".Extension)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001516 g.Out()
1517 g.P("}")
David Symonds22e7eb42013-03-23 12:17:58 +11001518 g.P("return m.XXX_extensions")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001519 g.Out()
1520 g.P("}")
1521 }
1522
1523 // Default constants
David Symonds8bb32ca2012-06-28 10:22:09 -07001524 defNames := make(map[*descriptor.FieldDescriptorProto]string)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001525 for _, field := range message.Field {
David Symonds8bb32ca2012-06-28 10:22:09 -07001526 def := field.GetDefaultValue()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001527 if def == "" {
1528 continue
1529 }
1530 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
David Symonds8bb32ca2012-06-28 10:22:09 -07001531 defNames[field] = fieldname
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001532 typename, _ := g.GoType(message, field)
1533 if typename[0] == '*' {
1534 typename = typename[1:]
1535 }
1536 kind := "const "
1537 switch {
1538 case typename == "bool":
1539 case typename == "string":
David Symonds151cce02012-12-06 13:34:42 +11001540 def = strconv.Quote(def)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001541 case typename == "[]byte":
David Symonds151cce02012-12-06 13:34:42 +11001542 def = "[]byte(" + strconv.Quote(def) + ")"
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001543 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001544 case def == "inf", def == "-inf", def == "nan":
1545 // These names are known to, and defined by, the protocol language.
1546 switch def {
1547 case "inf":
1548 def = "math.Inf(1)"
1549 case "-inf":
1550 def = "math.Inf(-1)"
1551 case "nan":
1552 def = "math.NaN()"
1553 }
1554 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1555 def = "float32(" + def + ")"
1556 }
1557 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001558 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1559 // Must be an enum. Need to construct the prefixed name.
David Symonds8bb32ca2012-06-28 10:22:09 -07001560 obj := g.ObjectNamed(field.GetTypeName())
David Symonds58a75832013-08-07 09:47:38 +10001561 var enum *EnumDescriptor
1562 if id, ok := obj.(*ImportedDescriptor); ok {
1563 // The enum type has been publicly imported.
1564 enum, _ = id.o.(*EnumDescriptor)
1565 } else {
1566 enum, _ = obj.(*EnumDescriptor)
1567 }
1568 if enum == nil {
1569 log.Printf("don't know how to generate constant for %s", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001570 continue
1571 }
David Symonds58a75832013-08-07 09:47:38 +10001572 def = g.DefaultPackageName(obj) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001573 }
1574 g.P(kind, fieldname, " ", typename, " = ", def)
David Symonds58a75832013-08-07 09:47:38 +10001575 g.file.addExport(message, constOrVarSymbol{fieldname, kind, ""})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001576 }
1577 g.P()
1578
David Symonds8bb32ca2012-06-28 10:22:09 -07001579 // Field getters
David Symonds381349d2012-09-18 15:11:46 +10001580 var getters []getterSymbol
David Symonds8bb32ca2012-06-28 10:22:09 -07001581 for _, field := range message.Field {
David Symonds8bb32ca2012-06-28 10:22:09 -07001582 fname := fieldNames[field]
1583 typename, _ := g.GoType(message, field)
David Symonds22e7eb42013-03-23 12:17:58 +11001584 mname := "Get" + fieldGetterNames[field]
David Symonds8bb32ca2012-06-28 10:22:09 -07001585 star := ""
1586 if needsStar(*field.Type) && typename[0] == '*' {
1587 typename = typename[1:]
1588 star = "*"
1589 }
David Symonds381349d2012-09-18 15:11:46 +10001590
1591 // Only export getter symbols for basic types,
David Symonds6eaeef12012-11-07 11:42:56 +11001592 // and for messages and enums in the same package.
1593 // Groups are not exported.
David Symonds381349d2012-09-18 15:11:46 +10001594 // Foreign types can't be hoisted through a public import because
1595 // the importer may not already be importing the defining .proto.
1596 // As an example, imagine we have an import tree like this:
1597 // A.proto -> B.proto -> C.proto
1598 // If A publicly imports B, we need to generate the getters from B in A's output,
1599 // but if one such getter returns something from C then we cannot do that
1600 // because A is not importing C already.
1601 var getter, genType bool
1602 switch *field.Type {
David Symonds6eaeef12012-11-07 11:42:56 +11001603 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1604 getter = false
1605 case descriptor.FieldDescriptorProto_TYPE_MESSAGE, descriptor.FieldDescriptorProto_TYPE_ENUM:
David Symonds381349d2012-09-18 15:11:46 +10001606 // Only export getter if its return type is in this package.
1607 getter = g.ObjectNamed(field.GetTypeName()).PackageName() == message.PackageName()
1608 genType = true
1609 default:
1610 getter = true
1611 }
1612 if getter {
1613 getters = append(getters, getterSymbol{
1614 name: mname,
1615 typ: typename,
1616 typeName: field.GetTypeName(),
1617 genType: genType,
1618 })
1619 }
1620
David Symonds22e7eb42013-03-23 12:17:58 +11001621 g.P("func (m *", ccTypeName, ") "+mname+"() "+typename+" {")
David Symonds8bb32ca2012-06-28 10:22:09 -07001622 g.In()
1623 def, hasDef := defNames[field]
1624 typeDefaultIsNil := false // whether this field type's default value is a literal nil unless specified
1625 switch *field.Type {
1626 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1627 typeDefaultIsNil = !hasDef
1628 case descriptor.FieldDescriptorProto_TYPE_GROUP, descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1629 typeDefaultIsNil = true
1630 }
David Symondsdb7a6872013-01-30 17:08:05 +11001631 if isRepeated(field) {
1632 typeDefaultIsNil = true
1633 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001634 if typeDefaultIsNil {
1635 // A bytes field with no explicit default needs less generated code,
David Symondsdb7a6872013-01-30 17:08:05 +11001636 // as does a message or group field, or a repeated field.
David Symonds22e7eb42013-03-23 12:17:58 +11001637 g.P("if m != nil {")
David Symonds8bb32ca2012-06-28 10:22:09 -07001638 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001639 g.P("return m." + fname)
David Symonds8bb32ca2012-06-28 10:22:09 -07001640 g.Out()
1641 g.P("}")
1642 g.P("return nil")
1643 g.Out()
1644 g.P("}")
1645 g.P()
1646 continue
1647 }
David Symonds22e7eb42013-03-23 12:17:58 +11001648 g.P("if m != nil && m." + fname + " != nil {")
David Symonds8bb32ca2012-06-28 10:22:09 -07001649 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001650 g.P("return " + star + "m." + fname)
David Symonds8bb32ca2012-06-28 10:22:09 -07001651 g.Out()
1652 g.P("}")
1653 if hasDef {
1654 if *field.Type != descriptor.FieldDescriptorProto_TYPE_BYTES {
1655 g.P("return " + def)
1656 } else {
1657 // The default is a []byte var.
1658 // Make a copy when returning it to be safe.
1659 g.P("return append([]byte(nil), ", def, "...)")
1660 }
1661 } else {
1662 switch *field.Type {
1663 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1664 g.P("return false")
1665 case descriptor.FieldDescriptorProto_TYPE_STRING:
1666 g.P(`return ""`)
1667 default:
1668 g.P("return 0")
1669 }
1670 }
1671 g.Out()
1672 g.P("}")
1673 g.P()
1674 }
1675
David Symonds6eaeef12012-11-07 11:42:56 +11001676 if !message.group {
1677 g.file.addExport(message, &messageSymbol{ccTypeName, hasExtensions, isMessageSet, getters})
1678 }
David Symonds381349d2012-09-18 15:11:46 +10001679
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001680 for _, ext := range message.ext {
1681 g.generateExtension(ext)
1682 }
David Symonds525838c2012-07-20 15:42:49 +10001683
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001684}
1685
1686func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001687 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001688
1689 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1690 field := ext.FieldDescriptorProto
1691 fieldType, wireType := g.GoType(ext.parent, field)
1692 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001693 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001694 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1695 // foreign extension type
1696 g.RecordTypeUse(*n)
1697 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001698
David Symonds20c73662012-01-20 07:32:21 +11001699 typeName := ext.TypeName()
1700
1701 // Special case for proto2 message sets: If this extension is extending
1702 // proto2_bridge.MessageSet, and its final name component is "message_set_extension",
1703 // then drop that last component.
1704 if extendedType == "*proto2_bridge.MessageSet" && typeName[len(typeName)-1] == "message_set_extension" {
1705 typeName = typeName[:len(typeName)-1]
1706 }
1707
David Symondsc057ad52012-02-11 15:43:42 +11001708 // For text formatting, the package must be exactly what the .proto file declares,
1709 // ignoring overrides such as the go_package option, and with no dot/underscore mapping.
1710 extName := strings.Join(typeName, ".")
1711 if g.file.Package != nil {
1712 extName = *g.file.Package + "." + extName
1713 }
1714
David Symonds11db5982012-08-15 10:54:05 +10001715 g.P("var ", ccTypeName, " = &", g.Pkg["proto"], ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001716 g.In()
1717 g.P("ExtendedType: (", extendedType, ")(nil),")
1718 g.P("ExtensionType: (", fieldType, ")(nil),")
1719 g.P("Field: ", field.Number, ",")
David Symondsc057ad52012-02-11 15:43:42 +11001720 g.P(`Name: "`, extName, `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001721 g.P("Tag: ", tag, ",")
1722
1723 g.Out()
1724 g.P("}")
1725 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001726
David Symonds58a75832013-08-07 09:47:38 +10001727 g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var", ""})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001728}
1729
1730func (g *Generator) generateInitFunction() {
1731 g.P("func init() {")
1732 g.In()
1733 for _, enum := range g.file.enum {
1734 g.generateEnumRegistration(enum)
1735 }
David Symondse37856c2011-06-22 12:52:53 +10001736 for _, d := range g.file.desc {
1737 for _, ext := range d.ext {
1738 g.generateExtensionRegistration(ext)
1739 }
1740 }
1741 for _, ext := range g.file.ext {
1742 g.generateExtensionRegistration(ext)
1743 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001744 g.Out()
1745 g.P("}")
1746}
1747
1748func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
David Symonds4decd802011-08-04 11:27:07 +10001749 // // We always print the full (proto-world) package name here.
David Symonds8bb32ca2012-06-28 10:22:09 -07001750 pkg := enum.File().GetPackage()
David Symonds4decd802011-08-04 11:27:07 +10001751 if pkg != "" {
1752 pkg += "."
1753 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001754 // The full type name
1755 typeName := enum.TypeName()
1756 // The full type name, CamelCased.
1757 ccTypeName := CamelCaseSlice(typeName)
David Symonds151cce02012-12-06 13:34:42 +11001758 g.P(g.Pkg["proto"]+".RegisterEnum(", strconv.Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001759}
1760
David Symondse37856c2011-06-22 12:52:53 +10001761func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
David Symonds11db5982012-08-15 10:54:05 +10001762 g.P(g.Pkg["proto"]+".RegisterExtension(", ext.DescName(), ")")
David Symondse37856c2011-06-22 12:52:53 +10001763}
1764
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001765// And now lots of helper functions.
1766
Rob Pike2c7bafc2010-06-10 16:07:14 -07001767// Is c an ASCII lower-case letter?
1768func isASCIILower(c byte) bool {
1769 return 'a' <= c && c <= 'z'
1770}
1771
1772// Is c an ASCII digit?
1773func isASCIIDigit(c byte) bool {
1774 return '0' <= c && c <= '9'
1775}
1776
1777// CamelCase returns the CamelCased name.
1778// If there is an interior underscore followed by a lower case letter,
1779// drop the underscore and convert the letter to upper case.
1780// There is a remote possibility of this rewrite causing a name collision,
1781// but it's so remote we're prepared to pretend it's nonexistent - since the
1782// C++ generator lowercases names, it's extremely unlikely to have two fields
1783// with different capitalizations.
David Symonds39034052013-08-19 17:11:31 +10001784// In short, _my_field_name_2 becomes XMyFieldName_2.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001785func CamelCase(s string) string {
1786 if s == "" {
1787 return ""
1788 }
1789 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001790 i := 0
1791 if s[0] == '_' {
1792 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001793 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001794 i++
1795 }
1796 // Invariant: if the next letter is lower case, it must be converted
1797 // to upper case.
1798 // That is, we process a word at a time, where words are marked by _ or
1799 // upper case letter. Digits are treated as words.
1800 for ; i < len(s); i++ {
1801 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001802 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1803 continue // Skip the underscore in s.
1804 }
1805 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001806 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001807 continue
1808 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001809 // Assume we have a letter now - if not, it's a bogus identifier.
1810 // The next word is a sequence of characters that must start upper case.
1811 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001812 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001813 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001814 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001815 // Accept lower case sequence that follows.
1816 for i+1 < len(s) && isASCIILower(s[i+1]) {
1817 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001818 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001819 }
1820 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001821 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001822}
1823
1824// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1825// be joined with "_".
1826func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1827
1828// dottedSlice turns a sliced name into a dotted name.
1829func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1830
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001831// Given a .proto file name, return the output name for the generated Go program.
1832func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001833 ext := path.Ext(name)
1834 if ext == ".proto" || ext == ".protodevel" {
1835 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001836 }
1837 return name + ".pb.go"
1838}
1839
1840// Is this field optional?
1841func isOptional(field *descriptor.FieldDescriptorProto) bool {
1842 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1843}
1844
1845// Is this field required?
1846func isRequired(field *descriptor.FieldDescriptorProto) bool {
1847 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1848}
1849
1850// Is this field repeated?
1851func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1852 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1853}
1854
David Symonds151cce02012-12-06 13:34:42 +11001855// badToUnderscore is the mapping function used to generate Go names from package names,
David Symonds162d0032012-06-28 09:44:46 -07001856// which can be dotted in the input .proto file. It replaces non-identifier characters such as
1857// dot or dash with underscore.
David Symonds151cce02012-12-06 13:34:42 +11001858func badToUnderscore(r rune) rune {
David Symonds162d0032012-06-28 09:44:46 -07001859 if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
1860 return r
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001861 }
David Symonds162d0032012-06-28 09:44:46 -07001862 return '_'
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001863}
Rob Pikec9e7d972010-06-10 10:30:22 -07001864
David Symonds151cce02012-12-06 13:34:42 +11001865// baseName returns the last path element of the name, with the last dotted suffix removed.
1866func baseName(name string) string {
Rob Pikec9e7d972010-06-10 10:30:22 -07001867 // First, find the last element
1868 if i := strings.LastIndex(name, "/"); i >= 0 {
1869 name = name[i+1:]
1870 }
1871 // Now drop the suffix
1872 if i := strings.LastIndex(name, "."); i >= 0 {
1873 name = name[0:i]
1874 }
1875 return name
1876}
David Symonds52925902013-07-26 19:20:20 +10001877
1878// The SourceCodeInfo message describes the location of elements of a parsed
1879// .proto file by way of a "path", which is a sequence of integers that
1880// describe the route from a FileDescriptorProto to the relevant submessage.
1881// The path alternates between a field number of a repeated field, and an index
1882// into that repeated field. The constants below define the field numbers that
1883// are used.
1884//
1885// See descriptor.proto for more information about this.
1886const (
1887 // tag numbers in FileDescriptorProto
1888 messagePath = 4 // message_type
1889 enumPath = 5 // enum_type
1890 // tag numbers in DescriptorProto
1891 messageFieldPath = 2 // field
1892 messageMessagePath = 3 // nested_type
1893 messageEnumPath = 4 // enum_type
1894 // tag numbers in EnumDescriptorProto
1895 enumValuePath = 2 // value
1896)