blob: 7fff0fa801455645bb4e48bb57002851cab2b001 [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 {
380 sym string
381 typ string // either "const" or "var"
382}
383
384func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
385 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
386}
387
David Symonds4decd802011-08-04 11:27:07 +1000388// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700389type Object interface {
390 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
391 TypeName() []string
David Symonds4decd802011-08-04 11:27:07 +1000392 File() *descriptor.FileDescriptorProto
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700393}
394
395// Each package name we generate must be unique. The package we're generating
David Symonds4decd802011-08-04 11:27:07 +1000396// gets its own name but every other package must have a unique name that does
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700397// not conflict in the code we generate. These names are chosen globally (although
398// they don't have to be, it simplifies things to do them globally).
399func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
400 s, ok := uniquePackageName[fd]
401 if !ok {
David Symondsd4661c52012-08-30 15:17:53 +1000402 log.Fatal("internal error: no package name defined for " + fd.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700403 }
404 return s
405}
406
407// Generator is the type whose methods generate the output, stored in the associated response structure.
408type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000409 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700410
411 Request *plugin.CodeGeneratorRequest // The input.
412 Response *plugin.CodeGeneratorResponse // The output.
413
David Symonds162d0032012-06-28 09:44:46 -0700414 Param map[string]string // Command-line parameters.
415 PackageImportPath string // Go import path of the package we're generating code for
416 ImportPrefix string // String to prefix to imported package file names.
417 ImportMap map[string]string // Mapping from import name to generated name
Rob Pikec9e7d972010-06-10 10:30:22 -0700418
David Symonds11db5982012-08-15 10:54:05 +1000419 Pkg map[string]string // The names under which we import support packages
Rob Pikec9e7d972010-06-10 10:30:22 -0700420
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700421 packageName string // What we're calling ourselves.
422 allFiles []*FileDescriptor // All files in the tree
423 genFiles []*FileDescriptor // Those files we will generate output for.
424 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000425 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700426 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
427 indent string
428}
429
430// New creates a new generator and allocates the request and response protobufs.
431func New() *Generator {
432 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000433 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100434 g.Request = new(plugin.CodeGeneratorRequest)
435 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700436 return g
437}
438
Rob Pikea17fdd92011-11-02 12:43:05 -0700439// Error reports a problem, including an error, and exits the program.
440func (g *Generator) Error(err error, msgs ...string) {
441 s := strings.Join(msgs, " ") + ":" + err.Error()
David Symondsd4661c52012-08-30 15:17:53 +1000442 log.Print("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700443 os.Exit(1)
444}
445
446// Fail reports a problem and exits the program.
447func (g *Generator) Fail(msgs ...string) {
448 s := strings.Join(msgs, " ")
David Symondsd4661c52012-08-30 15:17:53 +1000449 log.Print("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700450 os.Exit(1)
451}
452
Rob Pikec9e7d972010-06-10 10:30:22 -0700453// CommandLineParameters breaks the comma-separated list of key=value pairs
454// in the parameter (a member of the request protobuf) into a key/value map.
455// It then sets file name mappings defined by those entries.
456func (g *Generator) CommandLineParameters(parameter string) {
457 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000458 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700459 if i := strings.Index(p, "="); i < 0 {
460 g.Param[p] = ""
461 } else {
462 g.Param[p[0:i]] = p[i+1:]
463 }
464 }
465
466 g.ImportMap = make(map[string]string)
467 for k, v := range g.Param {
David Symonds162d0032012-06-28 09:44:46 -0700468 switch k {
469 case "import_prefix":
Rob Pikec9e7d972010-06-10 10:30:22 -0700470 g.ImportPrefix = v
David Symondse90896d2012-08-15 11:18:55 +1000471 case "import_path":
David Symonds162d0032012-06-28 09:44:46 -0700472 g.PackageImportPath = v
473 default:
474 if len(k) > 0 && k[0] == 'M' {
475 g.ImportMap[k[1:]] = v
476 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700477 }
478 }
479}
480
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700481// DefaultPackageName returns the package name printed for the object.
482// If its file is in a different package, it returns the package name we're using for this file, plus ".".
483// Otherwise it returns the empty string.
484func (g *Generator) DefaultPackageName(obj Object) string {
485 pkg := obj.PackageName()
486 if pkg == g.packageName {
487 return ""
488 }
489 return pkg + "."
490}
491
492// For each input file, the unique package name to use, underscored.
493var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pike3f6f2d82011-12-18 13:55:35 -0800494
Rob Pikec9e7d972010-06-10 10:30:22 -0700495// Package names already registered. Key is the name from the .proto file;
496// value is the name that appears in the generated code.
497var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700498
Rob Pikec9e7d972010-06-10 10:30:22 -0700499// Create and remember a guaranteed unique package name for this file descriptor.
500// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
501// has no file descriptor.
502func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000503 // Convert dots to underscores before finding a unique alias.
David Symonds151cce02012-12-06 13:34:42 +1100504 pkg = strings.Map(badToUnderscore, pkg)
David Symonds41389812011-07-19 14:57:40 +1000505
David Symonds79eae332010-10-16 11:33:20 +1100506 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700507 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100508 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700509 }
510 // Install it.
511 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700512 if f != nil {
513 uniquePackageName[f.FileDescriptorProto] = pkg
514 }
515 return pkg
516}
517
David Symonds162d0032012-06-28 09:44:46 -0700518var isGoKeyword = map[string]bool{
519 "break": true,
520 "case": true,
521 "chan": true,
522 "const": true,
523 "continue": true,
524 "default": true,
525 "else": true,
526 "defer": true,
527 "fallthrough": true,
528 "for": true,
529 "func": true,
530 "go": true,
531 "goto": true,
532 "if": true,
533 "import": true,
534 "interface": true,
535 "map": true,
536 "package": true,
537 "range": true,
538 "return": true,
539 "select": true,
540 "struct": true,
541 "switch": true,
542 "type": true,
543 "var": true,
544}
545
546// defaultGoPackage returns the package name to use,
547// derived from the import path of the package we're building code for.
548func (g *Generator) defaultGoPackage() string {
549 p := g.PackageImportPath
550 if i := strings.LastIndex(p, "/"); i >= 0 {
551 p = p[i+1:]
552 }
553 if p == "" {
554 return ""
555 }
556
David Symonds151cce02012-12-06 13:34:42 +1100557 p = strings.Map(badToUnderscore, p)
David Symonds162d0032012-06-28 09:44:46 -0700558 // Identifier must not be keyword: insert _.
559 if isGoKeyword[p] {
560 p = "_" + p
561 }
562 // Identifier must not begin with digit: insert _.
563 if r, _ := utf8.DecodeRuneInString(p); unicode.IsDigit(r) {
564 p = "_" + p
565 }
566 return p
567}
568
Rob Pikec9e7d972010-06-10 10:30:22 -0700569// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700570// The package name must agree across all files being generated.
571// It also defines unique package names for all imported files.
572func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700573 // Register the name for this package. It will be the first name
574 // registered so is guaranteed to be unmodified.
David Symonds162d0032012-06-28 09:44:46 -0700575 pkg, explicit := g.genFiles[0].goPackageName()
576
577 // Check all files for an explicit go_package option.
578 for _, f := range g.genFiles {
579 thisPkg, thisExplicit := f.goPackageName()
580 if thisExplicit {
581 if !explicit {
582 // Let this file's go_package option serve for all input files.
583 pkg, explicit = thisPkg, true
584 } else if thisPkg != pkg {
585 g.Fail("inconsistent package names:", thisPkg, pkg)
586 }
587 }
588 }
589
590 // If we don't have an explicit go_package option but we have an
591 // import path, use that.
592 if !explicit {
593 p := g.defaultGoPackage()
594 if p != "" {
595 pkg, explicit = p, true
596 }
597 }
598
599 // If there was no go_package and no import path to use,
600 // double-check that all the inputs have the same implicit
601 // Go package name.
602 if !explicit {
603 for _, f := range g.genFiles {
604 thisPkg, _ := f.goPackageName()
605 if thisPkg != pkg {
606 g.Fail("inconsistent package names:", thisPkg, pkg)
607 }
608 }
609 }
610
Rob Pikec9e7d972010-06-10 10:30:22 -0700611 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
David Symonds162d0032012-06-28 09:44:46 -0700612
David Symonds11db5982012-08-15 10:54:05 +1000613 // Register the support package names. They might collide with the
Rob Pikec9e7d972010-06-10 10:30:22 -0700614 // name of a package we import.
David Symonds11db5982012-08-15 10:54:05 +1000615 g.Pkg = map[string]string{
616 "json": RegisterUniquePackageName("json", nil),
617 "math": RegisterUniquePackageName("math", nil),
618 "proto": RegisterUniquePackageName("proto", nil),
619 }
David Symonds162d0032012-06-28 09:44:46 -0700620
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700621AllFiles:
622 for _, f := range g.allFiles {
623 for _, genf := range g.genFiles {
624 if f == genf {
625 // In this package already.
626 uniquePackageName[f.FileDescriptorProto] = g.packageName
627 continue AllFiles
628 }
629 }
David Symonds7d5c8242011-03-14 12:03:50 -0700630 // The file is a dependency, so we want to ignore its go_package option
631 // because that is only relevant for its specific generated output.
David Symonds8bb32ca2012-06-28 10:22:09 -0700632 pkg := f.GetPackage()
David Symonds7d5c8242011-03-14 12:03:50 -0700633 if pkg == "" {
David Symonds151cce02012-12-06 13:34:42 +1100634 pkg = baseName(*f.Name)
David Symonds7d5c8242011-03-14 12:03:50 -0700635 }
636 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700637 }
638}
639
640// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
641// and FileDescriptorProtos into file-referenced objects within the Generator.
642// It also creates the list of files to generate and so should be called before GenerateAllFiles.
643func (g *Generator) WrapTypes() {
644 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
645 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700646 // We must wrap the descriptors before we wrap the enums
647 descs := wrapDescriptors(f)
648 g.buildNestedDescriptors(descs)
649 enums := wrapEnumDescriptors(f, descs)
650 exts := wrapExtensions(f)
David Symonds4decd802011-08-04 11:27:07 +1000651 imps := wrapImported(f, g)
David Symonds52925902013-07-26 19:20:20 +1000652 fd := &FileDescriptor{
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700653 FileDescriptorProto: f,
654 desc: descs,
655 enum: enums,
656 ext: exts,
David Symonds4decd802011-08-04 11:27:07 +1000657 imp: imps,
David Symonds151cce02012-12-06 13:34:42 +1100658 exported: make(map[Object][]symbol),
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700659 }
David Symonds52925902013-07-26 19:20:20 +1000660 extractComments(fd)
661 g.allFiles[i] = fd
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700662 }
663
664 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
665FindFiles:
666 for i, fileName := range g.Request.FileToGenerate {
667 // Search the list. This algorithm is n^2 but n is tiny.
668 for _, file := range g.allFiles {
David Symonds8bb32ca2012-06-28 10:22:09 -0700669 if fileName == file.GetName() {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700670 g.genFiles[i] = file
671 continue FindFiles
672 }
673 }
674 g.Fail("could not find file named", fileName)
675 }
676 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
677}
678
679// Scan the descriptors in this file. For each one, build the slice of nested descriptors
680func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
681 for _, desc := range descs {
682 if len(desc.NestedType) != 0 {
683 desc.nested = make([]*Descriptor, len(desc.NestedType))
684 n := 0
685 for _, nest := range descs {
686 if nest.parent == desc {
687 desc.nested[n] = nest
688 n++
689 }
690 }
691 if n != len(desc.NestedType) {
David Symonds8bb32ca2012-06-28 10:22:09 -0700692 g.Fail("internal error: nesting failure for", desc.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700693 }
694 }
695 }
696}
697
David Symonds52925902013-07-26 19:20:20 +1000698// Construct the Descriptor
699func newDescriptor(desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto, index int) *Descriptor {
David Symonds69cffb22013-02-21 14:26:53 +1100700 d := &Descriptor{
701 common: common{file},
702 DescriptorProto: desc,
703 parent: parent,
704 index: index,
705 }
David Symonds52925902013-07-26 19:20:20 +1000706 if parent == nil {
707 d.path = fmt.Sprintf("%d,%d", messagePath, index)
708 } else {
709 d.path = fmt.Sprintf("%s,%d,%d", parent.path, messageMessagePath, index)
710 }
David Symonds6eaeef12012-11-07 11:42:56 +1100711
712 // The only way to distinguish a group from a message is whether
713 // the containing message has a TYPE_GROUP field that matches.
714 if parent != nil {
715 parts := d.TypeName()
716 if file.Package != nil {
717 parts = append([]string{*file.Package}, parts...)
718 }
719 exp := "." + strings.Join(parts, ".")
720 for _, field := range parent.Field {
721 if field.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP && field.GetTypeName() == exp {
722 d.group = true
723 break
724 }
725 }
726 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700727
728 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
729 for i, field := range desc.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000730 d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700731 }
732
David Symonds52925902013-07-26 19:20:20 +1000733 return d
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700734}
735
736// Return a slice of all the Descriptors defined within this file
737func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
738 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
David Symonds4de8f722012-09-26 13:51:38 +1000739 for i, desc := range file.MessageType {
740 sl = wrapThisDescriptor(sl, desc, nil, file, i)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700741 }
742 return sl
743}
744
745// Wrap this Descriptor, recursively
David Symonds4de8f722012-09-26 13:51:38 +1000746func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto, index int) []*Descriptor {
David Symonds52925902013-07-26 19:20:20 +1000747 sl = append(sl, newDescriptor(desc, parent, file, index))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700748 me := sl[len(sl)-1]
David Symonds69cffb22013-02-21 14:26:53 +1100749 for i, nested := range desc.NestedType {
750 sl = wrapThisDescriptor(sl, nested, me, file, i)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700751 }
752 return sl
753}
754
David Symonds52925902013-07-26 19:20:20 +1000755// Construct the EnumDescriptor
756func newEnumDescriptor(desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto, index int) *EnumDescriptor {
757 ed := &EnumDescriptor{
758 common: common{file},
759 EnumDescriptorProto: desc,
760 parent: parent,
761 }
762 if parent == nil {
763 ed.path = fmt.Sprintf("%d,%d", enumPath, index)
764 } else {
765 ed.path = fmt.Sprintf("%s,%d,%d", parent.path, messageEnumPath, index)
766 }
767 return ed
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700768}
769
770// Return a slice of all the EnumDescriptors defined within this file
771func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
772 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000773 // Top-level enums.
David Symonds52925902013-07-26 19:20:20 +1000774 for i, enum := range file.EnumType {
775 sl = append(sl, newEnumDescriptor(enum, nil, file, i))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700776 }
David Symonds5256cf62010-06-27 10:33:42 +1000777 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700778 for _, nested := range descs {
David Symonds52925902013-07-26 19:20:20 +1000779 for i, enum := range nested.EnumType {
780 sl = append(sl, newEnumDescriptor(enum, nested, file, i))
David Symonds5256cf62010-06-27 10:33:42 +1000781 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700782 }
783 return sl
784}
785
786// Return a slice of all the top-level ExtensionDescriptors defined within this file.
787func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
788 sl := make([]*ExtensionDescriptor, len(file.Extension))
789 for i, field := range file.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000790 sl[i] = &ExtensionDescriptor{common{file}, field, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700791 }
792 return sl
793}
794
David Symonds4decd802011-08-04 11:27:07 +1000795// Return a slice of all the types that are publicly imported into this file.
796func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
797 for _, index := range file.PublicDependency {
798 df := g.fileByName(file.Dependency[index])
799 for _, d := range df.desc {
800 sl = append(sl, &ImportedDescriptor{common{file}, d})
801 }
802 for _, e := range df.enum {
803 sl = append(sl, &ImportedDescriptor{common{file}, e})
804 }
805 for _, ext := range df.ext {
806 sl = append(sl, &ImportedDescriptor{common{file}, ext})
807 }
808 }
809 return
810}
811
David Symonds52925902013-07-26 19:20:20 +1000812func extractComments(file *FileDescriptor) {
813 file.comments = make(map[string]*descriptor.SourceCodeInfo_Location)
814 for _, loc := range file.GetSourceCodeInfo().GetLocation() {
815 if loc.LeadingComments == nil {
816 continue
817 }
818 var p []string
819 for _, n := range loc.Path {
820 p = append(p, strconv.Itoa(int(n)))
821 }
822 file.comments[strings.Join(p, ",")] = loc
823 }
824}
825
Rob Pikec9e7d972010-06-10 10:30:22 -0700826// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700827// The key names for the map come from the input data, which puts a period at the beginning.
828// It should be called after SetPackageNames and before GenerateAllFiles.
829func (g *Generator) BuildTypeNameMap() {
830 g.typeNameToObject = make(map[string]Object)
831 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700832 // The names in this loop are defined by the proto world, not us, so the
833 // package name may be empty. If so, the dotted package name of X will
834 // be ".X"; otherwise it will be ".pkg.X".
David Symonds8bb32ca2012-06-28 10:22:09 -0700835 dottedPkg := "." + f.GetPackage()
Rob Pikec9e7d972010-06-10 10:30:22 -0700836 if dottedPkg != "." {
837 dottedPkg += "."
838 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700839 for _, enum := range f.enum {
840 name := dottedPkg + dottedSlice(enum.TypeName())
841 g.typeNameToObject[name] = enum
842 }
843 for _, desc := range f.desc {
844 name := dottedPkg + dottedSlice(desc.TypeName())
845 g.typeNameToObject[name] = desc
846 }
847 }
848}
849
850// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
851// returns the descriptor for the message or enum with that name.
852func (g *Generator) ObjectNamed(typeName string) Object {
David Symonds4decd802011-08-04 11:27:07 +1000853 o, ok := g.typeNameToObject[typeName]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700854 if !ok {
855 g.Fail("can't find object with type", typeName)
856 }
David Symonds4decd802011-08-04 11:27:07 +1000857
858 // If the file of this object isn't a direct dependency of the current file,
859 // or in the current file, then this object has been publicly imported into
860 // a dependency of the current file.
861 // We should return the ImportedDescriptor object for it instead.
862 direct := *o.File().Name == *g.file.Name
863 if !direct {
864 for _, dep := range g.file.Dependency {
865 if *g.fileByName(dep).Name == *o.File().Name {
866 direct = true
867 break
868 }
869 }
870 }
871 if !direct {
872 found := false
873 Loop:
874 for _, dep := range g.file.Dependency {
875 df := g.fileByName(*g.fileByName(dep).Name)
876 for _, td := range df.imp {
877 if td.o == o {
878 // Found it!
879 o = td
880 found = true
881 break Loop
882 }
883 }
884 }
885 if !found {
886 log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
887 }
888 }
889
890 return o
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700891}
892
893// P prints the arguments to the generated output. It handles strings and int32s, plus
894// handling indirections because they may be *string, etc.
895func (g *Generator) P(str ...interface{}) {
896 g.WriteString(g.indent)
897 for _, v := range str {
898 switch s := v.(type) {
899 case string:
900 g.WriteString(s)
901 case *string:
902 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700903 case bool:
904 g.WriteString(fmt.Sprintf("%t", s))
905 case *bool:
906 g.WriteString(fmt.Sprintf("%t", *s))
David Symonds4de8f722012-09-26 13:51:38 +1000907 case int:
908 g.WriteString(fmt.Sprintf("%d", s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700909 case *int32:
910 g.WriteString(fmt.Sprintf("%d", *s))
David Symonds2ce8ed42013-06-20 13:22:17 +1000911 case *int64:
912 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700913 case float64:
914 g.WriteString(fmt.Sprintf("%g", s))
915 case *float64:
916 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700917 default:
918 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
919 }
920 }
921 g.WriteByte('\n')
922}
923
924// In Indents the output one tab stop.
925func (g *Generator) In() { g.indent += "\t" }
926
927// Out unindents the output one tab stop.
928func (g *Generator) Out() {
929 if len(g.indent) > 0 {
930 g.indent = g.indent[1:]
931 }
932}
933
934// GenerateAllFiles generates the output for all the files we're outputting.
935func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700936 // Initialize the plugins
937 for _, p := range plugins {
938 p.Init(g)
939 }
David Symonds31d58a22011-01-20 18:33:21 +1100940 // Generate the output. The generator runs for every file, even the files
941 // that we don't generate output for, so that we can collate the full list
942 // of exported symbols to support public imports.
943 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
944 for _, file := range g.genFiles {
945 genFileMap[file] = true
946 }
947 i := 0
948 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700949 g.Reset()
950 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100951 if _, ok := genFileMap[file]; !ok {
952 continue
953 }
David Symondsb0127532010-11-09 11:10:46 +1100954 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700955 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
956 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100957 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700958 }
959}
960
961// Run all the plugins associated with the file.
962func (g *Generator) runPlugins(file *FileDescriptor) {
963 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700964 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700965 }
966}
967
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700968// FileOf return the FileDescriptor for this FileDescriptorProto.
969func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
970 for _, file := range g.allFiles {
971 if file.FileDescriptorProto == fd {
972 return file
973 }
974 }
David Symonds8bb32ca2012-06-28 10:22:09 -0700975 g.Fail("could not find file in table:", fd.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700976 return nil
977}
978
979// Fill the response protocol buffer with the generated output for all the files we're
980// supposed to generate.
981func (g *Generator) generate(file *FileDescriptor) {
982 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000983 g.usedPackages = make(map[string]bool)
984
David Symonds4decd802011-08-04 11:27:07 +1000985 for _, td := range g.file.imp {
986 g.generateImported(td)
987 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700988 for _, enum := range g.file.enum {
989 g.generateEnum(enum)
990 }
991 for _, desc := range g.file.desc {
992 g.generateMessage(desc)
993 }
994 for _, ext := range g.file.ext {
995 g.generateExtension(ext)
996 }
997 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000998
Rob Pikec9e7d972010-06-10 10:30:22 -0700999 // Run the plugins before the imports so we know which imports are necessary.
1000 g.runPlugins(file)
1001
David Symondsf90e3382010-05-05 10:53:44 +10001002 // Generate header and imports last, though they appear first in the output.
1003 rem := g.Buffer
1004 g.Buffer = new(bytes.Buffer)
1005 g.generateHeader()
1006 g.generateImports()
1007 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +10001008
1009 // Reformat generated code.
1010 fset := token.NewFileSet()
1011 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
1012 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -07001013 g.Fail("bad Go source code was generated:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +10001014 return
1015 }
1016 g.Reset()
David Symonds29d5d392012-12-20 12:14:58 +11001017 err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(g, fset, ast)
David Symondsb1d55a02011-04-08 09:55:06 +10001018 if err != nil {
Rob Pikea17fdd92011-11-02 12:43:05 -07001019 g.Fail("generated Go source code could not be reformatted:", err.Error())
David Symondsb1d55a02011-04-08 09:55:06 +10001020 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001021}
1022
David Symonds62539862012-08-04 10:06:55 +10001023// Generate the header, including package definition
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001024func (g *Generator) generateHeader() {
David Symondsd4661c52012-08-30 15:17:53 +10001025 g.P("// Code generated by protoc-gen-go.")
1026 g.P("// source: ", *g.file.Name)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001027 g.P("// DO NOT EDIT!")
1028 g.P()
1029 g.P("package ", g.file.PackageName())
1030 g.P()
1031}
1032
David Symonds52925902013-07-26 19:20:20 +10001033// PrintComments prints any comments from the source .proto file.
1034// The path is a comma-separated list of integers.
1035// See descriptor.proto for its format.
1036func (g *Generator) PrintComments(path string) {
1037 if loc, ok := g.file.comments[path]; ok {
1038 text := strings.TrimSuffix(loc.GetLeadingComments(), "\n")
1039 for _, line := range strings.Split(text, "\n") {
1040 g.P("// ", strings.TrimPrefix(line, " "))
1041 }
1042 }
1043}
1044
David Symonds31d58a22011-01-20 18:33:21 +11001045func (g *Generator) fileByName(filename string) *FileDescriptor {
1046 for _, fd := range g.allFiles {
David Symonds8bb32ca2012-06-28 10:22:09 -07001047 if fd.GetName() == filename {
David Symonds31d58a22011-01-20 18:33:21 +11001048 return fd
1049 }
1050 }
1051 return nil
1052}
1053
David Symonds9685cb02012-02-13 08:10:34 +11001054// weak returns whether the ith import of the current file is a weak import.
1055func (g *Generator) weak(i int32) bool {
1056 for _, j := range g.file.WeakDependency {
1057 if j == i {
1058 return true
1059 }
1060 }
1061 return false
1062}
1063
David Symonds62539862012-08-04 10:06:55 +10001064// Generate the imports
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001065func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -07001066 // We almost always need a proto import. Rather than computing when we
1067 // do, which is tricky when there's a plugin, just import it and
Rob Pikea17fdd92011-11-02 12:43:05 -07001068 // reference it later. The same argument applies to the math package,
David Symonds11db5982012-08-15 10:54:05 +10001069 // for handling bit patterns for floating-point numbers, and to the
1070 // json package, for symbolic names of enum values for JSON marshaling.
David Symonds151cce02012-12-06 13:34:42 +11001071 g.P("import " + g.Pkg["proto"] + " " + strconv.Quote(g.ImportPrefix+"code.google.com/p/goprotobuf/proto"))
David Symonds11db5982012-08-15 10:54:05 +10001072 g.P("import " + g.Pkg["json"] + ` "encoding/json"`)
1073 g.P("import " + g.Pkg["math"] + ` "math"`)
David Symonds9685cb02012-02-13 08:10:34 +11001074 for i, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +11001075 fd := g.fileByName(s)
1076 // Do not import our own package.
1077 if fd.PackageName() == g.packageName {
1078 continue
1079 }
1080 filename := goFileName(s)
1081 if substitution, ok := g.ImportMap[s]; ok {
1082 filename = substitution
1083 }
1084 filename = g.ImportPrefix + filename
1085 if strings.HasSuffix(filename, ".go") {
1086 filename = filename[0 : len(filename)-3]
1087 }
David Symonds9685cb02012-02-13 08:10:34 +11001088 // Skip weak imports.
1089 if g.weak(int32(i)) {
David Symonds151cce02012-12-06 13:34:42 +11001090 g.P("// skipping weak import ", fd.PackageName(), " ", strconv.Quote(filename))
David Symonds9685cb02012-02-13 08:10:34 +11001091 continue
1092 }
David Symonds31d58a22011-01-20 18:33:21 +11001093 if _, ok := g.usedPackages[fd.PackageName()]; ok {
David Symonds151cce02012-12-06 13:34:42 +11001094 g.P("import ", fd.PackageName(), " ", strconv.Quote(filename))
David Symonds31d58a22011-01-20 18:33:21 +11001095 } else {
David Symonds3fa055f2011-05-05 15:19:04 -07001096 // TODO: Re-enable this when we are more feature-complete.
1097 // For instance, some protos use foreign field extensions, which we don't support.
1098 // Until then, this is just annoying spam.
1099 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
David Symonds151cce02012-12-06 13:34:42 +11001100 g.P("// discarding unused import ", fd.PackageName(), " ", strconv.Quote(filename))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001101 }
1102 }
1103 g.P()
1104 // TODO: may need to worry about uniqueness across plugins
1105 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -07001106 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001107 g.P()
1108 }
David Symonds62539862012-08-04 10:06:55 +10001109 g.P("// Reference proto, json, and math imports to suppress error if they are not otherwise used.")
David Symonds11db5982012-08-15 10:54:05 +10001110 g.P("var _ = ", g.Pkg["proto"], ".Marshal")
David Symondsdb7a6872013-01-30 17:08:05 +11001111 g.P("var _ = &", g.Pkg["json"], ".SyntaxError{}")
1112 g.P("var _ = ", g.Pkg["math"], ".Inf")
Rob Pikec9e7d972010-06-10 10:30:22 -07001113 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001114}
1115
David Symonds4decd802011-08-04 11:27:07 +10001116func (g *Generator) generateImported(id *ImportedDescriptor) {
David Symondsb2a00c82011-08-04 16:52:58 +10001117 // Don't generate public import symbols for files that we are generating
1118 // code for, since those symbols will already be in this package.
1119 // We can't simply avoid creating the ImportedDescriptor objects,
1120 // because g.genFiles isn't populated at that stage.
David Symonds4decd802011-08-04 11:27:07 +10001121 tn := id.TypeName()
1122 sn := tn[len(tn)-1]
David Symondsb2a00c82011-08-04 16:52:58 +10001123 df := g.FileOf(id.o.File())
1124 filename := *df.Name
1125 for _, fd := range g.genFiles {
1126 if *fd.Name == filename {
1127 g.P("// Ignoring public import of ", sn, " from ", filename)
1128 g.P()
1129 return
1130 }
1131 }
1132 g.P("// ", sn, " from public import ", filename)
1133 g.usedPackages[df.PackageName()] = true
David Symonds4decd802011-08-04 11:27:07 +10001134
David Symondsb2a00c82011-08-04 16:52:58 +10001135 for _, sym := range df.exported[id.o] {
1136 sym.GenerateAlias(g, df.PackageName())
1137 }
1138
1139 g.P()
David Symonds4decd802011-08-04 11:27:07 +10001140}
1141
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001142// Generate the enum definitions for this EnumDescriptor.
1143func (g *Generator) generateEnum(enum *EnumDescriptor) {
1144 // The full type name
1145 typeName := enum.TypeName()
1146 // The full type name, CamelCased.
1147 ccTypeName := CamelCaseSlice(typeName)
1148 ccPrefix := enum.prefix()
David Symonds52925902013-07-26 19:20:20 +10001149
1150 g.PrintComments(enum.path)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001151 g.P("type ", ccTypeName, " int32")
David Symondsb2a00c82011-08-04 16:52:58 +10001152 g.file.addExport(enum, enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001153 g.P("const (")
1154 g.In()
David Symonds52925902013-07-26 19:20:20 +10001155 for i, e := range enum.Value {
1156 g.PrintComments(fmt.Sprintf("%s,%d,%d", enum.path, enumValuePath, i))
1157
David Symonds31d58a22011-01-20 18:33:21 +11001158 name := ccPrefix + *e.Name
David Symondsdd9ca042011-08-29 16:07:16 +10001159 g.P(name, " ", ccTypeName, " = ", e.Number)
David Symondsb2a00c82011-08-04 16:52:58 +10001160 g.file.addExport(enum, constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001161 }
1162 g.Out()
1163 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +11001164 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001165 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -07001166 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001167 for _, e := range enum.Value {
1168 duplicate := ""
1169 if _, present := generated[*e.Number]; present {
1170 duplicate = "// Duplicate value: "
1171 }
David Symonds151cce02012-12-06 13:34:42 +11001172 g.P(duplicate, e.Number, ": ", strconv.Quote(*e.Name), ",")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001173 generated[*e.Number] = true
1174 }
1175 g.Out()
1176 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +11001177 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001178 g.In()
1179 for _, e := range enum.Value {
David Symonds151cce02012-12-06 13:34:42 +11001180 g.P(strconv.Quote(*e.Name), ": ", e.Number, ",")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001181 }
1182 g.Out()
1183 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +11001184
David Symondsefeca9a2012-05-08 10:36:04 +10001185 g.P("func (x ", ccTypeName, ") Enum() *", ccTypeName, " {")
1186 g.In()
1187 g.P("p := new(", ccTypeName, ")")
1188 g.P("*p = x")
1189 g.P("return p")
1190 g.Out()
1191 g.P("}")
1192
David Symonds940b9612011-04-01 10:45:23 +11001193 g.P("func (x ", ccTypeName, ") String() string {")
1194 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001195 g.P("return ", g.Pkg["proto"], ".EnumName(", ccTypeName, "_name, int32(x))")
David Symonds940b9612011-04-01 10:45:23 +11001196 g.Out()
1197 g.P("}")
1198
David Symonds62539862012-08-04 10:06:55 +10001199 g.P("func (x ", ccTypeName, ") MarshalJSON() ([]byte, error) {")
1200 g.In()
1201 g.P("return json.Marshal(x.String())")
1202 g.Out()
1203 g.P("}")
1204
1205 g.P("func (x *", ccTypeName, ") UnmarshalJSON(data []byte) error {")
1206 g.In()
David Symonds11db5982012-08-15 10:54:05 +10001207 g.P("value, err := ", g.Pkg["proto"], ".UnmarshalJSONEnum(", ccTypeName, `_value, data, "`, ccTypeName, `")`)
David Symonds62539862012-08-04 10:06:55 +10001208 g.P("if err != nil {")
1209 g.In()
1210 g.P("return err")
1211 g.Out()
1212 g.P("}")
1213 g.P("*x = ", ccTypeName, "(value)")
1214 g.P("return nil")
1215 g.Out()
1216 g.P("}")
1217
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001218 g.P()
1219}
1220
David Symonds8935abf2011-07-04 15:53:16 +10001221// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001222// identifies details of the field for the protocol buffer marshaling and unmarshaling
1223// code. The fields are:
1224// wire encoding
1225// protocol tag number
1226// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +11001227// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001228// name= the original declared name
1229// enum= the name of the enum type if it is an enum-typed field.
1230// def= string representation of the default value, if any.
1231// The default value must be in a representation that can be used at run-time
1232// to generate the default value. Thus bools become 0 and 1, for instance.
1233func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
1234 optrepreq := ""
1235 switch {
1236 case isOptional(field):
1237 optrepreq = "opt"
1238 case isRequired(field):
1239 optrepreq = "req"
1240 case isRepeated(field):
1241 optrepreq = "rep"
1242 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001243 defaultValue := field.GetDefaultValue()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001244 if defaultValue != "" {
1245 switch *field.Type {
1246 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1247 if defaultValue == "true" {
1248 defaultValue = "1"
1249 } else {
1250 defaultValue = "0"
1251 }
1252 case descriptor.FieldDescriptorProto_TYPE_STRING,
1253 descriptor.FieldDescriptorProto_TYPE_BYTES:
David Symondsb79d99b2011-08-29 16:38:49 +10001254 // Nothing to do. Quoting is done for the whole tag.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001255 case descriptor.FieldDescriptorProto_TYPE_ENUM:
1256 // For enums we need to provide the integer constant.
David Symonds8bb32ca2012-06-28 10:22:09 -07001257 obj := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001258 enum, ok := obj.(*EnumDescriptor)
1259 if !ok {
1260 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
1261 }
1262 defaultValue = enum.integerValueAsString(defaultValue)
1263 }
1264 defaultValue = ",def=" + defaultValue
1265 }
1266 enum := ""
1267 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
David Symonds4decd802011-08-04 11:27:07 +10001268 // We avoid using obj.PackageName(), because we want to use the
1269 // original (proto-world) package name.
David Symonds8bb32ca2012-06-28 10:22:09 -07001270 obj := g.ObjectNamed(field.GetTypeName())
David Symonds4decd802011-08-04 11:27:07 +10001271 enum = ",enum="
David Symonds8bb32ca2012-06-28 10:22:09 -07001272 if pkg := obj.File().GetPackage(); pkg != "" {
David Symonds4decd802011-08-04 11:27:07 +10001273 enum += pkg + "."
1274 }
1275 enum += CamelCaseSlice(obj.TypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001276 }
David Symonds5b7775e2010-12-01 10:09:04 +11001277 packed := ""
David Symonds8bb32ca2012-06-28 10:22:09 -07001278 if field.Options != nil && field.Options.GetPacked() {
David Symonds5b7775e2010-12-01 10:09:04 +11001279 packed = ",packed"
1280 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001281 fieldName := field.GetName()
David Symondse37856c2011-06-22 12:52:53 +10001282 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +10001283 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
1284 // We must use the type name for groups instead of
1285 // the field name to preserve capitalization.
1286 // type_name in FieldDescriptorProto is fully-qualified,
1287 // but we only want the local part.
1288 name = *field.TypeName
1289 if i := strings.LastIndex(name, "."); i >= 0 {
1290 name = name[i+1:]
1291 }
David Symondse37856c2011-06-22 12:52:53 +10001292 }
1293 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001294 name = ""
1295 } else {
1296 name = ",name=" + name
1297 }
David Symonds151cce02012-12-06 13:34:42 +11001298 return strconv.Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001299 wiretype,
David Symonds8bb32ca2012-06-28 10:22:09 -07001300 field.GetNumber(),
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001301 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +11001302 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001303 name,
1304 enum,
1305 defaultValue))
1306}
1307
1308func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
1309 switch typ {
1310 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1311 return false
1312 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1313 return false
1314 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1315 return false
1316 }
1317 return true
1318}
1319
1320// TypeName is the printed name appropriate for an item. If the object is in the current file,
1321// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -07001322// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001323// package name followed by the item name.
1324// The result always has an initial capital.
1325func (g *Generator) TypeName(obj Object) string {
1326 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
1327}
1328
1329// TypeNameWithPackage is like TypeName, but always includes the package
1330// name even if the object is in our own package.
1331func (g *Generator) TypeNameWithPackage(obj Object) string {
1332 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
1333}
1334
1335// GoType returns a string representing the type name, and the wire type
1336func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
1337 // TODO: Options.
1338 switch *field.Type {
1339 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
1340 typ, wire = "float64", "fixed64"
1341 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
1342 typ, wire = "float32", "fixed32"
1343 case descriptor.FieldDescriptorProto_TYPE_INT64:
1344 typ, wire = "int64", "varint"
1345 case descriptor.FieldDescriptorProto_TYPE_UINT64:
1346 typ, wire = "uint64", "varint"
1347 case descriptor.FieldDescriptorProto_TYPE_INT32:
1348 typ, wire = "int32", "varint"
1349 case descriptor.FieldDescriptorProto_TYPE_UINT32:
1350 typ, wire = "uint32", "varint"
1351 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
1352 typ, wire = "uint64", "fixed64"
1353 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
1354 typ, wire = "uint32", "fixed32"
1355 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1356 typ, wire = "bool", "varint"
1357 case descriptor.FieldDescriptorProto_TYPE_STRING:
1358 typ, wire = "string", "bytes"
1359 case descriptor.FieldDescriptorProto_TYPE_GROUP:
David Symonds8bb32ca2012-06-28 10:22:09 -07001360 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001361 typ, wire = "*"+g.TypeName(desc), "group"
1362 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
David Symonds8bb32ca2012-06-28 10:22:09 -07001363 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001364 typ, wire = "*"+g.TypeName(desc), "bytes"
1365 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1366 typ, wire = "[]byte", "bytes"
1367 case descriptor.FieldDescriptorProto_TYPE_ENUM:
David Symonds8bb32ca2012-06-28 10:22:09 -07001368 desc := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001369 typ, wire = g.TypeName(desc), "varint"
1370 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
1371 typ, wire = "int32", "fixed32"
1372 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1373 typ, wire = "int64", "fixed64"
1374 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1375 typ, wire = "int32", "zigzag32"
1376 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1377 typ, wire = "int64", "zigzag64"
1378 default:
David Symonds8bb32ca2012-06-28 10:22:09 -07001379 g.Fail("unknown type for", field.GetName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001380 }
1381 if isRepeated(field) {
1382 typ = "[]" + typ
1383 } else if needsStar(*field.Type) {
1384 typ = "*" + typ
1385 }
1386 return
1387}
1388
David Symondsf90e3382010-05-05 10:53:44 +10001389func (g *Generator) RecordTypeUse(t string) {
1390 if obj, ok := g.typeNameToObject[t]; ok {
David Symonds4decd802011-08-04 11:27:07 +10001391 // Call ObjectNamed to get the true object to record the use.
1392 obj = g.ObjectNamed(t)
David Symondsf90e3382010-05-05 10:53:44 +10001393 g.usedPackages[obj.PackageName()] = true
1394 }
1395}
1396
David Symonds3ce53032012-02-13 10:55:03 +11001397// Method names that may be generated. Fields with these names get an
1398// underscore appended.
1399var methodNames = [...]string{
1400 "Reset",
1401 "String",
David Symonds9f60f432012-06-14 09:45:25 +10001402 "ProtoMessage",
David Symonds3ce53032012-02-13 10:55:03 +11001403 "Marshal",
1404 "Unmarshal",
1405 "ExtensionRangeArray",
1406 "ExtensionMap",
David Symonds4de8f722012-09-26 13:51:38 +10001407 "Descriptor",
David Symonds3ce53032012-02-13 10:55:03 +11001408}
1409
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001410// Generate the type and default constant definitions for this Descriptor.
1411func (g *Generator) generateMessage(message *Descriptor) {
1412 // The full type name
1413 typeName := message.TypeName()
1414 // The full type name, CamelCased.
1415 ccTypeName := CamelCaseSlice(typeName)
1416
David Symonds3ce53032012-02-13 10:55:03 +11001417 usedNames := make(map[string]bool)
1418 for _, n := range methodNames {
1419 usedNames[n] = true
1420 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001421 fieldNames := make(map[*descriptor.FieldDescriptorProto]string)
David Symonds22e7eb42013-03-23 12:17:58 +11001422 fieldGetterNames := make(map[*descriptor.FieldDescriptorProto]string)
David Symonds52925902013-07-26 19:20:20 +10001423
1424 g.PrintComments(message.path)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001425 g.P("type ", ccTypeName, " struct {")
1426 g.In()
David Symonds6eaeef12012-11-07 11:42:56 +11001427
David Symonds52925902013-07-26 19:20:20 +10001428 for i, field := range message.Field {
1429 g.PrintComments(fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i))
1430
David Symonds22e7eb42013-03-23 12:17:58 +11001431 fieldName := CamelCase(*field.Name)
1432 for usedNames[fieldName] {
1433 fieldName += "_"
David Symonds3ce53032012-02-13 10:55:03 +11001434 }
David Symonds22e7eb42013-03-23 12:17:58 +11001435 fieldGetterName := fieldName
1436 usedNames[fieldName] = true
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001437 typename, wiretype := g.GoType(message, field)
David Symondsb2a00c82011-08-04 16:52:58 +10001438 jsonName := *field.Name
David Symonds6eaeef12012-11-07 11:42:56 +11001439 tag := fmt.Sprintf("protobuf:%s json:%q", g.goTag(field, wiretype), jsonName+",omitempty")
David Symonds22e7eb42013-03-23 12:17:58 +11001440 fieldNames[field] = fieldName
1441 fieldGetterNames[field] = fieldGetterName
1442 g.P(fieldName, "\t", typename, "\t`", tag, "`")
David Symonds8bb32ca2012-06-28 10:22:09 -07001443 g.RecordTypeUse(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001444 }
1445 if len(message.ExtensionRange) > 0 {
David Symonds11db5982012-08-15 10:54:05 +10001446 g.P("XXX_extensions\t\tmap[int32]", g.Pkg["proto"], ".Extension `json:\"-\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001447 }
David Symonds22e7eb42013-03-23 12:17:58 +11001448 g.P("XXX_unrecognized\t[]byte `json:\"-\"`")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001449 g.Out()
1450 g.P("}")
1451
David Symonds9f60f432012-06-14 09:45:25 +10001452 // Reset, String and ProtoMessage methods.
David Symonds22e7eb42013-03-23 12:17:58 +11001453 g.P("func (m *", ccTypeName, ") Reset() { *m = ", ccTypeName, "{} }")
David Symonds4f4ce3d2013-05-03 08:51:59 +10001454 g.P("func (m *", ccTypeName, ") String() string { return ", g.Pkg["proto"], ".CompactTextString(m) }")
1455 g.P("func (*", ccTypeName, ") ProtoMessage() {}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001456
1457 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001458 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001459 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001460 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001461 // message_set_wire_format only makes sense when extensions are defined.
David Symonds8bb32ca2012-06-28 10:22:09 -07001462 if opts := message.Options; opts != nil && opts.GetMessageSetWireFormat() {
David Symonds31d58a22011-01-20 18:33:21 +11001463 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001464 g.P()
David Symonds22e7eb42013-03-23 12:17:58 +11001465 g.P("func (m *", ccTypeName, ") Marshal() ([]byte, error) {")
David Symonds4fee3b12010-11-11 10:00:13 +11001466 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001467 g.P("return ", g.Pkg["proto"], ".MarshalMessageSet(m.ExtensionMap())")
David Symonds4fee3b12010-11-11 10:00:13 +11001468 g.Out()
1469 g.P("}")
David Symonds22e7eb42013-03-23 12:17:58 +11001470 g.P("func (m *", ccTypeName, ") Unmarshal(buf []byte) error {")
David Symonds4fee3b12010-11-11 10:00:13 +11001471 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001472 g.P("return ", g.Pkg["proto"], ".UnmarshalMessageSet(buf, m.ExtensionMap())")
David Symonds4fee3b12010-11-11 10:00:13 +11001473 g.Out()
1474 g.P("}")
1475 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
David Symonds11db5982012-08-15 10:54:05 +10001476 g.P("var _ ", g.Pkg["proto"], ".Marshaler = (*", ccTypeName, ")(nil)")
1477 g.P("var _ ", g.Pkg["proto"], ".Unmarshaler = (*", ccTypeName, ")(nil)")
David Symonds4fee3b12010-11-11 10:00:13 +11001478 }
1479
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001480 g.P()
David Symonds11db5982012-08-15 10:54:05 +10001481 g.P("var extRange_", ccTypeName, " = []", g.Pkg["proto"], ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001482 g.In()
1483 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001484 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
David Symonds1d72f7a2011-08-19 18:28:52 +10001485 g.P("{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001486 }
1487 g.Out()
1488 g.P("}")
David Symonds11db5982012-08-15 10:54:05 +10001489 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.Pkg["proto"], ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001490 g.In()
1491 g.P("return extRange_", ccTypeName)
1492 g.Out()
1493 g.P("}")
David Symonds22e7eb42013-03-23 12:17:58 +11001494 g.P("func (m *", ccTypeName, ") ExtensionMap() map[int32]", g.Pkg["proto"], ".Extension {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001495 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001496 g.P("if m.XXX_extensions == nil {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001497 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001498 g.P("m.XXX_extensions = make(map[int32]", g.Pkg["proto"], ".Extension)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001499 g.Out()
1500 g.P("}")
David Symonds22e7eb42013-03-23 12:17:58 +11001501 g.P("return m.XXX_extensions")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001502 g.Out()
1503 g.P("}")
1504 }
1505
1506 // Default constants
David Symonds8bb32ca2012-06-28 10:22:09 -07001507 defNames := make(map[*descriptor.FieldDescriptorProto]string)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001508 for _, field := range message.Field {
David Symonds8bb32ca2012-06-28 10:22:09 -07001509 def := field.GetDefaultValue()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001510 if def == "" {
1511 continue
1512 }
1513 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
David Symonds8bb32ca2012-06-28 10:22:09 -07001514 defNames[field] = fieldname
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001515 typename, _ := g.GoType(message, field)
1516 if typename[0] == '*' {
1517 typename = typename[1:]
1518 }
1519 kind := "const "
1520 switch {
1521 case typename == "bool":
1522 case typename == "string":
David Symonds151cce02012-12-06 13:34:42 +11001523 def = strconv.Quote(def)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001524 case typename == "[]byte":
David Symonds151cce02012-12-06 13:34:42 +11001525 def = "[]byte(" + strconv.Quote(def) + ")"
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001526 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001527 case def == "inf", def == "-inf", def == "nan":
1528 // These names are known to, and defined by, the protocol language.
1529 switch def {
1530 case "inf":
1531 def = "math.Inf(1)"
1532 case "-inf":
1533 def = "math.Inf(-1)"
1534 case "nan":
1535 def = "math.NaN()"
1536 }
1537 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1538 def = "float32(" + def + ")"
1539 }
1540 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001541 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1542 // Must be an enum. Need to construct the prefixed name.
David Symonds8bb32ca2012-06-28 10:22:09 -07001543 obj := g.ObjectNamed(field.GetTypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001544 enum, ok := obj.(*EnumDescriptor)
1545 if !ok {
David Symondsd4661c52012-08-30 15:17:53 +10001546 log.Print("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001547 continue
1548 }
Rob Pike87af39e2010-07-19 10:48:02 -07001549 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001550 }
1551 g.P(kind, fieldname, " ", typename, " = ", def)
David Symondsb2a00c82011-08-04 16:52:58 +10001552 g.file.addExport(message, constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001553 }
1554 g.P()
1555
David Symonds8bb32ca2012-06-28 10:22:09 -07001556 // Field getters
David Symonds381349d2012-09-18 15:11:46 +10001557 var getters []getterSymbol
David Symonds8bb32ca2012-06-28 10:22:09 -07001558 for _, field := range message.Field {
David Symonds8bb32ca2012-06-28 10:22:09 -07001559 fname := fieldNames[field]
1560 typename, _ := g.GoType(message, field)
David Symonds22e7eb42013-03-23 12:17:58 +11001561 mname := "Get" + fieldGetterNames[field]
David Symonds8bb32ca2012-06-28 10:22:09 -07001562 star := ""
1563 if needsStar(*field.Type) && typename[0] == '*' {
1564 typename = typename[1:]
1565 star = "*"
1566 }
David Symonds381349d2012-09-18 15:11:46 +10001567
1568 // Only export getter symbols for basic types,
David Symonds6eaeef12012-11-07 11:42:56 +11001569 // and for messages and enums in the same package.
1570 // Groups are not exported.
David Symonds381349d2012-09-18 15:11:46 +10001571 // Foreign types can't be hoisted through a public import because
1572 // the importer may not already be importing the defining .proto.
1573 // As an example, imagine we have an import tree like this:
1574 // A.proto -> B.proto -> C.proto
1575 // If A publicly imports B, we need to generate the getters from B in A's output,
1576 // but if one such getter returns something from C then we cannot do that
1577 // because A is not importing C already.
1578 var getter, genType bool
1579 switch *field.Type {
David Symonds6eaeef12012-11-07 11:42:56 +11001580 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1581 getter = false
1582 case descriptor.FieldDescriptorProto_TYPE_MESSAGE, descriptor.FieldDescriptorProto_TYPE_ENUM:
David Symonds381349d2012-09-18 15:11:46 +10001583 // Only export getter if its return type is in this package.
1584 getter = g.ObjectNamed(field.GetTypeName()).PackageName() == message.PackageName()
1585 genType = true
1586 default:
1587 getter = true
1588 }
1589 if getter {
1590 getters = append(getters, getterSymbol{
1591 name: mname,
1592 typ: typename,
1593 typeName: field.GetTypeName(),
1594 genType: genType,
1595 })
1596 }
1597
David Symonds22e7eb42013-03-23 12:17:58 +11001598 g.P("func (m *", ccTypeName, ") "+mname+"() "+typename+" {")
David Symonds8bb32ca2012-06-28 10:22:09 -07001599 g.In()
1600 def, hasDef := defNames[field]
1601 typeDefaultIsNil := false // whether this field type's default value is a literal nil unless specified
1602 switch *field.Type {
1603 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1604 typeDefaultIsNil = !hasDef
1605 case descriptor.FieldDescriptorProto_TYPE_GROUP, descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1606 typeDefaultIsNil = true
1607 }
David Symondsdb7a6872013-01-30 17:08:05 +11001608 if isRepeated(field) {
1609 typeDefaultIsNil = true
1610 }
David Symonds8bb32ca2012-06-28 10:22:09 -07001611 if typeDefaultIsNil {
1612 // A bytes field with no explicit default needs less generated code,
David Symondsdb7a6872013-01-30 17:08:05 +11001613 // as does a message or group field, or a repeated field.
David Symonds22e7eb42013-03-23 12:17:58 +11001614 g.P("if m != nil {")
David Symonds8bb32ca2012-06-28 10:22:09 -07001615 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001616 g.P("return m." + fname)
David Symonds8bb32ca2012-06-28 10:22:09 -07001617 g.Out()
1618 g.P("}")
1619 g.P("return nil")
1620 g.Out()
1621 g.P("}")
1622 g.P()
1623 continue
1624 }
David Symonds22e7eb42013-03-23 12:17:58 +11001625 g.P("if m != nil && m." + fname + " != nil {")
David Symonds8bb32ca2012-06-28 10:22:09 -07001626 g.In()
David Symonds22e7eb42013-03-23 12:17:58 +11001627 g.P("return " + star + "m." + fname)
David Symonds8bb32ca2012-06-28 10:22:09 -07001628 g.Out()
1629 g.P("}")
1630 if hasDef {
1631 if *field.Type != descriptor.FieldDescriptorProto_TYPE_BYTES {
1632 g.P("return " + def)
1633 } else {
1634 // The default is a []byte var.
1635 // Make a copy when returning it to be safe.
1636 g.P("return append([]byte(nil), ", def, "...)")
1637 }
1638 } else {
1639 switch *field.Type {
1640 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1641 g.P("return false")
1642 case descriptor.FieldDescriptorProto_TYPE_STRING:
1643 g.P(`return ""`)
1644 default:
1645 g.P("return 0")
1646 }
1647 }
1648 g.Out()
1649 g.P("}")
1650 g.P()
1651 }
1652
David Symonds6eaeef12012-11-07 11:42:56 +11001653 if !message.group {
1654 g.file.addExport(message, &messageSymbol{ccTypeName, hasExtensions, isMessageSet, getters})
1655 }
David Symonds381349d2012-09-18 15:11:46 +10001656
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001657 for _, ext := range message.ext {
1658 g.generateExtension(ext)
1659 }
David Symonds525838c2012-07-20 15:42:49 +10001660
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001661}
1662
1663func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001664 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001665
1666 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1667 field := ext.FieldDescriptorProto
1668 fieldType, wireType := g.GoType(ext.parent, field)
1669 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001670 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001671 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1672 // foreign extension type
1673 g.RecordTypeUse(*n)
1674 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001675
David Symonds20c73662012-01-20 07:32:21 +11001676 typeName := ext.TypeName()
1677
1678 // Special case for proto2 message sets: If this extension is extending
1679 // proto2_bridge.MessageSet, and its final name component is "message_set_extension",
1680 // then drop that last component.
1681 if extendedType == "*proto2_bridge.MessageSet" && typeName[len(typeName)-1] == "message_set_extension" {
1682 typeName = typeName[:len(typeName)-1]
1683 }
1684
David Symondsc057ad52012-02-11 15:43:42 +11001685 // For text formatting, the package must be exactly what the .proto file declares,
1686 // ignoring overrides such as the go_package option, and with no dot/underscore mapping.
1687 extName := strings.Join(typeName, ".")
1688 if g.file.Package != nil {
1689 extName = *g.file.Package + "." + extName
1690 }
1691
David Symonds11db5982012-08-15 10:54:05 +10001692 g.P("var ", ccTypeName, " = &", g.Pkg["proto"], ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001693 g.In()
1694 g.P("ExtendedType: (", extendedType, ")(nil),")
1695 g.P("ExtensionType: (", fieldType, ")(nil),")
1696 g.P("Field: ", field.Number, ",")
David Symondsc057ad52012-02-11 15:43:42 +11001697 g.P(`Name: "`, extName, `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001698 g.P("Tag: ", tag, ",")
1699
1700 g.Out()
1701 g.P("}")
1702 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001703
David Symondsb2a00c82011-08-04 16:52:58 +10001704 g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001705}
1706
1707func (g *Generator) generateInitFunction() {
1708 g.P("func init() {")
1709 g.In()
1710 for _, enum := range g.file.enum {
1711 g.generateEnumRegistration(enum)
1712 }
David Symondse37856c2011-06-22 12:52:53 +10001713 for _, d := range g.file.desc {
1714 for _, ext := range d.ext {
1715 g.generateExtensionRegistration(ext)
1716 }
1717 }
1718 for _, ext := range g.file.ext {
1719 g.generateExtensionRegistration(ext)
1720 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001721 g.Out()
1722 g.P("}")
1723}
1724
1725func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
David Symonds4decd802011-08-04 11:27:07 +10001726 // // We always print the full (proto-world) package name here.
David Symonds8bb32ca2012-06-28 10:22:09 -07001727 pkg := enum.File().GetPackage()
David Symonds4decd802011-08-04 11:27:07 +10001728 if pkg != "" {
1729 pkg += "."
1730 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001731 // The full type name
1732 typeName := enum.TypeName()
1733 // The full type name, CamelCased.
1734 ccTypeName := CamelCaseSlice(typeName)
David Symonds151cce02012-12-06 13:34:42 +11001735 g.P(g.Pkg["proto"]+".RegisterEnum(", strconv.Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001736}
1737
David Symondse37856c2011-06-22 12:52:53 +10001738func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
David Symonds11db5982012-08-15 10:54:05 +10001739 g.P(g.Pkg["proto"]+".RegisterExtension(", ext.DescName(), ")")
David Symondse37856c2011-06-22 12:52:53 +10001740}
1741
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001742// And now lots of helper functions.
1743
Rob Pike2c7bafc2010-06-10 16:07:14 -07001744// Is c an ASCII lower-case letter?
1745func isASCIILower(c byte) bool {
1746 return 'a' <= c && c <= 'z'
1747}
1748
1749// Is c an ASCII digit?
1750func isASCIIDigit(c byte) bool {
1751 return '0' <= c && c <= '9'
1752}
1753
1754// CamelCase returns the CamelCased name.
1755// If there is an interior underscore followed by a lower case letter,
1756// drop the underscore and convert the letter to upper case.
1757// There is a remote possibility of this rewrite causing a name collision,
1758// but it's so remote we're prepared to pretend it's nonexistent - since the
1759// C++ generator lowercases names, it's extremely unlikely to have two fields
1760// with different capitalizations.
1761// In short, _my_field_name_2 becomes XMyFieldName2.
1762func CamelCase(s string) string {
1763 if s == "" {
1764 return ""
1765 }
1766 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001767 i := 0
1768 if s[0] == '_' {
1769 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001770 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001771 i++
1772 }
1773 // Invariant: if the next letter is lower case, it must be converted
1774 // to upper case.
1775 // That is, we process a word at a time, where words are marked by _ or
1776 // upper case letter. Digits are treated as words.
1777 for ; i < len(s); i++ {
1778 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001779 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1780 continue // Skip the underscore in s.
1781 }
1782 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001783 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001784 continue
1785 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001786 // Assume we have a letter now - if not, it's a bogus identifier.
1787 // The next word is a sequence of characters that must start upper case.
1788 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001789 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001790 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001791 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001792 // Accept lower case sequence that follows.
1793 for i+1 < len(s) && isASCIILower(s[i+1]) {
1794 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001795 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001796 }
1797 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001798 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001799}
1800
1801// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1802// be joined with "_".
1803func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1804
1805// dottedSlice turns a sliced name into a dotted name.
1806func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1807
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001808// Given a .proto file name, return the output name for the generated Go program.
1809func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001810 ext := path.Ext(name)
1811 if ext == ".proto" || ext == ".protodevel" {
1812 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001813 }
1814 return name + ".pb.go"
1815}
1816
1817// Is this field optional?
1818func isOptional(field *descriptor.FieldDescriptorProto) bool {
1819 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1820}
1821
1822// Is this field required?
1823func isRequired(field *descriptor.FieldDescriptorProto) bool {
1824 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1825}
1826
1827// Is this field repeated?
1828func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1829 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1830}
1831
David Symonds151cce02012-12-06 13:34:42 +11001832// badToUnderscore is the mapping function used to generate Go names from package names,
David Symonds162d0032012-06-28 09:44:46 -07001833// which can be dotted in the input .proto file. It replaces non-identifier characters such as
1834// dot or dash with underscore.
David Symonds151cce02012-12-06 13:34:42 +11001835func badToUnderscore(r rune) rune {
David Symonds162d0032012-06-28 09:44:46 -07001836 if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
1837 return r
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001838 }
David Symonds162d0032012-06-28 09:44:46 -07001839 return '_'
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001840}
Rob Pikec9e7d972010-06-10 10:30:22 -07001841
David Symonds151cce02012-12-06 13:34:42 +11001842// baseName returns the last path element of the name, with the last dotted suffix removed.
1843func baseName(name string) string {
Rob Pikec9e7d972010-06-10 10:30:22 -07001844 // First, find the last element
1845 if i := strings.LastIndex(name, "/"); i >= 0 {
1846 name = name[i+1:]
1847 }
1848 // Now drop the suffix
1849 if i := strings.LastIndex(name, "."); i >= 0 {
1850 name = name[0:i]
1851 }
1852 return name
1853}
David Symonds52925902013-07-26 19:20:20 +10001854
1855// The SourceCodeInfo message describes the location of elements of a parsed
1856// .proto file by way of a "path", which is a sequence of integers that
1857// describe the route from a FileDescriptorProto to the relevant submessage.
1858// The path alternates between a field number of a repeated field, and an index
1859// into that repeated field. The constants below define the field numbers that
1860// are used.
1861//
1862// See descriptor.proto for more information about this.
1863const (
1864 // tag numbers in FileDescriptorProto
1865 messagePath = 4 // message_type
1866 enumPath = 5 // enum_type
1867 // tag numbers in DescriptorProto
1868 messageFieldPath = 2 // field
1869 messageMessagePath = 3 // nested_type
1870 messageEnumPath = 4 // enum_type
1871 // tag numbers in EnumDescriptorProto
1872 enumValuePath = 2 // value
1873)