blob: c2c6297b05c862175d136fe1e610f597e1cd2ec1 [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33 The code generator for the plugin for the Google protocol buffer compiler.
34 It generates Go code from the protocol buffer description files read by the
35 main routine.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070036*/
37package generator
38
39import (
40 "bytes"
41 "fmt"
42 "log"
43 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070044 "path"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070045 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070046
47 "goprotobuf.googlecode.com/hg/proto"
48 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
49 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
50)
51
52// A Plugin provides functionality to add to the output during Go code generation,
53// such as to produce RPC stubs.
54type Plugin interface {
55 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070056 Name() string
57 // Init is called once after data structures are built but before
58 // code generation begins.
59 Init(g *Generator)
60 // Generate produces the code generated by the plugin for this file,
61 // except for the imports, by calling the generator's methods P, In, and Out.
62 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070063 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070064 // It is called after Generate.
65 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070066}
67
68var plugins []Plugin
69
70// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
71// It is typically called during initialization.
72func RegisterPlugin(p Plugin) {
73 n := len(plugins)
74 if cap(plugins) == n {
Rob Pikec9e7d972010-06-10 10:30:22 -070075 nplugins := make([]Plugin, n, n+10) // very unlikely to need more than this
Rob Pikeaf82b4e2010-04-30 15:19:25 -070076 copy(nplugins, plugins)
77 plugins = nplugins
78 }
Rob Pikec9e7d972010-06-10 10:30:22 -070079 plugins = plugins[0 : n+1]
Rob Pikeaf82b4e2010-04-30 15:19:25 -070080 plugins[n] = p
Rob Pikeaf82b4e2010-04-30 15:19:25 -070081}
82
83// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
84// a pointer to the FileDescriptorProto that represents it. These types achieve that
85// wrapping by placing each Proto inside a struct with the pointer to its File. The
86// structs have the same names as their contents, with "Proto" removed.
87// FileDescriptor is used to store the things that it points to.
88
89// The file and package name method are common to messages and enums.
90type common struct {
91 File *descriptor.FileDescriptorProto // File this object comes from.
92}
93
94// PackageName is name in the package clause in the generated file.
95func (c *common) PackageName() string { return uniquePackageOf(c.File) }
96
97// Descriptor represents a protocol buffer message.
98type Descriptor struct {
99 common
100 *descriptor.DescriptorProto
101 parent *Descriptor // The containing message, if any.
102 nested []*Descriptor // Inner messages, if any.
103 ext []*ExtensionDescriptor // Extensions, if any.
104 typename []string // Cached typename vector.
105}
106
107// TypeName returns the elements of the dotted type name.
108// The package name is not part of this name.
109func (d *Descriptor) TypeName() []string {
110 if d.typename != nil {
111 return d.typename
112 }
113 n := 0
114 for parent := d; parent != nil; parent = parent.parent {
115 n++
116 }
117 s := make([]string, n, n)
118 for parent := d; parent != nil; parent = parent.parent {
119 n--
120 s[n] = proto.GetString(parent.Name)
121 }
122 d.typename = s
123 return s
124}
125
126// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
127// Otherwise it will be the descriptor of the message in which it is defined.
128type EnumDescriptor struct {
129 common
130 *descriptor.EnumDescriptorProto
131 parent *Descriptor // The containing message, if any.
132 typename []string // Cached typename vector.
133}
134
135// TypeName returns the elements of the dotted type name.
136// The package name is not part of this name.
137func (e *EnumDescriptor) TypeName() (s []string) {
138 if e.typename != nil {
139 return e.typename
140 }
141 name := proto.GetString(e.Name)
142 if e.parent == nil {
143 s = make([]string, 1)
144 } else {
145 pname := e.parent.TypeName()
146 s = make([]string, len(pname)+1)
147 copy(s, pname)
148 }
149 s[len(s)-1] = name
150 e.typename = s
151 return s
152}
153
154// Everything but the last element of the full type name, CamelCased.
155// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
156func (e *EnumDescriptor) prefix() string {
157 typeName := e.TypeName()
158 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
159 if e.parent == nil {
160 // If the enum is not part of a message, the prefix is just the type name.
161 ccPrefix = CamelCase(*e.Name) + "_"
162 }
163 return ccPrefix
164}
165
166// The integer value of the named constant in this enumerated type.
167func (e *EnumDescriptor) integerValueAsString(name string) string {
168 for _, c := range e.Value {
169 if proto.GetString(c.Name) == name {
170 return fmt.Sprint(proto.GetInt32(c.Number))
171 }
172 }
173 log.Exit("cannot find value for enum constant")
174 return ""
175}
176
177// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
178// Otherwise it will be the descriptor of the message in which it is defined.
179type ExtensionDescriptor struct {
180 common
181 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700182 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700183}
184
185// TypeName returns the elements of the dotted type name.
186// The package name is not part of this name.
187func (e *ExtensionDescriptor) TypeName() (s []string) {
188 name := proto.GetString(e.Name)
189 if e.parent == nil {
190 // top-level extension
191 s = make([]string, 1)
192 } else {
193 pname := e.parent.TypeName()
194 s = make([]string, len(pname)+1)
195 copy(s, pname)
196 }
197 s[len(s)-1] = name
198 return s
199}
200
201// FileDescriptor describes an protocol buffer descriptor file (.proto).
202// It includes slices of all the messages and enums defined within it.
203// Those slices are constructed by WrapTypes.
204type FileDescriptor struct {
205 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700206 desc []*Descriptor // All the messages defined in this file.
207 enum []*EnumDescriptor // All the enums defined in this file.
208 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700209}
210
211// PackageName is the package name we'll use in the generated code to refer to this file.
212func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
213
214// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700215// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700216func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700217 // Does the file have a package clause?
218 pkg := proto.GetString(d.Package)
219 if pkg != "" {
220 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700221 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700222 // Use the file base name.
223 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700224}
225
226// Object is an interface abstracting the abilities shared by enums and messages.
227type Object interface {
228 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
229 TypeName() []string
230}
231
232// Each package name we generate must be unique. The package we're generating
233// gets its own name but every other package must have a unqiue name that does
234// not conflict in the code we generate. These names are chosen globally (although
235// they don't have to be, it simplifies things to do them globally).
236func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
237 s, ok := uniquePackageName[fd]
238 if !ok {
239 log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
240 }
241 return s
242}
243
244// Generator is the type whose methods generate the output, stored in the associated response structure.
245type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000246 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700247
248 Request *plugin.CodeGeneratorRequest // The input.
249 Response *plugin.CodeGeneratorResponse // The output.
250
Rob Pikec9e7d972010-06-10 10:30:22 -0700251 Param map[string]string // Command-line parameters.
252 ImportPrefix string // String to prefix to imported package file names.
253 ImportMap map[string]string // Mapping from import name to generated name
254
255 ProtoPkg string // The name under which we import the library's package proto.
256
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700257 packageName string // What we're calling ourselves.
258 allFiles []*FileDescriptor // All files in the tree
259 genFiles []*FileDescriptor // Those files we will generate output for.
260 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000261 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700262 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
263 indent string
264}
265
266// New creates a new generator and allocates the request and response protobufs.
267func New() *Generator {
268 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000269 g.Buffer = new(bytes.Buffer)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700270 g.Request = plugin.NewCodeGeneratorRequest()
271 g.Response = plugin.NewCodeGeneratorResponse()
272 return g
273}
274
275// Error reports a problem, including an os.Error, and exits the program.
276func (g *Generator) Error(err os.Error, msgs ...string) {
277 s := strings.Join(msgs, " ") + ":" + err.String()
278 log.Stderr("protoc-gen-go: error: ", s)
279 g.Response.Error = proto.String(s)
280 os.Exit(1)
281}
282
283// Fail reports a problem and exits the program.
284func (g *Generator) Fail(msgs ...string) {
285 s := strings.Join(msgs, " ")
286 log.Stderr("protoc-gen-go: error: ", s)
287 g.Response.Error = proto.String(s)
288 os.Exit(1)
289}
290
Rob Pikec9e7d972010-06-10 10:30:22 -0700291// CommandLineParameters breaks the comma-separated list of key=value pairs
292// in the parameter (a member of the request protobuf) into a key/value map.
293// It then sets file name mappings defined by those entries.
294func (g *Generator) CommandLineParameters(parameter string) {
295 g.Param = make(map[string]string)
Rob Pike53385442010-06-30 22:22:43 -0700296 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700297 if i := strings.Index(p, "="); i < 0 {
298 g.Param[p] = ""
299 } else {
300 g.Param[p[0:i]] = p[i+1:]
301 }
302 }
303
304 g.ImportMap = make(map[string]string)
305 for k, v := range g.Param {
306 if k == "import_prefix" {
307 g.ImportPrefix = v
308 } else if len(k) > 0 && k[0] == 'M' {
309 g.ImportMap[k[1:]] = v
310 }
311 }
312}
313
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700314// DefaultPackageName returns the package name printed for the object.
315// If its file is in a different package, it returns the package name we're using for this file, plus ".".
316// Otherwise it returns the empty string.
317func (g *Generator) DefaultPackageName(obj Object) string {
318 pkg := obj.PackageName()
319 if pkg == g.packageName {
320 return ""
321 }
322 return pkg + "."
323}
324
325// For each input file, the unique package name to use, underscored.
326var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700327// Package names already registered. Key is the name from the .proto file;
328// value is the name that appears in the generated code.
329var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700330
Rob Pikec9e7d972010-06-10 10:30:22 -0700331// Create and remember a guaranteed unique package name for this file descriptor.
332// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
333// has no file descriptor.
334func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
335 for pkgNamesInUse[pkg] {
336 // It's a duplicate; must rename.
337 pkg += "X"
338 }
339 // Install it.
340 pkgNamesInUse[pkg] = true
341 pkg = strings.Map(DotToUnderscore, pkg)
342 if f != nil {
343 uniquePackageName[f.FileDescriptorProto] = pkg
344 }
345 return pkg
346}
347
348// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700349// The package name must agree across all files being generated.
350// It also defines unique package names for all imported files.
351func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700352 // Register the name for this package. It will be the first name
353 // registered so is guaranteed to be unmodified.
354 pkg := g.genFiles[0].originalPackageName()
355 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
356 // Register the proto package name. It might collide with the
357 // name of a package we import.
358 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700359 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700360 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700361 if thisPkg != pkg {
362 g.Fail("inconsistent package names:", thisPkg, pkg)
363 }
364 }
365AllFiles:
366 for _, f := range g.allFiles {
367 for _, genf := range g.genFiles {
368 if f == genf {
369 // In this package already.
370 uniquePackageName[f.FileDescriptorProto] = g.packageName
371 continue AllFiles
372 }
373 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700374 RegisterUniquePackageName(f.originalPackageName(), f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700375 }
376}
377
378// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
379// and FileDescriptorProtos into file-referenced objects within the Generator.
380// It also creates the list of files to generate and so should be called before GenerateAllFiles.
381func (g *Generator) WrapTypes() {
382 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
383 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700384 // We must wrap the descriptors before we wrap the enums
385 descs := wrapDescriptors(f)
386 g.buildNestedDescriptors(descs)
387 enums := wrapEnumDescriptors(f, descs)
388 exts := wrapExtensions(f)
389 g.allFiles[i] = &FileDescriptor{
390 FileDescriptorProto: f,
391 desc: descs,
392 enum: enums,
393 ext: exts,
394 }
395 }
396
397 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
398FindFiles:
399 for i, fileName := range g.Request.FileToGenerate {
400 // Search the list. This algorithm is n^2 but n is tiny.
401 for _, file := range g.allFiles {
402 if fileName == proto.GetString(file.Name) {
403 g.genFiles[i] = file
404 continue FindFiles
405 }
406 }
407 g.Fail("could not find file named", fileName)
408 }
409 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
410}
411
412// Scan the descriptors in this file. For each one, build the slice of nested descriptors
413func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
414 for _, desc := range descs {
415 if len(desc.NestedType) != 0 {
416 desc.nested = make([]*Descriptor, len(desc.NestedType))
417 n := 0
418 for _, nest := range descs {
419 if nest.parent == desc {
420 desc.nested[n] = nest
421 n++
422 }
423 }
424 if n != len(desc.NestedType) {
425 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
426 }
427 }
428 }
429}
430
431// Construct the Descriptor and add it to the slice
432func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
433 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
434
435 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
436 for i, field := range desc.Extension {
437 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
438 }
439
440 if len(sl) == cap(sl) {
441 nsl := make([]*Descriptor, len(sl), 2*len(sl))
442 copy(nsl, sl)
443 sl = nsl
444 }
445 sl = sl[0 : len(sl)+1]
446 sl[len(sl)-1] = d
447 return sl
448}
449
450// Return a slice of all the Descriptors defined within this file
451func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
452 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
453 for _, desc := range file.MessageType {
454 sl = wrapThisDescriptor(sl, desc, nil, file)
455 }
456 return sl
457}
458
459// Wrap this Descriptor, recursively
460func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
461 sl = addDescriptor(sl, desc, parent, file)
462 me := sl[len(sl)-1]
463 for _, nested := range desc.NestedType {
464 sl = wrapThisDescriptor(sl, nested, me, file)
465 }
466 return sl
467}
468
469// Construct the EnumDescriptor and add it to the slice
470func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
471 if len(sl) == cap(sl) {
472 nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
473 copy(nsl, sl)
474 sl = nsl
475 }
476 sl = sl[0 : len(sl)+1]
477 sl[len(sl)-1] = &EnumDescriptor{common{File: file}, desc, parent, nil}
478 return sl
479}
480
481// Return a slice of all the EnumDescriptors defined within this file
482func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
483 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000484 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700485 for _, enum := range file.EnumType {
486 sl = addEnumDescriptor(sl, enum, nil, file)
487 }
David Symonds5256cf62010-06-27 10:33:42 +1000488 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700489 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000490 for _, enum := range nested.EnumType {
491 sl = addEnumDescriptor(sl, enum, nested, file)
492 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700493 }
494 return sl
495}
496
497// Return a slice of all the top-level ExtensionDescriptors defined within this file.
498func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
499 sl := make([]*ExtensionDescriptor, len(file.Extension))
500 for i, field := range file.Extension {
501 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
502 }
503 return sl
504}
505
Rob Pikec9e7d972010-06-10 10:30:22 -0700506// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700507// The key names for the map come from the input data, which puts a period at the beginning.
508// It should be called after SetPackageNames and before GenerateAllFiles.
509func (g *Generator) BuildTypeNameMap() {
510 g.typeNameToObject = make(map[string]Object)
511 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700512 // The names in this loop are defined by the proto world, not us, so the
513 // package name may be empty. If so, the dotted package name of X will
514 // be ".X"; otherwise it will be ".pkg.X".
515 dottedPkg := "." + proto.GetString(f.Package)
516 if dottedPkg != "." {
517 dottedPkg += "."
518 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700519 for _, enum := range f.enum {
520 name := dottedPkg + dottedSlice(enum.TypeName())
521 g.typeNameToObject[name] = enum
522 }
523 for _, desc := range f.desc {
524 name := dottedPkg + dottedSlice(desc.TypeName())
525 g.typeNameToObject[name] = desc
526 }
527 }
528}
529
530// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
531// returns the descriptor for the message or enum with that name.
532func (g *Generator) ObjectNamed(typeName string) Object {
533 f, ok := g.typeNameToObject[typeName]
534 if !ok {
535 g.Fail("can't find object with type", typeName)
536 }
537 return f
538}
539
540// P prints the arguments to the generated output. It handles strings and int32s, plus
541// handling indirections because they may be *string, etc.
542func (g *Generator) P(str ...interface{}) {
543 g.WriteString(g.indent)
544 for _, v := range str {
545 switch s := v.(type) {
546 case string:
547 g.WriteString(s)
548 case *string:
549 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700550 case bool:
551 g.WriteString(fmt.Sprintf("%t", s))
552 case *bool:
553 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700554 case *int32:
555 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700556 case float64:
557 g.WriteString(fmt.Sprintf("%g", s))
558 case *float64:
559 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700560 default:
561 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
562 }
563 }
564 g.WriteByte('\n')
565}
566
567// In Indents the output one tab stop.
568func (g *Generator) In() { g.indent += "\t" }
569
570// Out unindents the output one tab stop.
571func (g *Generator) Out() {
572 if len(g.indent) > 0 {
573 g.indent = g.indent[1:]
574 }
575}
576
577// GenerateAllFiles generates the output for all the files we're outputting.
578func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700579 // Initialize the plugins
580 for _, p := range plugins {
581 p.Init(g)
582 }
583 // Generate the output.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700584 for i, file := range g.genFiles {
585 g.Reset()
586 g.generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700587 g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
588 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
589 g.Response.File[i].Content = proto.String(g.String())
590 }
591}
592
593// Run all the plugins associated with the file.
594func (g *Generator) runPlugins(file *FileDescriptor) {
595 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700596 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700597 }
598}
599
600
601// FileOf return the FileDescriptor for this FileDescriptorProto.
602func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
603 for _, file := range g.allFiles {
604 if file.FileDescriptorProto == fd {
605 return file
606 }
607 }
608 g.Fail("could not find file in table:", proto.GetString(fd.Name))
609 return nil
610}
611
612// Fill the response protocol buffer with the generated output for all the files we're
613// supposed to generate.
614func (g *Generator) generate(file *FileDescriptor) {
615 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000616 g.usedPackages = make(map[string]bool)
617
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700618 for _, enum := range g.file.enum {
619 g.generateEnum(enum)
620 }
621 for _, desc := range g.file.desc {
622 g.generateMessage(desc)
623 }
624 for _, ext := range g.file.ext {
625 g.generateExtension(ext)
626 }
627 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000628
Rob Pikec9e7d972010-06-10 10:30:22 -0700629 // Run the plugins before the imports so we know which imports are necessary.
630 g.runPlugins(file)
631
David Symondsf90e3382010-05-05 10:53:44 +1000632 // Generate header and imports last, though they appear first in the output.
633 rem := g.Buffer
634 g.Buffer = new(bytes.Buffer)
635 g.generateHeader()
636 g.generateImports()
637 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700638}
639
640// Generate the header, including package definition and imports
641func (g *Generator) generateHeader() {
642 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
643 g.P("// DO NOT EDIT!")
644 g.P()
645 g.P("package ", g.file.PackageName())
646 g.P()
647}
648
649// Generate the header, including package definition and imports
650func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700651 // We almost always need a proto import. Rather than computing when we
652 // do, which is tricky when there's a plugin, just import it and
653 // reference it later.
Rob Pike809831a2010-06-16 10:10:58 -0700654 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700655 for _, s := range g.file.Dependency {
656 // Need to find the descriptor for this file
657 for _, fd := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700658 // Do not import our own package.
659 if fd.PackageName() == g.packageName {
660 continue
661 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700662 if proto.GetString(fd.Name) == s {
663 filename := goFileName(s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700664 if substitution, ok := g.ImportMap[s]; ok {
665 filename = substitution
666 }
667 filename = g.ImportPrefix + filename
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700668 if strings.HasSuffix(filename, ".go") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700669 filename = filename[0 : len(filename)-3]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700670 }
David Symondsf90e3382010-05-05 10:53:44 +1000671 if _, ok := g.usedPackages[fd.PackageName()]; ok {
672 g.P("import ", fd.PackageName(), " ", Quote(filename))
673 } else {
674 log.Stderr("protoc-gen-go: discarding unused import: ", filename)
675 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700676 break
677 }
678 }
679 }
680 g.P()
681 // TODO: may need to worry about uniqueness across plugins
682 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700683 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700684 g.P()
685 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700686 g.P("// Reference proto import to suppress error if it's not otherwise used.")
687 g.P("var _ = ", g.ProtoPkg, ".GetString")
688 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700689}
690
691// Generate the enum definitions for this EnumDescriptor.
692func (g *Generator) generateEnum(enum *EnumDescriptor) {
693 // The full type name
694 typeName := enum.TypeName()
695 // The full type name, CamelCased.
696 ccTypeName := CamelCaseSlice(typeName)
697 ccPrefix := enum.prefix()
698 g.P("type ", ccTypeName, " int32")
699 g.P("const (")
700 g.In()
701 for _, e := range enum.Value {
702 g.P(ccPrefix+*e.Name, " = ", e.Number)
703 }
704 g.Out()
705 g.P(")")
706 g.P("var ", ccTypeName, "_name = map[int32] string {")
707 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700708 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700709 for _, e := range enum.Value {
710 duplicate := ""
711 if _, present := generated[*e.Number]; present {
712 duplicate = "// Duplicate value: "
713 }
714 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
715 generated[*e.Number] = true
716 }
717 g.Out()
718 g.P("}")
719 g.P("var ", ccTypeName, "_value = map[string] int32 {")
720 g.In()
721 for _, e := range enum.Value {
722 g.P(Quote(*e.Name), ": ", e.Number, ",")
723 }
724 g.Out()
725 g.P("}")
726 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
727 g.In()
728 g.P("e := ", ccTypeName, "(x)")
729 g.P("return &e")
730 g.Out()
731 g.P("}")
732 g.P()
733}
734
735// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
736// identifies details of the field for the protocol buffer marshaling and unmarshaling
737// code. The fields are:
738// wire encoding
739// protocol tag number
740// opt,req,rep for optional, required, or repeated
741// name= the original declared name
742// enum= the name of the enum type if it is an enum-typed field.
743// def= string representation of the default value, if any.
744// The default value must be in a representation that can be used at run-time
745// to generate the default value. Thus bools become 0 and 1, for instance.
746func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
747 optrepreq := ""
748 switch {
749 case isOptional(field):
750 optrepreq = "opt"
751 case isRequired(field):
752 optrepreq = "req"
753 case isRepeated(field):
754 optrepreq = "rep"
755 }
756 defaultValue := proto.GetString(field.DefaultValue)
757 if defaultValue != "" {
758 switch *field.Type {
759 case descriptor.FieldDescriptorProto_TYPE_BOOL:
760 if defaultValue == "true" {
761 defaultValue = "1"
762 } else {
763 defaultValue = "0"
764 }
765 case descriptor.FieldDescriptorProto_TYPE_STRING,
766 descriptor.FieldDescriptorProto_TYPE_BYTES:
767 // Protect frogs.
768 defaultValue = Quote(defaultValue)
769 // Don't need the quotes
770 defaultValue = defaultValue[1 : len(defaultValue)-1]
771 case descriptor.FieldDescriptorProto_TYPE_ENUM:
772 // For enums we need to provide the integer constant.
773 obj := g.ObjectNamed(proto.GetString(field.TypeName))
774 enum, ok := obj.(*EnumDescriptor)
775 if !ok {
776 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
777 }
778 defaultValue = enum.integerValueAsString(defaultValue)
779 }
780 defaultValue = ",def=" + defaultValue
781 }
782 enum := ""
783 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
784 obj := g.ObjectNamed(proto.GetString(field.TypeName))
785 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
786 }
787 name := proto.GetString(field.Name)
788 if name == CamelCase(name) {
789 name = ""
790 } else {
791 name = ",name=" + name
792 }
793 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
794 wiretype,
795 proto.GetInt32(field.Number),
796 optrepreq,
797 name,
798 enum,
799 defaultValue))
800}
801
802func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
803 switch typ {
804 case descriptor.FieldDescriptorProto_TYPE_GROUP:
805 return false
806 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
807 return false
808 case descriptor.FieldDescriptorProto_TYPE_BYTES:
809 return false
810 }
811 return true
812}
813
814// TypeName is the printed name appropriate for an item. If the object is in the current file,
815// TypeName drops the package name and underscores the rest.
816// Otherwise the object is from another package; and the result is the underscored
817// package name followed by the item name.
818// The result always has an initial capital.
819func (g *Generator) TypeName(obj Object) string {
820 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
821}
822
823// TypeNameWithPackage is like TypeName, but always includes the package
824// name even if the object is in our own package.
825func (g *Generator) TypeNameWithPackage(obj Object) string {
826 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
827}
828
829// GoType returns a string representing the type name, and the wire type
830func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
831 // TODO: Options.
832 switch *field.Type {
833 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
834 typ, wire = "float64", "fixed64"
835 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
836 typ, wire = "float32", "fixed32"
837 case descriptor.FieldDescriptorProto_TYPE_INT64:
838 typ, wire = "int64", "varint"
839 case descriptor.FieldDescriptorProto_TYPE_UINT64:
840 typ, wire = "uint64", "varint"
841 case descriptor.FieldDescriptorProto_TYPE_INT32:
842 typ, wire = "int32", "varint"
843 case descriptor.FieldDescriptorProto_TYPE_UINT32:
844 typ, wire = "uint32", "varint"
845 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
846 typ, wire = "uint64", "fixed64"
847 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
848 typ, wire = "uint32", "fixed32"
849 case descriptor.FieldDescriptorProto_TYPE_BOOL:
850 typ, wire = "bool", "varint"
851 case descriptor.FieldDescriptorProto_TYPE_STRING:
852 typ, wire = "string", "bytes"
853 case descriptor.FieldDescriptorProto_TYPE_GROUP:
854 desc := g.ObjectNamed(proto.GetString(field.TypeName))
855 typ, wire = "*"+g.TypeName(desc), "group"
856 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
857 desc := g.ObjectNamed(proto.GetString(field.TypeName))
858 typ, wire = "*"+g.TypeName(desc), "bytes"
859 case descriptor.FieldDescriptorProto_TYPE_BYTES:
860 typ, wire = "[]byte", "bytes"
861 case descriptor.FieldDescriptorProto_TYPE_ENUM:
862 desc := g.ObjectNamed(proto.GetString(field.TypeName))
863 typ, wire = g.TypeName(desc), "varint"
864 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
865 typ, wire = "int32", "fixed32"
866 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
867 typ, wire = "int64", "fixed64"
868 case descriptor.FieldDescriptorProto_TYPE_SINT32:
869 typ, wire = "int32", "zigzag32"
870 case descriptor.FieldDescriptorProto_TYPE_SINT64:
871 typ, wire = "int64", "zigzag64"
872 default:
873 g.Fail("unknown type for", proto.GetString(field.Name))
874 }
875 if isRepeated(field) {
876 typ = "[]" + typ
877 } else if needsStar(*field.Type) {
878 typ = "*" + typ
879 }
880 return
881}
882
David Symondsf90e3382010-05-05 10:53:44 +1000883func (g *Generator) RecordTypeUse(t string) {
884 if obj, ok := g.typeNameToObject[t]; ok {
885 g.usedPackages[obj.PackageName()] = true
886 }
887}
888
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700889// Generate the type and default constant definitions for this Descriptor.
890func (g *Generator) generateMessage(message *Descriptor) {
891 // The full type name
892 typeName := message.TypeName()
893 // The full type name, CamelCased.
894 ccTypeName := CamelCaseSlice(typeName)
895
896 g.P("type ", ccTypeName, " struct {")
897 g.In()
898 for _, field := range message.Field {
899 fieldname := CamelCase(*field.Name)
900 typename, wiretype := g.GoType(message, field)
901 tag := g.goTag(field, wiretype)
902 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000903 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700904 }
905 if len(message.ExtensionRange) > 0 {
906 g.P("XXX_extensions\t\tmap[int32][]byte")
907 }
908 g.P("XXX_unrecognized\t[]byte")
909 g.Out()
910 g.P("}")
911
912 // Reset and New functions
913 g.P("func (this *", ccTypeName, ") Reset() {")
914 g.In()
915 g.P("*this = ", ccTypeName, "{}")
916 g.Out()
917 g.P("}")
918 g.P("func New", ccTypeName, "() *", ccTypeName, " {")
919 g.In()
920 g.P("return new(", ccTypeName, ")")
921 g.Out()
922 g.P("}")
923
924 // Extension support methods
925 if len(message.ExtensionRange) > 0 {
926 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -0700927 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700928 g.In()
929 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -0700930 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
931 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700932 }
933 g.Out()
934 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -0700935 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700936 g.In()
937 g.P("return extRange_", ccTypeName)
938 g.Out()
939 g.P("}")
940 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
941 g.In()
942 g.P("if this.XXX_extensions == nil {")
943 g.In()
944 g.P("this.XXX_extensions = make(map[int32][]byte)")
945 g.Out()
946 g.P("}")
947 g.P("return this.XXX_extensions")
948 g.Out()
949 g.P("}")
950 }
951
952 // Default constants
953 for _, field := range message.Field {
954 def := proto.GetString(field.DefaultValue)
955 if def == "" {
956 continue
957 }
958 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
959 typename, _ := g.GoType(message, field)
960 if typename[0] == '*' {
961 typename = typename[1:]
962 }
963 kind := "const "
964 switch {
965 case typename == "bool":
966 case typename == "string":
967 def = Quote(def)
968 case typename == "[]byte":
969 def = "[]byte(" + Quote(def) + ")"
970 kind = "var "
971 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
972 // Must be an enum. Need to construct the prefixed name.
973 obj := g.ObjectNamed(proto.GetString(field.TypeName))
974 enum, ok := obj.(*EnumDescriptor)
975 if !ok {
976 log.Stderr("don't know how to generate constant for", fieldname)
977 continue
978 }
Rob Pike87af39e2010-07-19 10:48:02 -0700979 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700980 }
981 g.P(kind, fieldname, " ", typename, " = ", def)
982 }
983 g.P()
984
985 for _, ext := range message.ext {
986 g.generateExtension(ext)
987 }
988}
989
990func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
991 // The full type name
992 typeName := ext.TypeName()
993 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
994 for i, s := range typeName {
995 typeName[i] = CamelCase(s)
996 }
997 ccTypeName := "E_" + strings.Join(typeName, "_")
998
999 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1000 field := ext.FieldDescriptorProto
1001 fieldType, wireType := g.GoType(ext.parent, field)
1002 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001003 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001004
Rob Pikec9e7d972010-06-10 10:30:22 -07001005 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001006 g.In()
1007 g.P("ExtendedType: (", extendedType, ")(nil),")
1008 g.P("ExtensionType: (", fieldType, ")(nil),")
1009 g.P("Field: ", field.Number, ",")
1010 g.P("Tag: ", tag, ",")
1011
1012 g.Out()
1013 g.P("}")
1014 g.P()
1015}
1016
1017func (g *Generator) generateInitFunction() {
1018 g.P("func init() {")
1019 g.In()
1020 for _, enum := range g.file.enum {
1021 g.generateEnumRegistration(enum)
1022 }
1023 g.Out()
1024 g.P("}")
1025}
1026
1027func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1028 pkg := g.packageName + "." // We always print the full package name here.
1029 // The full type name
1030 typeName := enum.TypeName()
1031 // The full type name, CamelCased.
1032 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001033 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001034}
1035
1036// And now lots of helper functions.
1037
Rob Pike2c7bafc2010-06-10 16:07:14 -07001038// Is c an ASCII lower-case letter?
1039func isASCIILower(c byte) bool {
1040 return 'a' <= c && c <= 'z'
1041}
1042
1043// Is c an ASCII digit?
1044func isASCIIDigit(c byte) bool {
1045 return '0' <= c && c <= '9'
1046}
1047
1048// CamelCase returns the CamelCased name.
1049// If there is an interior underscore followed by a lower case letter,
1050// drop the underscore and convert the letter to upper case.
1051// There is a remote possibility of this rewrite causing a name collision,
1052// but it's so remote we're prepared to pretend it's nonexistent - since the
1053// C++ generator lowercases names, it's extremely unlikely to have two fields
1054// with different capitalizations.
1055// In short, _my_field_name_2 becomes XMyFieldName2.
1056func CamelCase(s string) string {
1057 if s == "" {
1058 return ""
1059 }
1060 t := make([]byte, 0, 32)
1061 oneC := make([]byte, 1)
1062 i := 0
1063 if s[0] == '_' {
1064 // Need a capital letter; drop the '_'.
1065 oneC[0] = 'X'
1066 t = bytes.Add(t, oneC)
1067 i++
1068 }
1069 // Invariant: if the next letter is lower case, it must be converted
1070 // to upper case.
1071 // That is, we process a word at a time, where words are marked by _ or
1072 // upper case letter. Digits are treated as words.
1073 for ; i < len(s); i++ {
1074 c := s[i]
1075 oneC[0] = c
1076 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1077 continue // Skip the underscore in s.
1078 }
1079 if isASCIIDigit(c) {
1080 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001081 continue
1082 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001083 // Assume we have a letter now - if not, it's a bogus identifier.
1084 // The next word is a sequence of characters that must start upper case.
1085 if isASCIILower(c) {
1086 oneC[0] ^= ' ' // Make it a capital letter.
1087 }
1088 t = bytes.Add(t, oneC) // Guaranteed not lower case.
1089 // Accept lower case sequence that follows.
1090 for i+1 < len(s) && isASCIILower(s[i+1]) {
1091 i++
1092 oneC[0] = s[i]
1093 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001094 }
1095 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001096 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001097}
1098
1099// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1100// be joined with "_".
1101func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1102
1103// dottedSlice turns a sliced name into a dotted name.
1104func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1105
1106// Quote returns a Go-source quoted string representation of s.
1107func Quote(s string) string { return fmt.Sprintf("%q", s) }
1108
1109// Given a .proto file name, return the output name for the generated Go program.
1110func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001111 ext := path.Ext(name)
1112 if ext == ".proto" || ext == ".protodevel" {
1113 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001114 }
1115 return name + ".pb.go"
1116}
1117
1118// Is this field optional?
1119func isOptional(field *descriptor.FieldDescriptorProto) bool {
1120 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1121}
1122
1123// Is this field required?
1124func isRequired(field *descriptor.FieldDescriptorProto) bool {
1125 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1126}
1127
1128// Is this field repeated?
1129func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1130 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1131}
1132
1133// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001134// which can be dotted in the input .proto file. It maps dots to underscores.
1135// Because we also get here from package names generated from file names, it also maps
1136// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001137func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001138 switch rune {
1139 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001140 return '_'
1141 }
1142 return rune
1143}
Rob Pikec9e7d972010-06-10 10:30:22 -07001144
1145// BaseName returns the last path element of the name, with the last dotted suffix removed.
1146func BaseName(name string) string {
1147 // First, find the last element
1148 if i := strings.LastIndex(name, "/"); i >= 0 {
1149 name = name[i+1:]
1150 }
1151 // Now drop the suffix
1152 if i := strings.LastIndex(name, "."); i >= 0 {
1153 name = name[0:i]
1154 }
1155 return name
1156}