blob: f64b5d41110a1f8eca6563042cabf08d2092909a [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"
David Symonds79eae332010-10-16 11:33:20 +110045 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070046 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070047
48 "goprotobuf.googlecode.com/hg/proto"
49 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
50 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
51)
52
53// A Plugin provides functionality to add to the output during Go code generation,
54// such as to produce RPC stubs.
55type Plugin interface {
56 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070057 Name() string
58 // Init is called once after data structures are built but before
59 // code generation begins.
60 Init(g *Generator)
61 // Generate produces the code generated by the plugin for this file,
62 // except for the imports, by calling the generator's methods P, In, and Out.
63 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070064 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070065 // It is called after Generate.
66 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070067}
68
69var plugins []Plugin
70
71// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
72// It is typically called during initialization.
73func RegisterPlugin(p Plugin) {
74 n := len(plugins)
75 if cap(plugins) == n {
Rob Pikec9e7d972010-06-10 10:30:22 -070076 nplugins := make([]Plugin, n, n+10) // very unlikely to need more than this
Rob Pikeaf82b4e2010-04-30 15:19:25 -070077 copy(nplugins, plugins)
78 plugins = nplugins
79 }
Rob Pikec9e7d972010-06-10 10:30:22 -070080 plugins = plugins[0 : n+1]
Rob Pikeaf82b4e2010-04-30 15:19:25 -070081 plugins[n] = p
Rob Pikeaf82b4e2010-04-30 15:19:25 -070082}
83
84// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
85// a pointer to the FileDescriptorProto that represents it. These types achieve that
86// wrapping by placing each Proto inside a struct with the pointer to its File. The
87// structs have the same names as their contents, with "Proto" removed.
88// FileDescriptor is used to store the things that it points to.
89
90// The file and package name method are common to messages and enums.
91type common struct {
92 File *descriptor.FileDescriptorProto // File this object comes from.
93}
94
95// PackageName is name in the package clause in the generated file.
96func (c *common) PackageName() string { return uniquePackageOf(c.File) }
97
98// Descriptor represents a protocol buffer message.
99type Descriptor struct {
100 common
101 *descriptor.DescriptorProto
102 parent *Descriptor // The containing message, if any.
103 nested []*Descriptor // Inner messages, if any.
104 ext []*ExtensionDescriptor // Extensions, if any.
105 typename []string // Cached typename vector.
106}
107
108// TypeName returns the elements of the dotted type name.
109// The package name is not part of this name.
110func (d *Descriptor) TypeName() []string {
111 if d.typename != nil {
112 return d.typename
113 }
114 n := 0
115 for parent := d; parent != nil; parent = parent.parent {
116 n++
117 }
118 s := make([]string, n, n)
119 for parent := d; parent != nil; parent = parent.parent {
120 n--
121 s[n] = proto.GetString(parent.Name)
122 }
123 d.typename = s
124 return s
125}
126
127// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
128// Otherwise it will be the descriptor of the message in which it is defined.
129type EnumDescriptor struct {
130 common
131 *descriptor.EnumDescriptorProto
132 parent *Descriptor // The containing message, if any.
133 typename []string // Cached typename vector.
134}
135
136// TypeName returns the elements of the dotted type name.
137// The package name is not part of this name.
138func (e *EnumDescriptor) TypeName() (s []string) {
139 if e.typename != nil {
140 return e.typename
141 }
142 name := proto.GetString(e.Name)
143 if e.parent == nil {
144 s = make([]string, 1)
145 } else {
146 pname := e.parent.TypeName()
147 s = make([]string, len(pname)+1)
148 copy(s, pname)
149 }
150 s[len(s)-1] = name
151 e.typename = s
152 return s
153}
154
155// Everything but the last element of the full type name, CamelCased.
156// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
157func (e *EnumDescriptor) prefix() string {
158 typeName := e.TypeName()
159 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
160 if e.parent == nil {
161 // If the enum is not part of a message, the prefix is just the type name.
162 ccPrefix = CamelCase(*e.Name) + "_"
163 }
164 return ccPrefix
165}
166
167// The integer value of the named constant in this enumerated type.
168func (e *EnumDescriptor) integerValueAsString(name string) string {
169 for _, c := range e.Value {
170 if proto.GetString(c.Name) == name {
171 return fmt.Sprint(proto.GetInt32(c.Number))
172 }
173 }
174 log.Exit("cannot find value for enum constant")
175 return ""
176}
177
178// ExtensionDescriptor desribes an extension. If it's at top level, its parent will be nil.
179// Otherwise it will be the descriptor of the message in which it is defined.
180type ExtensionDescriptor struct {
181 common
182 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700183 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700184}
185
186// TypeName returns the elements of the dotted type name.
187// The package name is not part of this name.
188func (e *ExtensionDescriptor) TypeName() (s []string) {
189 name := proto.GetString(e.Name)
190 if e.parent == nil {
191 // top-level extension
192 s = make([]string, 1)
193 } else {
194 pname := e.parent.TypeName()
195 s = make([]string, len(pname)+1)
196 copy(s, pname)
197 }
198 s[len(s)-1] = name
199 return s
200}
201
202// FileDescriptor describes an protocol buffer descriptor file (.proto).
203// It includes slices of all the messages and enums defined within it.
204// Those slices are constructed by WrapTypes.
205type FileDescriptor struct {
206 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700207 desc []*Descriptor // All the messages defined in this file.
208 enum []*EnumDescriptor // All the enums defined in this file.
209 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700210}
211
212// PackageName is the package name we'll use in the generated code to refer to this file.
213func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
214
215// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700216// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700217func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700218 // Does the file have a package clause?
219 pkg := proto.GetString(d.Package)
220 if pkg != "" {
221 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700222 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700223 // Use the file base name.
224 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700225}
226
227// Object is an interface abstracting the abilities shared by enums and messages.
228type Object interface {
229 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
230 TypeName() []string
231}
232
233// Each package name we generate must be unique. The package we're generating
234// gets its own name but every other package must have a unqiue name that does
235// not conflict in the code we generate. These names are chosen globally (although
236// they don't have to be, it simplifies things to do them globally).
237func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
238 s, ok := uniquePackageName[fd]
239 if !ok {
240 log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
241 }
242 return s
243}
244
245// Generator is the type whose methods generate the output, stored in the associated response structure.
246type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000247 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700248
249 Request *plugin.CodeGeneratorRequest // The input.
250 Response *plugin.CodeGeneratorResponse // The output.
251
Rob Pikec9e7d972010-06-10 10:30:22 -0700252 Param map[string]string // Command-line parameters.
253 ImportPrefix string // String to prefix to imported package file names.
254 ImportMap map[string]string // Mapping from import name to generated name
255
256 ProtoPkg string // The name under which we import the library's package proto.
257
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700258 packageName string // What we're calling ourselves.
259 allFiles []*FileDescriptor // All files in the tree
260 genFiles []*FileDescriptor // Those files we will generate output for.
261 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000262 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700263 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
264 indent string
265}
266
267// New creates a new generator and allocates the request and response protobufs.
268func New() *Generator {
269 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000270 g.Buffer = new(bytes.Buffer)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700271 g.Request = plugin.NewCodeGeneratorRequest()
272 g.Response = plugin.NewCodeGeneratorResponse()
273 return g
274}
275
276// Error reports a problem, including an os.Error, and exits the program.
277func (g *Generator) Error(err os.Error, msgs ...string) {
278 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700279 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700280 g.Response.Error = proto.String(s)
281 os.Exit(1)
282}
283
284// Fail reports a problem and exits the program.
285func (g *Generator) Fail(msgs ...string) {
286 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700287 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700288 g.Response.Error = proto.String(s)
289 os.Exit(1)
290}
291
Rob Pikec9e7d972010-06-10 10:30:22 -0700292// CommandLineParameters breaks the comma-separated list of key=value pairs
293// in the parameter (a member of the request protobuf) into a key/value map.
294// It then sets file name mappings defined by those entries.
295func (g *Generator) CommandLineParameters(parameter string) {
296 g.Param = make(map[string]string)
Rob Pike53385442010-06-30 22:22:43 -0700297 for _, p := range strings.Split(parameter, ",", -1) {
Rob Pikec9e7d972010-06-10 10:30:22 -0700298 if i := strings.Index(p, "="); i < 0 {
299 g.Param[p] = ""
300 } else {
301 g.Param[p[0:i]] = p[i+1:]
302 }
303 }
304
305 g.ImportMap = make(map[string]string)
306 for k, v := range g.Param {
307 if k == "import_prefix" {
308 g.ImportPrefix = v
309 } else if len(k) > 0 && k[0] == 'M' {
310 g.ImportMap[k[1:]] = v
311 }
312 }
313}
314
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700315// DefaultPackageName returns the package name printed for the object.
316// If its file is in a different package, it returns the package name we're using for this file, plus ".".
317// Otherwise it returns the empty string.
318func (g *Generator) DefaultPackageName(obj Object) string {
319 pkg := obj.PackageName()
320 if pkg == g.packageName {
321 return ""
322 }
323 return pkg + "."
324}
325
326// For each input file, the unique package name to use, underscored.
327var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700328// Package names already registered. Key is the name from the .proto file;
329// value is the name that appears in the generated code.
330var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700331
Rob Pikec9e7d972010-06-10 10:30:22 -0700332// Create and remember a guaranteed unique package name for this file descriptor.
333// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
334// has no file descriptor.
335func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds79eae332010-10-16 11:33:20 +1100336 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700337 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100338 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700339 }
340 // Install it.
341 pkgNamesInUse[pkg] = true
342 pkg = strings.Map(DotToUnderscore, pkg)
343 if f != nil {
344 uniquePackageName[f.FileDescriptorProto] = pkg
345 }
346 return pkg
347}
348
349// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700350// The package name must agree across all files being generated.
351// It also defines unique package names for all imported files.
352func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700353 // Register the name for this package. It will be the first name
354 // registered so is guaranteed to be unmodified.
355 pkg := g.genFiles[0].originalPackageName()
356 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
357 // Register the proto package name. It might collide with the
358 // name of a package we import.
359 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700360 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700361 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700362 if thisPkg != pkg {
363 g.Fail("inconsistent package names:", thisPkg, pkg)
364 }
365 }
366AllFiles:
367 for _, f := range g.allFiles {
368 for _, genf := range g.genFiles {
369 if f == genf {
370 // In this package already.
371 uniquePackageName[f.FileDescriptorProto] = g.packageName
372 continue AllFiles
373 }
374 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700375 RegisterUniquePackageName(f.originalPackageName(), f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700376 }
377}
378
379// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
380// and FileDescriptorProtos into file-referenced objects within the Generator.
381// It also creates the list of files to generate and so should be called before GenerateAllFiles.
382func (g *Generator) WrapTypes() {
383 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
384 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700385 // We must wrap the descriptors before we wrap the enums
386 descs := wrapDescriptors(f)
387 g.buildNestedDescriptors(descs)
388 enums := wrapEnumDescriptors(f, descs)
389 exts := wrapExtensions(f)
390 g.allFiles[i] = &FileDescriptor{
391 FileDescriptorProto: f,
392 desc: descs,
393 enum: enums,
394 ext: exts,
395 }
396 }
397
398 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
399FindFiles:
400 for i, fileName := range g.Request.FileToGenerate {
401 // Search the list. This algorithm is n^2 but n is tiny.
402 for _, file := range g.allFiles {
403 if fileName == proto.GetString(file.Name) {
404 g.genFiles[i] = file
405 continue FindFiles
406 }
407 }
408 g.Fail("could not find file named", fileName)
409 }
410 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
411}
412
413// Scan the descriptors in this file. For each one, build the slice of nested descriptors
414func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
415 for _, desc := range descs {
416 if len(desc.NestedType) != 0 {
417 desc.nested = make([]*Descriptor, len(desc.NestedType))
418 n := 0
419 for _, nest := range descs {
420 if nest.parent == desc {
421 desc.nested[n] = nest
422 n++
423 }
424 }
425 if n != len(desc.NestedType) {
426 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
427 }
428 }
429 }
430}
431
432// Construct the Descriptor and add it to the slice
433func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
434 d := &Descriptor{common{File: file}, desc, parent, nil, nil, nil}
435
436 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
437 for i, field := range desc.Extension {
438 d.ext[i] = &ExtensionDescriptor{common{File: file}, field, d}
439 }
440
441 if len(sl) == cap(sl) {
442 nsl := make([]*Descriptor, len(sl), 2*len(sl))
443 copy(nsl, sl)
444 sl = nsl
445 }
446 sl = sl[0 : len(sl)+1]
447 sl[len(sl)-1] = d
448 return sl
449}
450
451// Return a slice of all the Descriptors defined within this file
452func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
453 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
454 for _, desc := range file.MessageType {
455 sl = wrapThisDescriptor(sl, desc, nil, file)
456 }
457 return sl
458}
459
460// Wrap this Descriptor, recursively
461func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
462 sl = addDescriptor(sl, desc, parent, file)
463 me := sl[len(sl)-1]
464 for _, nested := range desc.NestedType {
465 sl = wrapThisDescriptor(sl, nested, me, file)
466 }
467 return sl
468}
469
470// Construct the EnumDescriptor and add it to the slice
471func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
472 if len(sl) == cap(sl) {
473 nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
474 copy(nsl, sl)
475 sl = nsl
476 }
477 sl = sl[0 : len(sl)+1]
478 sl[len(sl)-1] = &EnumDescriptor{common{File: file}, desc, parent, nil}
479 return sl
480}
481
482// Return a slice of all the EnumDescriptors defined within this file
483func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
484 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000485 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700486 for _, enum := range file.EnumType {
487 sl = addEnumDescriptor(sl, enum, nil, file)
488 }
David Symonds5256cf62010-06-27 10:33:42 +1000489 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700490 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000491 for _, enum := range nested.EnumType {
492 sl = addEnumDescriptor(sl, enum, nested, file)
493 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700494 }
495 return sl
496}
497
498// Return a slice of all the top-level ExtensionDescriptors defined within this file.
499func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
500 sl := make([]*ExtensionDescriptor, len(file.Extension))
501 for i, field := range file.Extension {
502 sl[i] = &ExtensionDescriptor{common{File: file}, field, nil}
503 }
504 return sl
505}
506
Rob Pikec9e7d972010-06-10 10:30:22 -0700507// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700508// The key names for the map come from the input data, which puts a period at the beginning.
509// It should be called after SetPackageNames and before GenerateAllFiles.
510func (g *Generator) BuildTypeNameMap() {
511 g.typeNameToObject = make(map[string]Object)
512 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700513 // The names in this loop are defined by the proto world, not us, so the
514 // package name may be empty. If so, the dotted package name of X will
515 // be ".X"; otherwise it will be ".pkg.X".
516 dottedPkg := "." + proto.GetString(f.Package)
517 if dottedPkg != "." {
518 dottedPkg += "."
519 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700520 for _, enum := range f.enum {
521 name := dottedPkg + dottedSlice(enum.TypeName())
522 g.typeNameToObject[name] = enum
523 }
524 for _, desc := range f.desc {
525 name := dottedPkg + dottedSlice(desc.TypeName())
526 g.typeNameToObject[name] = desc
527 }
528 }
529}
530
531// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
532// returns the descriptor for the message or enum with that name.
533func (g *Generator) ObjectNamed(typeName string) Object {
534 f, ok := g.typeNameToObject[typeName]
535 if !ok {
536 g.Fail("can't find object with type", typeName)
537 }
538 return f
539}
540
541// P prints the arguments to the generated output. It handles strings and int32s, plus
542// handling indirections because they may be *string, etc.
543func (g *Generator) P(str ...interface{}) {
544 g.WriteString(g.indent)
545 for _, v := range str {
546 switch s := v.(type) {
547 case string:
548 g.WriteString(s)
549 case *string:
550 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700551 case bool:
552 g.WriteString(fmt.Sprintf("%t", s))
553 case *bool:
554 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700555 case *int32:
556 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700557 case float64:
558 g.WriteString(fmt.Sprintf("%g", s))
559 case *float64:
560 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700561 default:
562 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
563 }
564 }
565 g.WriteByte('\n')
566}
567
568// In Indents the output one tab stop.
569func (g *Generator) In() { g.indent += "\t" }
570
571// Out unindents the output one tab stop.
572func (g *Generator) Out() {
573 if len(g.indent) > 0 {
574 g.indent = g.indent[1:]
575 }
576}
577
578// GenerateAllFiles generates the output for all the files we're outputting.
579func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700580 // Initialize the plugins
581 for _, p := range plugins {
582 p.Init(g)
583 }
584 // Generate the output.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700585 for i, file := range g.genFiles {
586 g.Reset()
587 g.generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700588 g.Response.File[i] = plugin.NewCodeGeneratorResponse_File()
589 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
590 g.Response.File[i].Content = proto.String(g.String())
591 }
592}
593
594// Run all the plugins associated with the file.
595func (g *Generator) runPlugins(file *FileDescriptor) {
596 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700597 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700598 }
599}
600
601
602// FileOf return the FileDescriptor for this FileDescriptorProto.
603func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
604 for _, file := range g.allFiles {
605 if file.FileDescriptorProto == fd {
606 return file
607 }
608 }
609 g.Fail("could not find file in table:", proto.GetString(fd.Name))
610 return nil
611}
612
613// Fill the response protocol buffer with the generated output for all the files we're
614// supposed to generate.
615func (g *Generator) generate(file *FileDescriptor) {
616 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000617 g.usedPackages = make(map[string]bool)
618
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700619 for _, enum := range g.file.enum {
620 g.generateEnum(enum)
621 }
622 for _, desc := range g.file.desc {
623 g.generateMessage(desc)
624 }
625 for _, ext := range g.file.ext {
626 g.generateExtension(ext)
627 }
628 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000629
Rob Pikec9e7d972010-06-10 10:30:22 -0700630 // Run the plugins before the imports so we know which imports are necessary.
631 g.runPlugins(file)
632
David Symondsf90e3382010-05-05 10:53:44 +1000633 // Generate header and imports last, though they appear first in the output.
634 rem := g.Buffer
635 g.Buffer = new(bytes.Buffer)
636 g.generateHeader()
637 g.generateImports()
638 g.Write(rem.Bytes())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700639}
640
641// Generate the header, including package definition and imports
642func (g *Generator) generateHeader() {
643 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
644 g.P("// DO NOT EDIT!")
645 g.P()
646 g.P("package ", g.file.PackageName())
647 g.P()
648}
649
650// Generate the header, including package definition and imports
651func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700652 // We almost always need a proto import. Rather than computing when we
653 // do, which is tricky when there's a plugin, just import it and
654 // reference it later.
Rob Pike809831a2010-06-16 10:10:58 -0700655 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700656 for _, s := range g.file.Dependency {
657 // Need to find the descriptor for this file
658 for _, fd := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700659 // Do not import our own package.
660 if fd.PackageName() == g.packageName {
661 continue
662 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700663 if proto.GetString(fd.Name) == s {
664 filename := goFileName(s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700665 if substitution, ok := g.ImportMap[s]; ok {
666 filename = substitution
667 }
668 filename = g.ImportPrefix + filename
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700669 if strings.HasSuffix(filename, ".go") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700670 filename = filename[0 : len(filename)-3]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700671 }
David Symondsf90e3382010-05-05 10:53:44 +1000672 if _, ok := g.usedPackages[fd.PackageName()]; ok {
673 g.P("import ", fd.PackageName(), " ", Quote(filename))
674 } else {
Rob Pike5194c512010-10-14 13:02:16 -0700675 log.Println("protoc-gen-go: discarding unused import:", filename)
David Symondsf90e3382010-05-05 10:53:44 +1000676 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700677 break
678 }
679 }
680 }
681 g.P()
682 // TODO: may need to worry about uniqueness across plugins
683 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700684 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700685 g.P()
686 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700687 g.P("// Reference proto import to suppress error if it's not otherwise used.")
688 g.P("var _ = ", g.ProtoPkg, ".GetString")
689 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700690}
691
692// Generate the enum definitions for this EnumDescriptor.
693func (g *Generator) generateEnum(enum *EnumDescriptor) {
694 // The full type name
695 typeName := enum.TypeName()
696 // The full type name, CamelCased.
697 ccTypeName := CamelCaseSlice(typeName)
698 ccPrefix := enum.prefix()
699 g.P("type ", ccTypeName, " int32")
700 g.P("const (")
701 g.In()
702 for _, e := range enum.Value {
703 g.P(ccPrefix+*e.Name, " = ", e.Number)
704 }
705 g.Out()
706 g.P(")")
707 g.P("var ", ccTypeName, "_name = map[int32] string {")
708 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700709 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700710 for _, e := range enum.Value {
711 duplicate := ""
712 if _, present := generated[*e.Number]; present {
713 duplicate = "// Duplicate value: "
714 }
715 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
716 generated[*e.Number] = true
717 }
718 g.Out()
719 g.P("}")
720 g.P("var ", ccTypeName, "_value = map[string] int32 {")
721 g.In()
722 for _, e := range enum.Value {
723 g.P(Quote(*e.Name), ": ", e.Number, ",")
724 }
725 g.Out()
726 g.P("}")
727 g.P("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
728 g.In()
729 g.P("e := ", ccTypeName, "(x)")
730 g.P("return &e")
731 g.Out()
732 g.P("}")
733 g.P()
734}
735
736// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
737// identifies details of the field for the protocol buffer marshaling and unmarshaling
738// code. The fields are:
739// wire encoding
740// protocol tag number
741// opt,req,rep for optional, required, or repeated
742// name= the original declared name
743// enum= the name of the enum type if it is an enum-typed field.
744// def= string representation of the default value, if any.
745// The default value must be in a representation that can be used at run-time
746// to generate the default value. Thus bools become 0 and 1, for instance.
747func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
748 optrepreq := ""
749 switch {
750 case isOptional(field):
751 optrepreq = "opt"
752 case isRequired(field):
753 optrepreq = "req"
754 case isRepeated(field):
755 optrepreq = "rep"
756 }
757 defaultValue := proto.GetString(field.DefaultValue)
758 if defaultValue != "" {
759 switch *field.Type {
760 case descriptor.FieldDescriptorProto_TYPE_BOOL:
761 if defaultValue == "true" {
762 defaultValue = "1"
763 } else {
764 defaultValue = "0"
765 }
766 case descriptor.FieldDescriptorProto_TYPE_STRING,
767 descriptor.FieldDescriptorProto_TYPE_BYTES:
768 // Protect frogs.
769 defaultValue = Quote(defaultValue)
770 // Don't need the quotes
771 defaultValue = defaultValue[1 : len(defaultValue)-1]
772 case descriptor.FieldDescriptorProto_TYPE_ENUM:
773 // For enums we need to provide the integer constant.
774 obj := g.ObjectNamed(proto.GetString(field.TypeName))
775 enum, ok := obj.(*EnumDescriptor)
776 if !ok {
777 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
778 }
779 defaultValue = enum.integerValueAsString(defaultValue)
780 }
781 defaultValue = ",def=" + defaultValue
782 }
783 enum := ""
784 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
785 obj := g.ObjectNamed(proto.GetString(field.TypeName))
786 enum = ",enum=" + obj.PackageName() + "." + CamelCaseSlice(obj.TypeName())
787 }
788 name := proto.GetString(field.Name)
789 if name == CamelCase(name) {
790 name = ""
791 } else {
792 name = ",name=" + name
793 }
794 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
795 wiretype,
796 proto.GetInt32(field.Number),
797 optrepreq,
798 name,
799 enum,
800 defaultValue))
801}
802
803func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
804 switch typ {
805 case descriptor.FieldDescriptorProto_TYPE_GROUP:
806 return false
807 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
808 return false
809 case descriptor.FieldDescriptorProto_TYPE_BYTES:
810 return false
811 }
812 return true
813}
814
815// TypeName is the printed name appropriate for an item. If the object is in the current file,
816// TypeName drops the package name and underscores the rest.
817// Otherwise the object is from another package; and the result is the underscored
818// package name followed by the item name.
819// The result always has an initial capital.
820func (g *Generator) TypeName(obj Object) string {
821 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
822}
823
824// TypeNameWithPackage is like TypeName, but always includes the package
825// name even if the object is in our own package.
826func (g *Generator) TypeNameWithPackage(obj Object) string {
827 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
828}
829
830// GoType returns a string representing the type name, and the wire type
831func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
832 // TODO: Options.
833 switch *field.Type {
834 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
835 typ, wire = "float64", "fixed64"
836 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
837 typ, wire = "float32", "fixed32"
838 case descriptor.FieldDescriptorProto_TYPE_INT64:
839 typ, wire = "int64", "varint"
840 case descriptor.FieldDescriptorProto_TYPE_UINT64:
841 typ, wire = "uint64", "varint"
842 case descriptor.FieldDescriptorProto_TYPE_INT32:
843 typ, wire = "int32", "varint"
844 case descriptor.FieldDescriptorProto_TYPE_UINT32:
845 typ, wire = "uint32", "varint"
846 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
847 typ, wire = "uint64", "fixed64"
848 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
849 typ, wire = "uint32", "fixed32"
850 case descriptor.FieldDescriptorProto_TYPE_BOOL:
851 typ, wire = "bool", "varint"
852 case descriptor.FieldDescriptorProto_TYPE_STRING:
853 typ, wire = "string", "bytes"
854 case descriptor.FieldDescriptorProto_TYPE_GROUP:
855 desc := g.ObjectNamed(proto.GetString(field.TypeName))
856 typ, wire = "*"+g.TypeName(desc), "group"
857 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
858 desc := g.ObjectNamed(proto.GetString(field.TypeName))
859 typ, wire = "*"+g.TypeName(desc), "bytes"
860 case descriptor.FieldDescriptorProto_TYPE_BYTES:
861 typ, wire = "[]byte", "bytes"
862 case descriptor.FieldDescriptorProto_TYPE_ENUM:
863 desc := g.ObjectNamed(proto.GetString(field.TypeName))
864 typ, wire = g.TypeName(desc), "varint"
865 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
866 typ, wire = "int32", "fixed32"
867 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
868 typ, wire = "int64", "fixed64"
869 case descriptor.FieldDescriptorProto_TYPE_SINT32:
870 typ, wire = "int32", "zigzag32"
871 case descriptor.FieldDescriptorProto_TYPE_SINT64:
872 typ, wire = "int64", "zigzag64"
873 default:
874 g.Fail("unknown type for", proto.GetString(field.Name))
875 }
876 if isRepeated(field) {
877 typ = "[]" + typ
878 } else if needsStar(*field.Type) {
879 typ = "*" + typ
880 }
881 return
882}
883
David Symondsf90e3382010-05-05 10:53:44 +1000884func (g *Generator) RecordTypeUse(t string) {
885 if obj, ok := g.typeNameToObject[t]; ok {
886 g.usedPackages[obj.PackageName()] = true
887 }
888}
889
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700890// Generate the type and default constant definitions for this Descriptor.
891func (g *Generator) generateMessage(message *Descriptor) {
892 // The full type name
893 typeName := message.TypeName()
894 // The full type name, CamelCased.
895 ccTypeName := CamelCaseSlice(typeName)
896
897 g.P("type ", ccTypeName, " struct {")
898 g.In()
899 for _, field := range message.Field {
900 fieldname := CamelCase(*field.Name)
901 typename, wiretype := g.GoType(message, field)
902 tag := g.goTag(field, wiretype)
903 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +1000904 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700905 }
906 if len(message.ExtensionRange) > 0 {
907 g.P("XXX_extensions\t\tmap[int32][]byte")
908 }
909 g.P("XXX_unrecognized\t[]byte")
910 g.Out()
911 g.P("}")
912
Rob Pikec6d8e4a2010-07-28 15:34:32 -0700913 // Reset function
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700914 g.P("func (this *", ccTypeName, ") Reset() {")
915 g.In()
916 g.P("*this = ", ccTypeName, "{}")
917 g.Out()
918 g.P("}")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700919
920 // Extension support methods
921 if len(message.ExtensionRange) > 0 {
922 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -0700923 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700924 g.In()
925 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -0700926 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
927 g.P(g.ProtoPkg+".ExtensionRange{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700928 }
929 g.Out()
930 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -0700931 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700932 g.In()
933 g.P("return extRange_", ccTypeName)
934 g.Out()
935 g.P("}")
936 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32][]byte {")
937 g.In()
938 g.P("if this.XXX_extensions == nil {")
939 g.In()
940 g.P("this.XXX_extensions = make(map[int32][]byte)")
941 g.Out()
942 g.P("}")
943 g.P("return this.XXX_extensions")
944 g.Out()
945 g.P("}")
946 }
947
948 // Default constants
949 for _, field := range message.Field {
950 def := proto.GetString(field.DefaultValue)
951 if def == "" {
952 continue
953 }
954 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
955 typename, _ := g.GoType(message, field)
956 if typename[0] == '*' {
957 typename = typename[1:]
958 }
959 kind := "const "
960 switch {
961 case typename == "bool":
962 case typename == "string":
963 def = Quote(def)
964 case typename == "[]byte":
965 def = "[]byte(" + Quote(def) + ")"
966 kind = "var "
967 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
968 // Must be an enum. Need to construct the prefixed name.
969 obj := g.ObjectNamed(proto.GetString(field.TypeName))
970 enum, ok := obj.(*EnumDescriptor)
971 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -0700972 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700973 continue
974 }
Rob Pike87af39e2010-07-19 10:48:02 -0700975 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700976 }
977 g.P(kind, fieldname, " ", typename, " = ", def)
978 }
979 g.P()
980
981 for _, ext := range message.ext {
982 g.generateExtension(ext)
983 }
984}
985
986func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
987 // The full type name
988 typeName := ext.TypeName()
989 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
990 for i, s := range typeName {
991 typeName[i] = CamelCase(s)
992 }
993 ccTypeName := "E_" + strings.Join(typeName, "_")
994
995 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
996 field := ext.FieldDescriptorProto
997 fieldType, wireType := g.GoType(ext.parent, field)
998 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +1000999 g.RecordTypeUse(*ext.Extendee)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001000
Rob Pikec9e7d972010-06-10 10:30:22 -07001001 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001002 g.In()
1003 g.P("ExtendedType: (", extendedType, ")(nil),")
1004 g.P("ExtensionType: (", fieldType, ")(nil),")
1005 g.P("Field: ", field.Number, ",")
1006 g.P("Tag: ", tag, ",")
1007
1008 g.Out()
1009 g.P("}")
1010 g.P()
1011}
1012
1013func (g *Generator) generateInitFunction() {
1014 g.P("func init() {")
1015 g.In()
1016 for _, enum := range g.file.enum {
1017 g.generateEnumRegistration(enum)
1018 }
1019 g.Out()
1020 g.P("}")
1021}
1022
1023func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
1024 pkg := g.packageName + "." // We always print the full package name here.
1025 // The full type name
1026 typeName := enum.TypeName()
1027 // The full type name, CamelCased.
1028 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001029 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001030}
1031
1032// And now lots of helper functions.
1033
Rob Pike2c7bafc2010-06-10 16:07:14 -07001034// Is c an ASCII lower-case letter?
1035func isASCIILower(c byte) bool {
1036 return 'a' <= c && c <= 'z'
1037}
1038
1039// Is c an ASCII digit?
1040func isASCIIDigit(c byte) bool {
1041 return '0' <= c && c <= '9'
1042}
1043
1044// CamelCase returns the CamelCased name.
1045// If there is an interior underscore followed by a lower case letter,
1046// drop the underscore and convert the letter to upper case.
1047// There is a remote possibility of this rewrite causing a name collision,
1048// but it's so remote we're prepared to pretend it's nonexistent - since the
1049// C++ generator lowercases names, it's extremely unlikely to have two fields
1050// with different capitalizations.
1051// In short, _my_field_name_2 becomes XMyFieldName2.
1052func CamelCase(s string) string {
1053 if s == "" {
1054 return ""
1055 }
1056 t := make([]byte, 0, 32)
1057 oneC := make([]byte, 1)
1058 i := 0
1059 if s[0] == '_' {
1060 // Need a capital letter; drop the '_'.
1061 oneC[0] = 'X'
1062 t = bytes.Add(t, oneC)
1063 i++
1064 }
1065 // Invariant: if the next letter is lower case, it must be converted
1066 // to upper case.
1067 // That is, we process a word at a time, where words are marked by _ or
1068 // upper case letter. Digits are treated as words.
1069 for ; i < len(s); i++ {
1070 c := s[i]
1071 oneC[0] = c
1072 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1073 continue // Skip the underscore in s.
1074 }
1075 if isASCIIDigit(c) {
1076 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001077 continue
1078 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001079 // Assume we have a letter now - if not, it's a bogus identifier.
1080 // The next word is a sequence of characters that must start upper case.
1081 if isASCIILower(c) {
1082 oneC[0] ^= ' ' // Make it a capital letter.
1083 }
1084 t = bytes.Add(t, oneC) // Guaranteed not lower case.
1085 // Accept lower case sequence that follows.
1086 for i+1 < len(s) && isASCIILower(s[i+1]) {
1087 i++
1088 oneC[0] = s[i]
1089 t = bytes.Add(t, oneC)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001090 }
1091 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001092 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001093}
1094
1095// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1096// be joined with "_".
1097func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1098
1099// dottedSlice turns a sliced name into a dotted name.
1100func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1101
1102// Quote returns a Go-source quoted string representation of s.
1103func Quote(s string) string { return fmt.Sprintf("%q", s) }
1104
1105// Given a .proto file name, return the output name for the generated Go program.
1106func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001107 ext := path.Ext(name)
1108 if ext == ".proto" || ext == ".protodevel" {
1109 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001110 }
1111 return name + ".pb.go"
1112}
1113
1114// Is this field optional?
1115func isOptional(field *descriptor.FieldDescriptorProto) bool {
1116 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1117}
1118
1119// Is this field required?
1120func isRequired(field *descriptor.FieldDescriptorProto) bool {
1121 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1122}
1123
1124// Is this field repeated?
1125func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1126 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1127}
1128
1129// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001130// which can be dotted in the input .proto file. It maps dots to underscores.
1131// Because we also get here from package names generated from file names, it also maps
1132// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001133func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001134 switch rune {
1135 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001136 return '_'
1137 }
1138 return rune
1139}
Rob Pikec9e7d972010-06-10 10:30:22 -07001140
1141// BaseName returns the last path element of the name, with the last dotted suffix removed.
1142func BaseName(name string) string {
1143 // First, find the last element
1144 if i := strings.LastIndex(name, "/"); i >= 0 {
1145 name = name[i+1:]
1146 }
1147 // Now drop the suffix
1148 if i := strings.LastIndex(name, "."); i >= 0 {
1149 name = name[0:i]
1150 }
1151 return name
1152}