blob: 176c94f2ac20f5d65a5533c0de68c3048da9ceee [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)
Rob Pike53385442010-06-30 22:22:43 -0700295 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700296 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)
David Symonds5256cf62010-06-27 10:33:42 +1000483 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700484 for _, enum := range file.EnumType {
485 sl = addEnumDescriptor(sl, enum, nil, file)
486 }
David Symonds5256cf62010-06-27 10:33:42 +1000487 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700488 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000489 for _, enum := range nested.EnumType {
490 sl = addEnumDescriptor(sl, enum, nested, file)
491 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700492 }
493 return sl
494}
495
496// Return a slice of all the top-level ExtensionDescriptors defined within this file.
497func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
498 sl := make([]*ExtensionDescriptor, len(file.Extension))
499 for i, field := range file.Extension {
500 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
501 }
502 return sl
503}
504
Rob Pikec9e7d972010-06-10 10:30:22 -0700505// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700506// The key names for the map come from the input data, which puts a period at the beginning.
507// It should be called after SetPackageNames and before GenerateAllFiles.
508func (g *Generator) BuildTypeNameMap() {
509 g.typeNameToObject = make(map[string]Object)
510 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700511 // The names in this loop are defined by the proto world, not us, so the
512 // package name may be empty. If so, the dotted package name of X will
513 // be ".X"; otherwise it will be ".pkg.X".
514 dottedPkg := "." + proto.GetString(f.Package)
515 if dottedPkg != "." {
516 dottedPkg += "."
517 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700518 for _, enum := range f.enum {
519 name := dottedPkg + dottedSlice(enum.TypeName())
520 g.typeNameToObject[name] = enum
521 }
522 for _, desc := range f.desc {
523 name := dottedPkg + dottedSlice(desc.TypeName())
524 g.typeNameToObject[name] = desc
525 }
526 }
527}
528
529// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
530// returns the descriptor for the message or enum with that name.
531func (g *Generator) ObjectNamed(typeName string) Object {
532 f, ok := g.typeNameToObject[typeName]
533 if !ok {
534 g.Fail("can't find object with type", typeName)
535 }
536 return f
537}
538
539// P prints the arguments to the generated output. It handles strings and int32s, plus
540// handling indirections because they may be *string, etc.
541func (g *Generator) P(str ...interface{}) {
542 g.WriteString(g.indent)
543 for _, v := range str {
544 switch s := v.(type) {
545 case string:
546 g.WriteString(s)
547 case *string:
548 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700549 case bool:
550 g.WriteString(fmt.Sprintf("%t", s))
551 case *bool:
552 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700553 case *int32:
554 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700555 case float64:
556 g.WriteString(fmt.Sprintf("%g", s))
557 case *float64:
558 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700559 default:
560 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
561 }
562 }
563 g.WriteByte('\n')
564}
565
566// In Indents the output one tab stop.
567func (g *Generator) In() { g.indent += "\t" }
568
569// Out unindents the output one tab stop.
570func (g *Generator) Out() {
571 if len(g.indent) > 0 {
572 g.indent = g.indent[1:]
573 }
574}
575
576// GenerateAllFiles generates the output for all the files we're outputting.
577func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700578 // Initialize the plugins
579 for _, p := range plugins {
580 p.Init(g)
581 }
582 // Generate the output.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700583 for i, file := range g.genFiles {
584 g.Reset()
585 g.generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700586 g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
587 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
588 g.Response.File[i].Content = proto.String(g.String())
589 }
590}
591
592// Run all the plugins associated with the file.
593func (g *Generator) runPlugins(file *FileDescriptor) {
594 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700595 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700596 }
597}
598
599
600// FileOf return the FileDescriptor for this FileDescriptorProto.
601func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
602 for _, file := range g.allFiles {
603 if file.FileDescriptorProto == fd {
604 return file
605 }
606 }
607 g.Fail("could not find file in table:", proto.GetString(fd.Name))
608 return nil
609}
610
611// Fill the response protocol buffer with the generated output for all the files we're
612// supposed to generate.
613func (g *Generator) generate(file *FileDescriptor) {
614 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000615 g.usedPackages = make(map[string]bool)
616
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700617 for _, enum := range g.file.enum {
618 g.generateEnum(enum)
619 }
620 for _, desc := range g.file.desc {
621 g.generateMessage(desc)
622 }
623 for _, ext := range g.file.ext {
624 g.generateExtension(ext)
625 }
626 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000627
Rob Pikec9e7d972010-06-10 10:30:22 -0700628 // Run the plugins before the imports so we know which imports are necessary.
629 g.runPlugins(file)
630
David Symondsf90e3382010-05-05 10:53:44 +1000631 // Generate header and imports last, though they appear first in the output.
632 rem := g.Buffer
633 g.Buffer = new(bytes.Buffer)
634 g.generateHeader()
635 g.generateImports()
636 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700637}
638
639// Generate the header, including package definition and imports
640func (g *Generator) generateHeader() {
641 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
642 g.P("// DO NOT EDIT!")
643 g.P()
644 g.P("package ", g.file.PackageName())
645 g.P()
646}
647
648// Generate the header, including package definition and imports
649func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700650 // We almost always need a proto import. Rather than computing when we
651 // do, which is tricky when there's a plugin, just import it and
652 // reference it later.
Rob Pike809831a2010-06-16 10:10:58 -0700653 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700654 for _, s := range g.file.Dependency {
655 // Need to find the descriptor for this file
656 for _, fd := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700657 // Do not import our own package.
658 if fd.PackageName() == g.packageName {
659 continue
660 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700661 if proto.GetString(fd.Name) == s {
662 filename := goFileName(s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700663 if substitution, ok := g.ImportMap[s]; ok {
664 filename = substitution
665 }
666 filename = g.ImportPrefix + filename
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700667 if strings.HasSuffix(filename, ".go") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700668 filename = filename[0 : len(filename)-3]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700669 }
David Symondsf90e3382010-05-05 10:53:44 +1000670 if _, ok := g.usedPackages[fd.PackageName()]; ok {
671 g.P("import ", fd.PackageName(), " ", Quote(filename))
672 } else {
673 log.Stderr("protoc-gen-go: discarding unused import: ", filename)
674 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700675 break
676 }
677 }
678 }
679 g.P()
680 // TODO: may need to worry about uniqueness across plugins
681 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700682 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700683 g.P()
684 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700685 g.P("// Reference proto import to suppress error if it's not otherwise used.")
686 g.P("var _ = ", g.ProtoPkg, ".GetString")
687 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700688}
689
690// Generate the enum definitions for this EnumDescriptor.
691func (g *Generator) generateEnum(enum *EnumDescriptor) {
692 // The full type name
693 typeName := enum.TypeName()
694 // The full type name, CamelCased.
695 ccTypeName := CamelCaseSlice(typeName)
696 ccPrefix := enum.prefix()
697 g.P("type ", ccTypeName, " int32")
698 g.P("const (")
699 g.In()
700 for _, e := range enum.Value {
701 g.P(ccPrefix+*e.Name, " = ", e.Number)
702 }
703 g.Out()
704 g.P(")")
705 g.P("var ", ccTypeName, "_name = map[int32] string {")
706 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700707 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700708 for _, e := range enum.Value {
709 duplicate := ""
710 if _, present := generated[*e.Number]; present {
711 duplicate = "// Duplicate value: "
712 }
713 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
714 generated[*e.Number] = true
715 }
716 g.Out()
717 g.P("}")
718 g.P("var ", ccTypeName, "_value = map[string] int32 {")
719 g.In()
720 for _, e := range enum.Value {
721 g.P(Quote(*e.Name), ": ", e.Number, ",")
722 }
723 g.Out()
724 g.P("}")
725 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
726 g.In()
727 g.P("e := ", ccTypeName, "(x)")
728 g.P("return &e")
729 g.Out()
730 g.P("}")
731 g.P()
732}
733
734// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
735// identifies details of the field for the protocol buffer marshaling and unmarshaling
736// code. The fields are:
737// wire encoding
738// protocol tag number
739// opt,req,rep for optional, required, or repeated
740// name= the original declared name
741// enum= the name of the enum type if it is an enum-typed field.
742// def= string representation of the default value, if any.
743// The default value must be in a representation that can be used at run-time
744// to generate the default value. Thus bools become 0 and 1, for instance.
745func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
746 optrepreq := ""
747 switch {
748 case isOptional(field):
749 optrepreq = "opt"
750 case isRequired(field):
751 optrepreq = "req"
752 case isRepeated(field):
753 optrepreq = "rep"
754 }
755 defaultValue := proto.GetString(field.DefaultValue)
756 if defaultValue != "" {
757 switch *field.Type {
758 case descriptor.FieldDescriptorProto_TYPE_BOOL:
759 if defaultValue == "true" {
760 defaultValue = "1"
761 } else {
762 defaultValue = "0"
763 }
764 case descriptor.FieldDescriptorProto_TYPE_STRING,
765 descriptor.FieldDescriptorProto_TYPE_BYTES:
766 // Protect frogs.
767 defaultValue = Quote(defaultValue)
768 // Don't need the quotes
769 defaultValue = defaultValue[1 : len(defaultValue)-1]
770 case descriptor.FieldDescriptorProto_TYPE_ENUM:
771 // For enums we need to provide the integer constant.
772 obj := g.ObjectNamed(proto.GetString(field.TypeName))
773 enum, ok := obj.(*EnumDescriptor)
774 if !ok {
775 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
776 }
777 defaultValue = enum.integerValueAsString(defaultValue)
778 }
779 defaultValue = ",def=" + defaultValue
780 }
781 enum := ""
782 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
783 obj := g.ObjectNamed(proto.GetString(field.TypeName))
784 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
785 }
786 name := proto.GetString(field.Name)
787 if name == CamelCase(name) {
788 name = ""
789 } else {
790 name = ",name=" + name
791 }
792 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
793 wiretype,
794 proto.GetInt32(field.Number),
795 optrepreq,
796 name,
797 enum,
798 defaultValue))
799}
800
801func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
802 switch typ {
803 case descriptor.FieldDescriptorProto_TYPE_GROUP:
804 return false
805 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
806 return false
807 case descriptor.FieldDescriptorProto_TYPE_BYTES:
808 return false
809 }
810 return true
811}
812
813// TypeName is the printed name appropriate for an item. If the object is in the current file,
814// TypeName drops the package name and underscores the rest.
815// Otherwise the object is from another package; and the result is the underscored
816// package name followed by the item name.
817// The result always has an initial capital.
818func (g *Generator) TypeName(obj Object) string {
819 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
820}
821
822// TypeNameWithPackage is like TypeName, but always includes the package
823// name even if the object is in our own package.
824func (g *Generator) TypeNameWithPackage(obj Object) string {
825 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
826}
827
828// GoType returns a string representing the type name, and the wire type
829func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
830 // TODO: Options.
831 switch *field.Type {
832 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
833 typ, wire = "float64", "fixed64"
834 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
835 typ, wire = "float32", "fixed32"
836 case descriptor.FieldDescriptorProto_TYPE_INT64:
837 typ, wire = "int64", "varint"
838 case descriptor.FieldDescriptorProto_TYPE_UINT64:
839 typ, wire = "uint64", "varint"
840 case descriptor.FieldDescriptorProto_TYPE_INT32:
841 typ, wire = "int32", "varint"
842 case descriptor.FieldDescriptorProto_TYPE_UINT32:
843 typ, wire = "uint32", "varint"
844 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
845 typ, wire = "uint64", "fixed64"
846 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
847 typ, wire = "uint32", "fixed32"
848 case descriptor.FieldDescriptorProto_TYPE_BOOL:
849 typ, wire = "bool", "varint"
850 case descriptor.FieldDescriptorProto_TYPE_STRING:
851 typ, wire = "string", "bytes"
852 case descriptor.FieldDescriptorProto_TYPE_GROUP:
853 desc := g.ObjectNamed(proto.GetString(field.TypeName))
854 typ, wire = "*"+g.TypeName(desc), "group"
855 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
856 desc := g.ObjectNamed(proto.GetString(field.TypeName))
857 typ, wire = "*"+g.TypeName(desc), "bytes"
858 case descriptor.FieldDescriptorProto_TYPE_BYTES:
859 typ, wire = "[]byte", "bytes"
860 case descriptor.FieldDescriptorProto_TYPE_ENUM:
861 desc := g.ObjectNamed(proto.GetString(field.TypeName))
862 typ, wire = g.TypeName(desc), "varint"
863 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
864 typ, wire = "int32", "fixed32"
865 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
866 typ, wire = "int64", "fixed64"
867 case descriptor.FieldDescriptorProto_TYPE_SINT32:
868 typ, wire = "int32", "zigzag32"
869 case descriptor.FieldDescriptorProto_TYPE_SINT64:
870 typ, wire = "int64", "zigzag64"
871 default:
872 g.Fail("unknown type for", proto.GetString(field.Name))
873 }
874 if isRepeated(field) {
875 typ = "[]" + typ
876 } else if needsStar(*field.Type) {
877 typ = "*" + typ
878 }
879 return
880}
881
David Symondsf90e3382010-05-05 10:53:44 +1000882func (g *Generator) RecordTypeUse(t string) {
883 if obj, ok := g.typeNameToObject[t]; ok {
884 g.usedPackages[obj.PackageName()] = true
885 }
886}
887
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700888// Generate the type and default constant definitions for this Descriptor.
889func (g *Generator) generateMessage(message *Descriptor) {
890 // The full type name
891 typeName := message.TypeName()
892 // The full type name, CamelCased.
893 ccTypeName := CamelCaseSlice(typeName)
894
895 g.P("type ", ccTypeName, " struct {")
896 g.In()
897 for _, field := range message.Field {
898 fieldname := CamelCase(*field.Name)
899 typename, wiretype := g.GoType(message, field)
900 tag := g.goTag(field, wiretype)
901 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000902 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700903 }
904 if len(message.ExtensionRange) > 0 {
905 g.P("XXX_extensions\t\tmap[int32][]byte")
906 }
907 g.P("XXX_unrecognized\t[]byte")
908 g.Out()
909 g.P("}")
910
911 // Reset and New functions
912 g.P("func (this *", ccTypeName, ") Reset() {")
913 g.In()
914 g.P("*this = ", ccTypeName, "{}")
915 g.Out()
916 g.P("}")
917 g.P("func New", ccTypeName, "() *", ccTypeName, " {")
918 g.In()
919 g.P("return new(", ccTypeName, ")")
920 g.Out()
921 g.P("}")
922
923 // Extension support methods
924 if len(message.ExtensionRange) > 0 {
925 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -0700926 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700927 g.In()
928 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -0700929 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
930 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700931 }
932 g.Out()
933 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -0700934 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700935 g.In()
936 g.P("return extRange_", ccTypeName)
937 g.Out()
938 g.P("}")
939 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
940 g.In()
941 g.P("if this.XXX_extensions == nil {")
942 g.In()
943 g.P("this.XXX_extensions = make(map[int32][]byte)")
944 g.Out()
945 g.P("}")
946 g.P("return this.XXX_extensions")
947 g.Out()
948 g.P("}")
949 }
950
951 // Default constants
952 for _, field := range message.Field {
953 def := proto.GetString(field.DefaultValue)
954 if def == "" {
955 continue
956 }
957 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
958 typename, _ := g.GoType(message, field)
959 if typename[0] == '*' {
960 typename = typename[1:]
961 }
962 kind := "const "
963 switch {
964 case typename == "bool":
965 case typename == "string":
966 def = Quote(def)
967 case typename == "[]byte":
968 def = "[]byte(" + Quote(def) + ")"
969 kind = "var "
970 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
971 // Must be an enum. Need to construct the prefixed name.
972 obj := g.ObjectNamed(proto.GetString(field.TypeName))
973 enum, ok := obj.(*EnumDescriptor)
974 if !ok {
975 log.Stderr("don't know how to generate constant for", fieldname)
976 continue
977 }
978 def = enum.prefix() + def
979 }
980 g.P(kind, fieldname, " ", typename, " = ", def)
981 }
982 g.P()
983
984 for _, ext := range message.ext {
985 g.generateExtension(ext)
986 }
987}
988
989func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
990 // The full type name
991 typeName := ext.TypeName()
992 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
993 for i, s := range typeName {
994 typeName[i] = CamelCase(s)
995 }
996 ccTypeName := "E_" + strings.Join(typeName, "_")
997
998 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
999 field := ext.FieldDescriptorProto
1000 fieldType, wireType := g.GoType(ext.parent, field)
1001 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001002 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001003
Rob Pikec9e7d972010-06-10 10:30:22 -07001004 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001005 g.In()
1006 g.P("ExtendedType: (", extendedType, ")(nil),")
1007 g.P("ExtensionType: (", fieldType, ")(nil),")
1008 g.P("Field: ", field.Number, ",")
1009 g.P("Tag: ", tag, ",")
1010
1011 g.Out()
1012 g.P("}")
1013 g.P()
1014}
1015
1016func (g *Generator) generateInitFunction() {
1017 g.P("func init() {")
1018 g.In()
1019 for _, enum := range g.file.enum {
1020 g.generateEnumRegistration(enum)
1021 }
1022 g.Out()
1023 g.P("}")
1024}
1025
1026func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1027 pkg := g.packageName + "." // We always print the full package name here.
1028 // The full type name
1029 typeName := enum.TypeName()
1030 // The full type name, CamelCased.
1031 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001032 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001033}
1034
1035// And now lots of helper functions.
1036
Rob Pike2c7bafc2010-06-10 16:07:14 -07001037// Is c an ASCII lower-case letter?
1038func isASCIILower(c byte) bool {
1039 return 'a' <= c && c <= 'z'
1040}
1041
1042// Is c an ASCII digit?
1043func isASCIIDigit(c byte) bool {
1044 return '0' <= c && c <= '9'
1045}
1046
1047// CamelCase returns the CamelCased name.
1048// If there is an interior underscore followed by a lower case letter,
1049// drop the underscore and convert the letter to upper case.
1050// There is a remote possibility of this rewrite causing a name collision,
1051// but it's so remote we're prepared to pretend it's nonexistent - since the
1052// C++ generator lowercases names, it's extremely unlikely to have two fields
1053// with different capitalizations.
1054// In short, _my_field_name_2 becomes XMyFieldName2.
1055func CamelCase(s string) string {
1056 if s == "" {
1057 return ""
1058 }
1059 t := make([]byte, 0, 32)
1060 oneC := make([]byte, 1)
1061 i := 0
1062 if s[0] == '_' {
1063 // Need a capital letter; drop the '_'.
1064 oneC[0] = 'X'
1065 t = bytes.Add(t, oneC)
1066 i++
1067 }
1068 // Invariant: if the next letter is lower case, it must be converted
1069 // to upper case.
1070 // That is, we process a word at a time, where words are marked by _ or
1071 // upper case letter. Digits are treated as words.
1072 for ; i < len(s); i++ {
1073 c := s[i]
1074 oneC[0] = c
1075 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1076 continue // Skip the underscore in s.
1077 }
1078 if isASCIIDigit(c) {
1079 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001080 continue
1081 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001082 // Assume we have a letter now - if not, it's a bogus identifier.
1083 // The next word is a sequence of characters that must start upper case.
1084 if isASCIILower(c) {
1085 oneC[0] ^= ' ' // Make it a capital letter.
1086 }
1087 t = bytes.Add(t, oneC) // Guaranteed not lower case.
1088 // Accept lower case sequence that follows.
1089 for i+1 < len(s) && isASCIILower(s[i+1]) {
1090 i++
1091 oneC[0] = s[i]
1092 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001093 }
1094 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001095 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001096}
1097
1098// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1099// be joined with "_".
1100func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1101
1102// dottedSlice turns a sliced name into a dotted name.
1103func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1104
1105// Quote returns a Go-source quoted string representation of s.
1106func Quote(s string) string { return fmt.Sprintf("%q", s) }
1107
1108// Given a .proto file name, return the output name for the generated Go program.
1109func goFileName(name string) string {
1110 if strings.HasSuffix(name, ".proto") {
1111 name = name[0 : len(name)-6]
1112 }
1113 return name + ".pb.go"
1114}
1115
1116// Is this field optional?
1117func isOptional(field *descriptor.FieldDescriptorProto) bool {
1118 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1119}
1120
1121// Is this field required?
1122func isRequired(field *descriptor.FieldDescriptorProto) bool {
1123 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1124}
1125
1126// Is this field repeated?
1127func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1128 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1129}
1130
1131// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001132// which can be dotted in the input .proto file. It maps dots to underscores.
1133// Because we also get here from package names generated from file names, it also maps
1134// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001135func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001136 switch rune {
1137 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001138 return '_'
1139 }
1140 return rune
1141}
Rob Pikec9e7d972010-06-10 10:30:22 -07001142
1143// BaseName returns the last path element of the name, with the last dotted suffix removed.
1144func BaseName(name string) string {
1145 // First, find the last element
1146 if i := strings.LastIndex(name, "/"); i >= 0 {
1147 name = name[i+1:]
1148 }
1149 // Now drop the suffix
1150 if i := strings.LastIndex(name, "."); i >= 0 {
1151 name = name[0:i]
1152 }
1153 return name
1154}