blob: d68d2993cedc87d0810de07cf408867a7bfa1da8 [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"
44 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070045
46 "goprotobuf.googlecode.com/hg/proto"
47 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
48 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
49)
50
51// A Plugin provides functionality to add to the output during Go code generation,
52// such as to produce RPC stubs.
53type Plugin interface {
54 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070055 Name() string
56 // Init is called once after data structures are built but before
57 // code generation begins.
58 Init(g *Generator)
59 // Generate produces the code generated by the plugin for this file,
60 // except for the imports, by calling the generator's methods P, In, and Out.
61 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070062 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070063 // It is called after Generate.
64 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070065}
66
67var plugins []Plugin
68
69// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
70// It is typically called during initialization.
71func RegisterPlugin(p Plugin) {
72 n := len(plugins)
73 if cap(plugins) == n {
Rob Pikec9e7d972010-06-10 10:30:22 -070074 nplugins := make([]Plugin, n, n+10) // very unlikely to need more than this
Rob Pikeaf82b4e2010-04-30 15:19:25 -070075 copy(nplugins, plugins)
76 plugins = nplugins
77 }
Rob Pikec9e7d972010-06-10 10:30:22 -070078 plugins = plugins[0 : n+1]
Rob Pikeaf82b4e2010-04-30 15:19:25 -070079 plugins[n] = 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 {
90 File *descriptor.FileDescriptorProto // File this object comes from.
91}
92
93// PackageName is name in the package clause in the generated file.
94func (c *common) PackageName() string { return uniquePackageOf(c.File) }
95
96// Descriptor represents a protocol buffer message.
97type Descriptor struct {
98 common
99 *descriptor.DescriptorProto
100 parent *Descriptor // The containing message, if any.
101 nested []*Descriptor // Inner messages, if any.
102 ext []*ExtensionDescriptor // Extensions, if any.
103 typename []string // Cached typename vector.
104}
105
106// TypeName returns the elements of the dotted type name.
107// The package name is not part of this name.
108func (d *Descriptor) TypeName() []string {
109 if d.typename != nil {
110 return d.typename
111 }
112 n := 0
113 for parent := d; parent != nil; parent = parent.parent {
114 n++
115 }
116 s := make([]string, n, n)
117 for parent := d; parent != nil; parent = parent.parent {
118 n--
119 s[n] = proto.GetString(parent.Name)
120 }
121 d.typename = s
122 return s
123}
124
125// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
126// Otherwise it will be the descriptor of the message in which it is defined.
127type EnumDescriptor struct {
128 common
129 *descriptor.EnumDescriptorProto
130 parent *Descriptor // The containing message, if any.
131 typename []string // Cached typename vector.
132}
133
134// TypeName returns the elements of the dotted type name.
135// The package name is not part of this name.
136func (e *EnumDescriptor) TypeName() (s []string) {
137 if e.typename != nil {
138 return e.typename
139 }
140 name := proto.GetString(e.Name)
141 if e.parent == nil {
142 s = make([]string, 1)
143 } else {
144 pname := e.parent.TypeName()
145 s = make([]string, len(pname)+1)
146 copy(s, pname)
147 }
148 s[len(s)-1] = name
149 e.typename = s
150 return s
151}
152
153// Everything but the last element of the full type name, CamelCased.
154// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
155func (e *EnumDescriptor) prefix() string {
156 typeName := e.TypeName()
157 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
158 if e.parent == nil {
159 // If the enum is not part of a message, the prefix is just the type name.
160 ccPrefix = CamelCase(*e.Name) + "_"
161 }
162 return ccPrefix
163}
164
165// The integer value of the named constant in this enumerated type.
166func (e *EnumDescriptor) integerValueAsString(name string) string {
167 for _, c := range e.Value {
168 if proto.GetString(c.Name) == name {
169 return fmt.Sprint(proto.GetInt32(c.Number))
170 }
171 }
172 log.Exit("cannot find value for enum constant")
173 return ""
174}
175
176// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
177// Otherwise it will be the descriptor of the message in which it is defined.
178type ExtensionDescriptor struct {
179 common
180 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700181 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700182}
183
184// TypeName returns the elements of the dotted type name.
185// The package name is not part of this name.
186func (e *ExtensionDescriptor) TypeName() (s []string) {
187 name := proto.GetString(e.Name)
188 if e.parent == nil {
189 // top-level extension
190 s = make([]string, 1)
191 } else {
192 pname := e.parent.TypeName()
193 s = make([]string, len(pname)+1)
194 copy(s, pname)
195 }
196 s[len(s)-1] = name
197 return s
198}
199
200// FileDescriptor describes an protocol buffer descriptor file (.proto).
201// It includes slices of all the messages and enums defined within it.
202// Those slices are constructed by WrapTypes.
203type FileDescriptor struct {
204 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700205 desc []*Descriptor // All the messages defined in this file.
206 enum []*EnumDescriptor // All the enums defined in this file.
207 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700208}
209
210// PackageName is the package name we'll use in the generated code to refer to this file.
211func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
212
213// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700214// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700215func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700216 // Does the file have a package clause?
217 pkg := proto.GetString(d.Package)
218 if pkg != "" {
219 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700220 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700221 // Use the file base name.
222 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700223}
224
225// Object is an interface abstracting the abilities shared by enums and messages.
226type Object interface {
227 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
228 TypeName() []string
229}
230
231// Each package name we generate must be unique. The package we're generating
232// gets its own name but every other package must have a unqiue name that does
233// not conflict in the code we generate. These names are chosen globally (although
234// they don't have to be, it simplifies things to do them globally).
235func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
236 s, ok := uniquePackageName[fd]
237 if !ok {
238 log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
239 }
240 return s
241}
242
243// Generator is the type whose methods generate the output, stored in the associated response structure.
244type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000245 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700246
247 Request *plugin.CodeGeneratorRequest // The input.
248 Response *plugin.CodeGeneratorResponse // The output.
249
Rob Pikec9e7d972010-06-10 10:30:22 -0700250 Param map[string]string // Command-line parameters.
251 ImportPrefix string // String to prefix to imported package file names.
252 ImportMap map[string]string // Mapping from import name to generated name
253
254 ProtoPkg string // The name under which we import the library's package proto.
255
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700256 packageName string // What we're calling ourselves.
257 allFiles []*FileDescriptor // All files in the tree
258 genFiles []*FileDescriptor // Those files we will generate output for.
259 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000260 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700261 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
262 indent string
263}
264
265// New creates a new generator and allocates the request and response protobufs.
266func New() *Generator {
267 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000268 g.Buffer = new(bytes.Buffer)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700269 g.Request = plugin.NewCodeGeneratorRequest()
270 g.Response = plugin.NewCodeGeneratorResponse()
271 return g
272}
273
274// Error reports a problem, including an os.Error, and exits the program.
275func (g *Generator) Error(err os.Error, msgs ...string) {
276 s := strings.Join(msgs, " ") + ":" + err.String()
277 log.Stderr("protoc-gen-go: error: ", s)
278 g.Response.Error = proto.String(s)
279 os.Exit(1)
280}
281
282// Fail reports a problem and exits the program.
283func (g *Generator) Fail(msgs ...string) {
284 s := strings.Join(msgs, " ")
285 log.Stderr("protoc-gen-go: error: ", s)
286 g.Response.Error = proto.String(s)
287 os.Exit(1)
288}
289
Rob Pikec9e7d972010-06-10 10:30:22 -0700290// CommandLineParameters breaks the comma-separated list of key=value pairs
291// in the parameter (a member of the request protobuf) into a key/value map.
292// It then sets file name mappings defined by those entries.
293func (g *Generator) CommandLineParameters(parameter string) {
294 g.Param = make(map[string]string)
295 for _, p := range strings.Split(parameter, ",", 0) {
296 if i := strings.Index(p, "="); i < 0 {
297 g.Param[p] = ""
298 } else {
299 g.Param[p[0:i]] = p[i+1:]
300 }
301 }
302
303 g.ImportMap = make(map[string]string)
304 for k, v := range g.Param {
305 if k == "import_prefix" {
306 g.ImportPrefix = v
307 } else if len(k) > 0 && k[0] == 'M' {
308 g.ImportMap[k[1:]] = v
309 }
310 }
311}
312
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700313// DefaultPackageName returns the package name printed for the object.
314// If its file is in a different package, it returns the package name we're using for this file, plus ".".
315// Otherwise it returns the empty string.
316func (g *Generator) DefaultPackageName(obj Object) string {
317 pkg := obj.PackageName()
318 if pkg == g.packageName {
319 return ""
320 }
321 return pkg + "."
322}
323
324// For each input file, the unique package name to use, underscored.
325var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700326// Package names already registered. Key is the name from the .proto file;
327// value is the name that appears in the generated code.
328var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700329
Rob Pikec9e7d972010-06-10 10:30:22 -0700330// Create and remember a guaranteed unique package name for this file descriptor.
331// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
332// has no file descriptor.
333func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
334 for pkgNamesInUse[pkg] {
335 // It's a duplicate; must rename.
336 pkg += "X"
337 }
338 // Install it.
339 pkgNamesInUse[pkg] = true
340 pkg = strings.Map(DotToUnderscore, pkg)
341 if f != nil {
342 uniquePackageName[f.FileDescriptorProto] = pkg
343 }
344 return pkg
345}
346
347// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700348// The package name must agree across all files being generated.
349// It also defines unique package names for all imported files.
350func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700351 // Register the name for this package. It will be the first name
352 // registered so is guaranteed to be unmodified.
353 pkg := g.genFiles[0].originalPackageName()
354 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
355 // Register the proto package name. It might collide with the
356 // name of a package we import.
357 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700358 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700359 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700360 if thisPkg != pkg {
361 g.Fail("inconsistent package names:", thisPkg, pkg)
362 }
363 }
364AllFiles:
365 for _, f := range g.allFiles {
366 for _, genf := range g.genFiles {
367 if f == genf {
368 // In this package already.
369 uniquePackageName[f.FileDescriptorProto] = g.packageName
370 continue AllFiles
371 }
372 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700373 RegisterUniquePackageName(f.originalPackageName(), f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700374 }
375}
376
377// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
378// and FileDescriptorProtos into file-referenced objects within the Generator.
379// It also creates the list of files to generate and so should be called before GenerateAllFiles.
380func (g *Generator) WrapTypes() {
381 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
382 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700383 // We must wrap the descriptors before we wrap the enums
384 descs := wrapDescriptors(f)
385 g.buildNestedDescriptors(descs)
386 enums := wrapEnumDescriptors(f, descs)
387 exts := wrapExtensions(f)
388 g.allFiles[i] = &FileDescriptor{
389 FileDescriptorProto: f,
390 desc: descs,
391 enum: enums,
392 ext: exts,
393 }
394 }
395
396 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
397FindFiles:
398 for i, fileName := range g.Request.FileToGenerate {
399 // Search the list. This algorithm is n^2 but n is tiny.
400 for _, file := range g.allFiles {
401 if fileName == proto.GetString(file.Name) {
402 g.genFiles[i] = file
403 continue FindFiles
404 }
405 }
406 g.Fail("could not find file named", fileName)
407 }
408 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
409}
410
411// Scan the descriptors in this file. For each one, build the slice of nested descriptors
412func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
413 for _, desc := range descs {
414 if len(desc.NestedType) != 0 {
415 desc.nested = make([]*Descriptor, len(desc.NestedType))
416 n := 0
417 for _, nest := range descs {
418 if nest.parent == desc {
419 desc.nested[n] = nest
420 n++
421 }
422 }
423 if n != len(desc.NestedType) {
424 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
425 }
426 }
427 }
428}
429
430// Construct the Descriptor and add it to the slice
431func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
432 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
433
434 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
435 for i, field := range desc.Extension {
436 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
437 }
438
439 if len(sl) == cap(sl) {
440 nsl := make([]*Descriptor, len(sl), 2*len(sl))
441 copy(nsl, sl)
442 sl = nsl
443 }
444 sl = sl[0 : len(sl)+1]
445 sl[len(sl)-1] = d
446 return sl
447}
448
449// Return a slice of all the Descriptors defined within this file
450func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
451 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
452 for _, desc := range file.MessageType {
453 sl = wrapThisDescriptor(sl, desc, nil, file)
454 }
455 return sl
456}
457
458// Wrap this Descriptor, recursively
459func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
460 sl = addDescriptor(sl, desc, parent, file)
461 me := sl[len(sl)-1]
462 for _, nested := range desc.NestedType {
463 sl = wrapThisDescriptor(sl, nested, me, file)
464 }
465 return sl
466}
467
468// Construct the EnumDescriptor and add it to the slice
469func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
470 if len(sl) == cap(sl) {
471 nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
472 copy(nsl, sl)
473 sl = nsl
474 }
475 sl = sl[0 : len(sl)+1]
476 sl[len(sl)-1] = &EnumDescriptor{common{File: file}, desc, parent, nil}
477 return sl
478}
479
480// Return a slice of all the EnumDescriptors defined within this file
481func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
482 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
483 for _, enum := range file.EnumType {
484 sl = addEnumDescriptor(sl, enum, nil, file)
485 }
486 for _, nested := range descs {
487 sl = wrapEnumDescriptorsInMessage(sl, nested, file)
488 }
489 return sl
490}
491
492// Wrap this EnumDescriptor, recursively
493func wrapEnumDescriptorsInMessage(sl []*EnumDescriptor, desc *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
494 for _, enum := range desc.EnumType {
495 sl = addEnumDescriptor(sl, enum, desc, file)
496 }
497 for _, nested := range desc.nested {
498 sl = wrapEnumDescriptorsInMessage(sl, nested, file)
499 }
500 return sl
501}
502
503// Return a slice of all the top-level ExtensionDescriptors defined within this file.
504func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
505 sl := make([]*ExtensionDescriptor, len(file.Extension))
506 for i, field := range file.Extension {
507 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
508 }
509 return sl
510}
511
Rob Pikec9e7d972010-06-10 10:30:22 -0700512// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700513// The key names for the map come from the input data, which puts a period at the beginning.
514// It should be called after SetPackageNames and before GenerateAllFiles.
515func (g *Generator) BuildTypeNameMap() {
516 g.typeNameToObject = make(map[string]Object)
517 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700518 // The names in this loop are defined by the proto world, not us, so the
519 // package name may be empty. If so, the dotted package name of X will
520 // be ".X"; otherwise it will be ".pkg.X".
521 dottedPkg := "." + proto.GetString(f.Package)
522 if dottedPkg != "." {
523 dottedPkg += "."
524 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700525 for _, enum := range f.enum {
526 name := dottedPkg + dottedSlice(enum.TypeName())
527 g.typeNameToObject[name] = enum
528 }
529 for _, desc := range f.desc {
530 name := dottedPkg + dottedSlice(desc.TypeName())
531 g.typeNameToObject[name] = desc
532 }
533 }
534}
535
536// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
537// returns the descriptor for the message or enum with that name.
538func (g *Generator) ObjectNamed(typeName string) Object {
539 f, ok := g.typeNameToObject[typeName]
540 if !ok {
541 g.Fail("can't find object with type", typeName)
542 }
543 return f
544}
545
546// P prints the arguments to the generated output. It handles strings and int32s, plus
547// handling indirections because they may be *string, etc.
548func (g *Generator) P(str ...interface{}) {
549 g.WriteString(g.indent)
550 for _, v := range str {
551 switch s := v.(type) {
552 case string:
553 g.WriteString(s)
554 case *string:
555 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700556 case bool:
557 g.WriteString(fmt.Sprintf("%t", s))
558 case *bool:
559 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700560 case *int32:
561 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700562 case float64:
563 g.WriteString(fmt.Sprintf("%g", s))
564 case *float64:
565 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700566 default:
567 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
568 }
569 }
570 g.WriteByte('\n')
571}
572
573// In Indents the output one tab stop.
574func (g *Generator) In() { g.indent += "\t" }
575
576// Out unindents the output one tab stop.
577func (g *Generator) Out() {
578 if len(g.indent) > 0 {
579 g.indent = g.indent[1:]
580 }
581}
582
583// GenerateAllFiles generates the output for all the files we're outputting.
584func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700585 // Initialize the plugins
586 for _, p := range plugins {
587 p.Init(g)
588 }
589 // Generate the output.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700590 for i, file := range g.genFiles {
591 g.Reset()
592 g.generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700593 g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
594 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
595 g.Response.File[i].Content = proto.String(g.String())
596 }
597}
598
599// Run all the plugins associated with the file.
600func (g *Generator) runPlugins(file *FileDescriptor) {
601 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700602 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700603 }
604}
605
606
607// FileOf return the FileDescriptor for this FileDescriptorProto.
608func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
609 for _, file := range g.allFiles {
610 if file.FileDescriptorProto == fd {
611 return file
612 }
613 }
614 g.Fail("could not find file in table:", proto.GetString(fd.Name))
615 return nil
616}
617
618// Fill the response protocol buffer with the generated output for all the files we're
619// supposed to generate.
620func (g *Generator) generate(file *FileDescriptor) {
621 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000622 g.usedPackages = make(map[string]bool)
623
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700624 for _, enum := range g.file.enum {
625 g.generateEnum(enum)
626 }
627 for _, desc := range g.file.desc {
628 g.generateMessage(desc)
629 }
630 for _, ext := range g.file.ext {
631 g.generateExtension(ext)
632 }
633 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000634
Rob Pikec9e7d972010-06-10 10:30:22 -0700635 // Run the plugins before the imports so we know which imports are necessary.
636 g.runPlugins(file)
637
David Symondsf90e3382010-05-05 10:53:44 +1000638 // Generate header and imports last, though they appear first in the output.
639 rem := g.Buffer
640 g.Buffer = new(bytes.Buffer)
641 g.generateHeader()
642 g.generateImports()
643 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700644}
645
646// Generate the header, including package definition and imports
647func (g *Generator) generateHeader() {
648 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
649 g.P("// DO NOT EDIT!")
650 g.P()
651 g.P("package ", g.file.PackageName())
652 g.P()
653}
654
655// Generate the header, including package definition and imports
656func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700657 // We almost always need a proto import. Rather than computing when we
658 // do, which is tricky when there's a plugin, just import it and
659 // reference it later.
Rob Pike809831a2010-06-16 10:10:58 -0700660 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700661 for _, s := range g.file.Dependency {
662 // Need to find the descriptor for this file
663 for _, fd := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700664 // Do not import our own package.
665 if fd.PackageName() == g.packageName {
666 continue
667 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700668 if proto.GetString(fd.Name) == s {
669 filename := goFileName(s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700670 if substitution, ok := g.ImportMap[s]; ok {
671 filename = substitution
672 }
673 filename = g.ImportPrefix + filename
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700674 if strings.HasSuffix(filename, ".go") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700675 filename = filename[0 : len(filename)-3]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700676 }
David Symondsf90e3382010-05-05 10:53:44 +1000677 if _, ok := g.usedPackages[fd.PackageName()]; ok {
678 g.P("import ", fd.PackageName(), " ", Quote(filename))
679 } else {
680 log.Stderr("protoc-gen-go: discarding unused import: ", filename)
681 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700682 break
683 }
684 }
685 }
686 g.P()
687 // TODO: may need to worry about uniqueness across plugins
688 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700689 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700690 g.P()
691 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700692 g.P("// Reference proto import to suppress error if it's not otherwise used.")
693 g.P("var _ = ", g.ProtoPkg, ".GetString")
694 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700695}
696
697// Generate the enum definitions for this EnumDescriptor.
698func (g *Generator) generateEnum(enum *EnumDescriptor) {
699 // The full type name
700 typeName := enum.TypeName()
701 // The full type name, CamelCased.
702 ccTypeName := CamelCaseSlice(typeName)
703 ccPrefix := enum.prefix()
704 g.P("type ", ccTypeName, " int32")
705 g.P("const (")
706 g.In()
707 for _, e := range enum.Value {
708 g.P(ccPrefix+*e.Name, " = ", e.Number)
709 }
710 g.Out()
711 g.P(")")
712 g.P("var ", ccTypeName, "_name = map[int32] string {")
713 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700714 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700715 for _, e := range enum.Value {
716 duplicate := ""
717 if _, present := generated[*e.Number]; present {
718 duplicate = "// Duplicate value: "
719 }
720 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
721 generated[*e.Number] = true
722 }
723 g.Out()
724 g.P("}")
725 g.P("var ", ccTypeName, "_value = map[string] int32 {")
726 g.In()
727 for _, e := range enum.Value {
728 g.P(Quote(*e.Name), ": ", e.Number, ",")
729 }
730 g.Out()
731 g.P("}")
732 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
733 g.In()
734 g.P("e := ", ccTypeName, "(x)")
735 g.P("return &e")
736 g.Out()
737 g.P("}")
738 g.P()
739}
740
741// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
742// identifies details of the field for the protocol buffer marshaling and unmarshaling
743// code. The fields are:
744// wire encoding
745// protocol tag number
746// opt,req,rep for optional, required, or repeated
747// name= the original declared name
748// enum= the name of the enum type if it is an enum-typed field.
749// def= string representation of the default value, if any.
750// The default value must be in a representation that can be used at run-time
751// to generate the default value. Thus bools become 0 and 1, for instance.
752func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
753 optrepreq := ""
754 switch {
755 case isOptional(field):
756 optrepreq = "opt"
757 case isRequired(field):
758 optrepreq = "req"
759 case isRepeated(field):
760 optrepreq = "rep"
761 }
762 defaultValue := proto.GetString(field.DefaultValue)
763 if defaultValue != "" {
764 switch *field.Type {
765 case descriptor.FieldDescriptorProto_TYPE_BOOL:
766 if defaultValue == "true" {
767 defaultValue = "1"
768 } else {
769 defaultValue = "0"
770 }
771 case descriptor.FieldDescriptorProto_TYPE_STRING,
772 descriptor.FieldDescriptorProto_TYPE_BYTES:
773 // Protect frogs.
774 defaultValue = Quote(defaultValue)
775 // Don't need the quotes
776 defaultValue = defaultValue[1 : len(defaultValue)-1]
777 case descriptor.FieldDescriptorProto_TYPE_ENUM:
778 // For enums we need to provide the integer constant.
779 obj := g.ObjectNamed(proto.GetString(field.TypeName))
780 enum, ok := obj.(*EnumDescriptor)
781 if !ok {
782 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
783 }
784 defaultValue = enum.integerValueAsString(defaultValue)
785 }
786 defaultValue = ",def=" + defaultValue
787 }
788 enum := ""
789 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
790 obj := g.ObjectNamed(proto.GetString(field.TypeName))
791 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
792 }
793 name := proto.GetString(field.Name)
794 if name == CamelCase(name) {
795 name = ""
796 } else {
797 name = ",name=" + name
798 }
799 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
800 wiretype,
801 proto.GetInt32(field.Number),
802 optrepreq,
803 name,
804 enum,
805 defaultValue))
806}
807
808func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
809 switch typ {
810 case descriptor.FieldDescriptorProto_TYPE_GROUP:
811 return false
812 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
813 return false
814 case descriptor.FieldDescriptorProto_TYPE_BYTES:
815 return false
816 }
817 return true
818}
819
820// TypeName is the printed name appropriate for an item. If the object is in the current file,
821// TypeName drops the package name and underscores the rest.
822// Otherwise the object is from another package; and the result is the underscored
823// package name followed by the item name.
824// The result always has an initial capital.
825func (g *Generator) TypeName(obj Object) string {
826 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
827}
828
829// TypeNameWithPackage is like TypeName, but always includes the package
830// name even if the object is in our own package.
831func (g *Generator) TypeNameWithPackage(obj Object) string {
832 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
833}
834
835// GoType returns a string representing the type name, and the wire type
836func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
837 // TODO: Options.
838 switch *field.Type {
839 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
840 typ, wire = "float64", "fixed64"
841 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
842 typ, wire = "float32", "fixed32"
843 case descriptor.FieldDescriptorProto_TYPE_INT64:
844 typ, wire = "int64", "varint"
845 case descriptor.FieldDescriptorProto_TYPE_UINT64:
846 typ, wire = "uint64", "varint"
847 case descriptor.FieldDescriptorProto_TYPE_INT32:
848 typ, wire = "int32", "varint"
849 case descriptor.FieldDescriptorProto_TYPE_UINT32:
850 typ, wire = "uint32", "varint"
851 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
852 typ, wire = "uint64", "fixed64"
853 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
854 typ, wire = "uint32", "fixed32"
855 case descriptor.FieldDescriptorProto_TYPE_BOOL:
856 typ, wire = "bool", "varint"
857 case descriptor.FieldDescriptorProto_TYPE_STRING:
858 typ, wire = "string", "bytes"
859 case descriptor.FieldDescriptorProto_TYPE_GROUP:
860 desc := g.ObjectNamed(proto.GetString(field.TypeName))
861 typ, wire = "*"+g.TypeName(desc), "group"
862 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
863 desc := g.ObjectNamed(proto.GetString(field.TypeName))
864 typ, wire = "*"+g.TypeName(desc), "bytes"
865 case descriptor.FieldDescriptorProto_TYPE_BYTES:
866 typ, wire = "[]byte", "bytes"
867 case descriptor.FieldDescriptorProto_TYPE_ENUM:
868 desc := g.ObjectNamed(proto.GetString(field.TypeName))
869 typ, wire = g.TypeName(desc), "varint"
870 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
871 typ, wire = "int32", "fixed32"
872 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
873 typ, wire = "int64", "fixed64"
874 case descriptor.FieldDescriptorProto_TYPE_SINT32:
875 typ, wire = "int32", "zigzag32"
876 case descriptor.FieldDescriptorProto_TYPE_SINT64:
877 typ, wire = "int64", "zigzag64"
878 default:
879 g.Fail("unknown type for", proto.GetString(field.Name))
880 }
881 if isRepeated(field) {
882 typ = "[]" + typ
883 } else if needsStar(*field.Type) {
884 typ = "*" + typ
885 }
886 return
887}
888
David Symondsf90e3382010-05-05 10:53:44 +1000889func (g *Generator) RecordTypeUse(t string) {
890 if obj, ok := g.typeNameToObject[t]; ok {
891 g.usedPackages[obj.PackageName()] = true
892 }
893}
894
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700895// Generate the type and default constant definitions for this Descriptor.
896func (g *Generator) generateMessage(message *Descriptor) {
897 // The full type name
898 typeName := message.TypeName()
899 // The full type name, CamelCased.
900 ccTypeName := CamelCaseSlice(typeName)
901
902 g.P("type ", ccTypeName, " struct {")
903 g.In()
904 for _, field := range message.Field {
905 fieldname := CamelCase(*field.Name)
906 typename, wiretype := g.GoType(message, field)
907 tag := g.goTag(field, wiretype)
908 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000909 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700910 }
911 if len(message.ExtensionRange) > 0 {
912 g.P("XXX_extensions\t\tmap[int32][]byte")
913 }
914 g.P("XXX_unrecognized\t[]byte")
915 g.Out()
916 g.P("}")
917
918 // Reset and New functions
919 g.P("func (this *", ccTypeName, ") Reset() {")
920 g.In()
921 g.P("*this = ", ccTypeName, "{}")
922 g.Out()
923 g.P("}")
924 g.P("func New", ccTypeName, "() *", ccTypeName, " {")
925 g.In()
926 g.P("return new(", ccTypeName, ")")
927 g.Out()
928 g.P("}")
929
930 // Extension support methods
931 if len(message.ExtensionRange) > 0 {
932 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -0700933 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700934 g.In()
935 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -0700936 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
937 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700938 }
939 g.Out()
940 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -0700941 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700942 g.In()
943 g.P("return extRange_", ccTypeName)
944 g.Out()
945 g.P("}")
946 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
947 g.In()
948 g.P("if this.XXX_extensions == nil {")
949 g.In()
950 g.P("this.XXX_extensions = make(map[int32][]byte)")
951 g.Out()
952 g.P("}")
953 g.P("return this.XXX_extensions")
954 g.Out()
955 g.P("}")
956 }
957
958 // Default constants
959 for _, field := range message.Field {
960 def := proto.GetString(field.DefaultValue)
961 if def == "" {
962 continue
963 }
964 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
965 typename, _ := g.GoType(message, field)
966 if typename[0] == '*' {
967 typename = typename[1:]
968 }
969 kind := "const "
970 switch {
971 case typename == "bool":
972 case typename == "string":
973 def = Quote(def)
974 case typename == "[]byte":
975 def = "[]byte(" + Quote(def) + ")"
976 kind = "var "
977 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
978 // Must be an enum. Need to construct the prefixed name.
979 obj := g.ObjectNamed(proto.GetString(field.TypeName))
980 enum, ok := obj.(*EnumDescriptor)
981 if !ok {
982 log.Stderr("don't know how to generate constant for", fieldname)
983 continue
984 }
985 def = enum.prefix() + def
986 }
987 g.P(kind, fieldname, " ", typename, " = ", def)
988 }
989 g.P()
990
991 for _, ext := range message.ext {
992 g.generateExtension(ext)
993 }
994}
995
996func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
997 // The full type name
998 typeName := ext.TypeName()
999 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
1000 for i, s := range typeName {
1001 typeName[i] = CamelCase(s)
1002 }
1003 ccTypeName := "E_" + strings.Join(typeName, "_")
1004
1005 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1006 field := ext.FieldDescriptorProto
1007 fieldType, wireType := g.GoType(ext.parent, field)
1008 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001009 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001010
Rob Pikec9e7d972010-06-10 10:30:22 -07001011 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001012 g.In()
1013 g.P("ExtendedType: (", extendedType, ")(nil),")
1014 g.P("ExtensionType: (", fieldType, ")(nil),")
1015 g.P("Field: ", field.Number, ",")
1016 g.P("Tag: ", tag, ",")
1017
1018 g.Out()
1019 g.P("}")
1020 g.P()
1021}
1022
1023func (g *Generator) generateInitFunction() {
1024 g.P("func init() {")
1025 g.In()
1026 for _, enum := range g.file.enum {
1027 g.generateEnumRegistration(enum)
1028 }
1029 g.Out()
1030 g.P("}")
1031}
1032
1033func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1034 pkg := g.packageName + "." // We always print the full package name here.
1035 // The full type name
1036 typeName := enum.TypeName()
1037 // The full type name, CamelCased.
1038 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001039 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001040}
1041
1042// And now lots of helper functions.
1043
Rob Pike2c7bafc2010-06-10 16:07:14 -07001044// Is c an ASCII lower-case letter?
1045func isASCIILower(c byte) bool {
1046 return 'a' <= c && c <= 'z'
1047}
1048
1049// Is c an ASCII digit?
1050func isASCIIDigit(c byte) bool {
1051 return '0' <= c && c <= '9'
1052}
1053
1054// CamelCase returns the CamelCased name.
1055// If there is an interior underscore followed by a lower case letter,
1056// drop the underscore and convert the letter to upper case.
1057// There is a remote possibility of this rewrite causing a name collision,
1058// but it's so remote we're prepared to pretend it's nonexistent - since the
1059// C++ generator lowercases names, it's extremely unlikely to have two fields
1060// with different capitalizations.
1061// In short, _my_field_name_2 becomes XMyFieldName2.
1062func CamelCase(s string) string {
1063 if s == "" {
1064 return ""
1065 }
1066 t := make([]byte, 0, 32)
1067 oneC := make([]byte, 1)
1068 i := 0
1069 if s[0] == '_' {
1070 // Need a capital letter; drop the '_'.
1071 oneC[0] = 'X'
1072 t = bytes.Add(t, oneC)
1073 i++
1074 }
1075 // Invariant: if the next letter is lower case, it must be converted
1076 // to upper case.
1077 // That is, we process a word at a time, where words are marked by _ or
1078 // upper case letter. Digits are treated as words.
1079 for ; i < len(s); i++ {
1080 c := s[i]
1081 oneC[0] = c
1082 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1083 continue // Skip the underscore in s.
1084 }
1085 if isASCIIDigit(c) {
1086 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001087 continue
1088 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001089 // Assume we have a letter now - if not, it's a bogus identifier.
1090 // The next word is a sequence of characters that must start upper case.
1091 if isASCIILower(c) {
1092 oneC[0] ^= ' ' // Make it a capital letter.
1093 }
1094 t = bytes.Add(t, oneC) // Guaranteed not lower case.
1095 // Accept lower case sequence that follows.
1096 for i+1 < len(s) && isASCIILower(s[i+1]) {
1097 i++
1098 oneC[0] = s[i]
1099 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001100 }
1101 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001102 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001103}
1104
1105// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1106// be joined with "_".
1107func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1108
1109// dottedSlice turns a sliced name into a dotted name.
1110func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1111
1112// Quote returns a Go-source quoted string representation of s.
1113func Quote(s string) string { return fmt.Sprintf("%q", s) }
1114
1115// Given a .proto file name, return the output name for the generated Go program.
1116func goFileName(name string) string {
1117 if strings.HasSuffix(name, ".proto") {
1118 name = name[0 : len(name)-6]
1119 }
1120 return name + ".pb.go"
1121}
1122
1123// Is this field optional?
1124func isOptional(field *descriptor.FieldDescriptorProto) bool {
1125 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1126}
1127
1128// Is this field required?
1129func isRequired(field *descriptor.FieldDescriptorProto) bool {
1130 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1131}
1132
1133// Is this field repeated?
1134func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1135 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1136}
1137
1138// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001139// which can be dotted in the input .proto file. It maps dots to underscores.
1140// Because we also get here from package names generated from file names, it also maps
1141// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001142func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001143 switch rune {
1144 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001145 return '_'
1146 }
1147 return rune
1148}
Rob Pikec9e7d972010-06-10 10:30:22 -07001149
1150// BaseName returns the last path element of the name, with the last dotted suffix removed.
1151func BaseName(name string) string {
1152 // First, find the last element
1153 if i := strings.LastIndex(name, "/"); i >= 0 {
1154 name = name[i+1:]
1155 }
1156 // Now drop the suffix
1157 if i := strings.LastIndex(name, "."); i >= 0 {
1158 name = name[0:i]
1159 }
1160 return name
1161}