blob: f2cb3b24852978d48cb738e676aa86bea7213b63 [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -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 A plugin for the Google protocol buffer compiler to generate Go code.
34
35 This plugin takes no options and the protocol buffer file syntax does
36 not yet define any options for Go, so program does no option evaluation.
37 That may change.
38
39 Not supported yet:
40 Extensions
41 Services
42*/
43
44package main
45
46import (
47 "bytes"
48 "fmt"
49 "io/ioutil"
50 "log"
51 "os"
52 "strings"
53 "unicode"
54
55 "goprotobuf.googlecode.com/hg/proto"
56 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
57 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
58)
59
60func main() {
61 // Begin by allocating a generator. The request and response structures are stored there
62 // so we can do error handling easily - the response structure contains the field to
63 // report failure.
64 g := NewGenerator()
65
66 data, err := ioutil.ReadAll(os.Stdin)
67 if err != nil {
68 g.error(err, "reading input")
69 }
70
71 if err := proto.Unmarshal(data, g.request); err != nil {
72 g.error(err, "parsing input proto")
73 }
74
75 if len(g.request.FileToGenerate) == 0 {
76 g.fail("no files to generate")
77 }
78
79 // Create a wrapped version of the Descriptors and EnumDescriptors that
80 // point to the file that defines them.
81 g.WrapTypes()
82
83 g.SetPackageNames()
84 g.BuildTypeNameMap()
85
86 g.GenerateAllFiles()
87
88 // Send back the results.
89 data, err = proto.Marshal(g.response)
90 if err != nil {
91 g.error(err, "failed to marshal output proto")
92 }
93 _, err = os.Stdout.Write(data)
94 if err != nil {
95 g.error(err, "failed to write output proto")
96 }
97}
98
99// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
100// a pointer to the FileDescriptorProto that represents it. These types achieve that
101// wrapping by placing each Proto inside a struct with the pointer to its File. The
102// structs have the same names as their contents, with "Proto" removed.
103// FileDescriptor is used to store the things that it points to.
104
105// The file and package name method are common to messages and enums.
106type common struct {
107 file *descriptor.FileDescriptorProto // File this object comes from.
108}
109
110// The package name we will produce in our output.
111func (c *common) packageName() string { return uniquePackageOf(c.file) }
112
113// A message (struct).
114type Descriptor struct {
115 common
116 *descriptor.DescriptorProto
117 parent *Descriptor // The containing message, if any.
118 nested []*Descriptor // Inner messages, if any.
119 typename []string // Cached typename vector.
120}
121
122// Return the elements of the dotted type name. The package name is not part
123// of this name.
124func (d *Descriptor) typeName() []string {
125 if d.typename != nil {
126 return d.typename
127 }
128 n := 0
129 for parent := d; parent != nil; parent = parent.parent {
130 n++
131 }
132 s := make([]string, n, n)
133 for parent := d; parent != nil; parent = parent.parent {
134 n--
135 s[n] = proto.GetString(parent.Name)
136 }
137 d.typename = s
138 return s
139}
140
141// An enum. If it's at top level, its parent will be nil. Otherwise it will be
142// the descriptor of the message in which it is defined.
143type EnumDescriptor struct {
144 common
145 *descriptor.EnumDescriptorProto
146 parent *Descriptor // The containing message, if any.
147 typename []string // Cached typename vector.
148}
149
150// Return the elements of the dotted type name.
151func (e *EnumDescriptor) typeName() (s []string) {
152 if e.typename != nil {
153 return e.typename
154 }
155 name := proto.GetString(e.Name)
156 if e.parent == nil {
157 s = make([]string, 1)
158 } else {
159 pname := e.parent.typeName()
160 s = make([]string, len(pname)+1)
161 copy(s, pname)
162 }
163 s[len(s)-1] = name
164 e.typename = s
165 return s
166}
167
168// Everything but the last element of the full type name, CamelCased.
169// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
170func (e *EnumDescriptor) prefix() string {
171 typeName := e.typeName()
172 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
173 if e.parent == nil {
174 // If the enum is not part of a message, the prefix is just the type name.
175 ccPrefix = CamelCase(*e.Name) + "_"
176 }
177 return ccPrefix
178}
179
180// The integer value of the named constant in this enumerated type.
181func (e *EnumDescriptor) integerValueAsString(name string) string {
182 for _, c := range e.Value {
183 if proto.GetString(c.Name) == name {
184 return fmt.Sprint(proto.GetInt32(c.Number))
185 }
186 }
187 log.Exit("cannot find value for enum constant")
188 return ""
189}
190
191// A file. Includes slices of all the messages and enums defined within it.
192// Those slices are constructed by WrapTypes.
193type FileDescriptor struct {
194 *descriptor.FileDescriptorProto
195 desc []*Descriptor // All the messages defined in this file.
196 enum []*EnumDescriptor // All the enums defined in this file.
197}
198
199// The package name we'll use in the generated code to refer to this file.
200func (d *FileDescriptor) packageName() string { return uniquePackageOf(d.FileDescriptorProto) }
201
202// The package named defined in the input for this file, possibly dotted.
203func (d *FileDescriptor) originalPackageName() string {
204 return proto.GetString(d.Package)
205}
206
207// Simplify some things by abstracting the abilities shared by enums and messages.
208type Object interface {
209 packageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
210 typeName() []string
211}
212
213// Each package name we generate must be unique. The package we're generating
214// gets its own name but every other package must have a unqiue name that does
215// not conflict in the code we generate. These names are chosen globally (although
216// they don't have to be, it simplifies things to do them globally).
217func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
218 s, ok := uniquePackageName[fd]
219 if !ok {
220 log.Exit("internal error: no package name defined for", proto.GetString(fd.Name))
221 }
222 return s
223}
224
225// The type whose methods generate the output, stored in the associated response structure.
226type Generator struct {
227 bytes.Buffer
228
229 request *plugin.CodeGeneratorRequest // The input.
230 response *plugin.CodeGeneratorResponse // The output.
231
232 packageName string // What we're calling ourselves.
233 allFiles []*FileDescriptor // All files in the tree
234 genFiles []*FileDescriptor // Those files we will generate output for.
235 file *FileDescriptor // The file we are compiling now.
236 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
237 indent string
238}
239
240// Create a new generator and allocate the request and response protobufs.
241func NewGenerator() *Generator {
242 g := new(Generator)
243 g.request = plugin.NewCodeGeneratorRequest()
244 g.response = plugin.NewCodeGeneratorResponse()
245 return g
246}
247
248// Report problem, including an os.Error, and fail.
249func (g *Generator) error(err os.Error, msgs ...string) {
250 s := strings.Join(msgs, " ") + ":" + err.String()
251 log.Stderr("protoc-gen-go: error: ", s)
252 g.response.Error = proto.String(s)
253 os.Exit(1)
254}
255
256// Report problem and fail.
257func (g *Generator) fail(msgs ...string) {
258 s := strings.Join(msgs, " ")
259 log.Stderr("protoc-gen-go: error: ", s)
260 g.response.Error = proto.String(s)
261 os.Exit(1)
262}
263
264// If this file is in a different package, return the package name we're using for this file, plus ".".
265// Otherwise return the empty string.
266func (g *Generator) DefaultPackageName(obj Object) string {
267 pkg := obj.packageName()
268 if pkg == g.packageName {
269 return ""
270 }
271 return pkg + "."
272}
273
274// For each input file, the unique package name to use, underscored.
275var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
276
277// Set the package name for this run. It must agree across all files being generated.
278// Also define unique package names for all imported files.
279func (g *Generator) SetPackageNames() {
280 inUse := make(map[string]bool)
281 pkg := proto.GetString(g.genFiles[0].Package)
282 g.packageName = strings.Map(DotToUnderscore, pkg)
283 inUse[pkg] = true
284 for _, f := range g.genFiles {
285 thisPkg := proto.GetString(f.Package)
286 if thisPkg != pkg {
287 g.fail("inconsistent package names:", thisPkg, pkg)
288 }
289 }
290AllFiles:
291 for _, f := range g.allFiles {
292 for _, genf := range g.genFiles {
293 if f == genf {
294 // In this package already.
295 uniquePackageName[f.FileDescriptorProto] = g.packageName
296 continue AllFiles
297 }
298 }
299 truePkg := proto.GetString(f.Package)
300 pkg := truePkg
301 for {
302 _, present := inUse[pkg]
303 if present {
304 // It's a duplicate; must rename.
305 pkg += "X"
306 continue
307 }
308 break
309 }
310 // Install it.
311 if pkg != truePkg {
312 log.Stderr("renaming duplicate imported package named", truePkg, "to", pkg)
313 }
314 inUse[pkg] = true
315 uniquePackageName[f.FileDescriptorProto] = strings.Map(DotToUnderscore, pkg)
316 }
317}
318
319// Walk the incoming data, wrapping DescriptorProtos and EnumDescriptorProtos
320// into file-referenced objects within the Generator. Also create the list of files
321// to generate
322func (g *Generator) WrapTypes() {
323 g.allFiles = make([]*FileDescriptor, len(g.request.ProtoFile))
324 for i, f := range g.request.ProtoFile {
325 pkg := proto.GetString(f.Package)
326 if pkg == "" {
327 g.fail(proto.GetString(f.Name), "is missing a package declaration")
328 }
329 // We must wrap the descriptors before we wrap the enums
330 descs := WrapDescriptors(f)
331 g.BuildNestedDescriptors(descs)
332 enums := WrapEnumDescriptors(f, descs)
333 g.allFiles[i] = &FileDescriptor{
334 FileDescriptorProto: f,
335 desc: descs,
336 enum: enums,
337 }
338 }
339
340 g.genFiles = make([]*FileDescriptor, len(g.request.FileToGenerate))
341FindFiles:
342 for i, fileName := range g.request.FileToGenerate {
343 // Search the list. This algorithm is n^2 but n is tiny.
344 for _, file := range g.allFiles {
345 if fileName == proto.GetString(file.Name) {
346 g.genFiles[i] = file
347 continue FindFiles
348 }
349 }
350 g.fail("could not find file named", fileName)
351 }
352 g.response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
353}
354
355// Scan the descriptors in this file. For each one, build the slice of nested descriptors
356func (g *Generator) BuildNestedDescriptors(descs []*Descriptor) {
357 for _, desc := range descs {
358 if len(desc.NestedType) != 0 {
359 desc.nested = make([]*Descriptor, len(desc.NestedType))
360 n := 0
361 for _, nest := range descs {
362 if nest.parent == desc {
363 desc.nested[n] = nest
364 n++
365 }
366 }
367 if n != len(desc.NestedType) {
368 g.fail("internal error: nesting failure for", proto.GetString(desc.Name))
369 }
370 }
371 }
372}
373
374// Construct the Descriptor and add it to the slice
375func AddDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
376 if len(sl) == cap(sl) {
377 nsl := make([]*Descriptor, len(sl), 2*len(sl))
378 copy(nsl, sl)
379 sl = nsl
380 }
381 sl = sl[0 : len(sl)+1]
382 sl[len(sl)-1] = &Descriptor{common{file: file}, desc, parent, nil, nil}
383 return sl
384}
385
386// Return a slice of all the Descriptors defined within this file
387func WrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
388 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
389 for _, desc := range file.MessageType {
390 sl = WrapThisDescriptor(sl, desc, nil, file)
391 }
392 return sl
393}
394
395// Wrap this Descriptor, recursively
396func WrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
397 sl = AddDescriptor(sl, desc, parent, file)
398 me := sl[len(sl)-1]
399 for _, nested := range desc.NestedType {
400 sl = WrapThisDescriptor(sl, nested, me, file)
401 }
402 return sl
403}
404
405// Construct the EnumDescriptor and add it to the slice
406func AddEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
407 if len(sl) == cap(sl) {
408 nsl := make([]*EnumDescriptor, len(sl), 2*len(sl))
409 copy(nsl, sl)
410 sl = nsl
411 }
412 sl = sl[0 : len(sl)+1]
413 sl[len(sl)-1] = &EnumDescriptor{common{file: file}, desc, parent, nil}
414 return sl
415}
416
417// Return a slice of all the EnumDescriptors defined within this file
418func WrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
419 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
420 for _, enum := range file.EnumType {
421 sl = AddEnumDescriptor(sl, enum, nil, file)
422 }
423 for _, nested := range descs {
424 sl = WrapEnumDescriptorsInMessage(sl, nested, file)
425 }
426 return sl
427}
428
429// Wrap this EnumDescriptor, recursively
430func WrapEnumDescriptorsInMessage(sl []*EnumDescriptor, desc *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
431 for _, enum := range desc.EnumType {
432 sl = AddEnumDescriptor(sl, enum, desc, file)
433 }
434 for _, nested := range desc.nested {
435 sl = WrapEnumDescriptorsInMessage(sl, nested, file)
436 }
437 return sl
438}
439
440// Build the map from fully qualified type names to objects. The key for the map
441// comes from the input data, which puts a period at the beginning.
442func (g *Generator) BuildTypeNameMap() {
443 g.typeNameToObject = make(map[string]Object)
444 for _, f := range g.allFiles {
445 dottedPkg := "." + f.originalPackageName() + "."
446 for _, enum := range f.enum {
447 name := dottedPkg + DottedSlice(enum.typeName())
448 g.typeNameToObject[name] = enum
449 }
450 for _, desc := range f.desc {
451 name := dottedPkg + DottedSlice(desc.typeName())
452 g.typeNameToObject[name] = desc
453 }
454 }
455}
456
457// Given a fully-qualified input type name, return the descriptor for the message or enum with that type.
458func (g *Generator) objectNamed(typeName string) Object {
459 f, ok := g.typeNameToObject[typeName]
460 if !ok {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700461 g.fail("can't find object with type", typeName)
462 }
463 return f
464}
465
466// Print the arguments, handling indirections because they may be *string, etc.
467func (g *Generator) p(str ...interface{}) {
468 g.WriteString(g.indent)
469 for _, v := range str {
470 switch s := v.(type) {
471 case string:
472 g.WriteString(s)
473 case *string:
474 g.WriteString(*s)
475 case *int32:
476 g.WriteString(fmt.Sprintf("%d", *s))
477 default:
478 g.fail(fmt.Sprintf("unknown type in printer: %T", v))
479 }
480 }
481 g.WriteByte('\n')
482}
483
484// Indent the output one tab stop.
485func (g *Generator) in() { g.indent += "\t" }
486
487// Unindent the output one tab stop.
488func (g *Generator) out() {
489 if len(g.indent) > 0 {
490 g.indent = g.indent[1:]
491 }
492}
493
494// Generate the output for all the files we're generating output for.
495func (g *Generator) GenerateAllFiles() {
496 for i, file := range g.genFiles {
497 g.Reset()
498 g.Generate(file)
499 g.response.File[i] = plugin.NewCodeGeneratorResponse_File()
500 g.response.File[i].Name = proto.String(GoName(*file.Name))
501 g.response.File[i].Content = proto.String(g.String())
502 }
503}
504
505// Return the FileDescriptor for this FileDescriptorProto
506func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
507 for _, file := range g.allFiles {
508 if file.FileDescriptorProto == fd {
509 return file
510 }
511 }
512 g.fail("could not find file in table:", proto.GetString(fd.Name))
513 return nil
514}
515
516// Fill the response protocol buffer with the generated output for all the files we're
517// supposed to generate.
518func (g *Generator) Generate(file *FileDescriptor) {
519 g.file = g.FileOf(file.FileDescriptorProto)
520 g.GenerateHeader()
521 g.GenerateImports()
522 for _, enum := range g.file.enum {
523 g.GenerateEnum(enum)
524 }
525 for _, desc := range g.file.desc {
526 g.GenerateMessage(desc)
527 }
528 g.GenerateInitFunction()
529}
530
531// Generate the header, including package definition and imports
532func (g *Generator) GenerateHeader() {
533 g.p("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
534 g.p("// DO NOT EDIT!")
535 g.p()
536 g.p("package ", g.file.packageName())
537 g.p()
538}
539
540// Generate the header, including package definition and imports
541func (g *Generator) GenerateImports() {
542 if len(g.file.enum) > 0 {
543 g.p(`import "goprotobuf.googlecode.com/hg/proto"`)
544 }
545 for _, s := range g.file.Dependency {
546 // Need to find the descriptor for this file
547 for _, fd := range g.allFiles {
548 if proto.GetString(fd.Name) == s {
549 filename := GoName(s)
550 if strings.HasSuffix(filename, ".go") {
551 filename = filename[0:len(filename)-3]
552 }
553 g.p("import ", fd.packageName(), " ", Quote(filename))
554 break
555 }
556 }
557 }
558 g.p()
559}
560
561// Generate the enum definitions for this EnumDescriptor.
562func (g *Generator) GenerateEnum(enum *EnumDescriptor) {
563 // The full type name
564 typeName := enum.typeName()
565 // The full type name, CamelCased.
566 ccTypeName := CamelCaseSlice(typeName)
567 ccPrefix := enum.prefix()
568 g.p("type ", ccTypeName, " int32")
569 g.p("const (")
570 g.in()
571 for _, e := range enum.Value {
572 g.p(ccPrefix+*e.Name, " = ", e.Number)
573 }
574 g.out()
575 g.p(")")
576 g.p("var ", ccTypeName, "_name = map[int32] string {")
577 g.in()
578 generated := make(map[int32] bool) // avoid duplicate values
579 for _, e := range enum.Value {
580 duplicate := ""
581 if _, present := generated[*e.Number]; present {
582 duplicate = "// Duplicate value: "
583 }
584 g.p(duplicate, e.Number, ": ", Quote(*e.Name), ",")
585 generated[*e.Number] = true
586 }
587 g.out()
588 g.p("}")
589 g.p("var ", ccTypeName, "_value = map[string] int32 {")
590 g.in()
591 for _, e := range enum.Value {
592 g.p(Quote(*e.Name), ": ", e.Number, ",")
593 }
594 g.out()
595 g.p("}")
596 g.p("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
597 g.in()
598 g.p("e := ", ccTypeName, "(x)")
599 g.p("return &e")
600 g.out()
601 g.p("}")
602 g.p()
603}
604
605// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
606// identifies details of the field for the protocol buffer marshaling and unmarshaling
607// code. The fields are:
608// wire encoding
609// protocol tag number
610// opt,req,rep for optional, required, or repeated
611// name= the original declared name
612// enum= the name of the enum type if it is an enum-typed field.
613// def= string representation of the default value, if any.
614// The default value must be in a representation that can be used at run-time
615// to generate the default value. Thus bools become 0 and 1, for instance.
616func (g *Generator) GoTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
617 optrepreq := ""
618 switch {
619 case IsOptional(field):
620 optrepreq = "opt"
621 case IsRequired(field):
622 optrepreq = "req"
623 case IsRepeated(field):
624 optrepreq = "rep"
625 }
626 defaultValue := proto.GetString(field.DefaultValue)
627 if defaultValue != "" {
628 switch *field.Type {
629 case descriptor.FieldDescriptorProto_TYPE_BOOL:
630 if defaultValue == "true" {
631 defaultValue = "1"
632 } else {
633 defaultValue = "0"
634 }
635 case descriptor.FieldDescriptorProto_TYPE_STRING,
636 descriptor.FieldDescriptorProto_TYPE_BYTES:
637 // Protect frogs.
638 defaultValue = Quote(defaultValue)
639 // Don't need the quotes
640 defaultValue = defaultValue[1 : len(defaultValue)-1]
641 case descriptor.FieldDescriptorProto_TYPE_ENUM:
642 // For enums we need to provide the integer constant.
643 obj := g.objectNamed(proto.GetString(field.TypeName))
644 enum, ok := obj.(*EnumDescriptor)
645 if !ok {
646 g.fail("enum type inconsistent for", CamelCaseSlice(obj.typeName()))
647 }
648 defaultValue = enum.integerValueAsString(defaultValue)
649 }
650 defaultValue = ",def=" + defaultValue
651 }
652 enum := ""
653 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
654 obj := g.objectNamed(proto.GetString(field.TypeName))
655 enum = ",enum=" + obj.packageName() + "." + CamelCaseSlice(obj.typeName())
656 }
657 name := proto.GetString(field.Name)
658 if name == CamelCase(name) {
659 name = ""
660 } else {
661 name = ",name=" + name
662 }
663 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
664 wiretype,
665 proto.GetInt32(field.Number),
666 optrepreq,
667 name,
668 enum,
669 defaultValue))
670}
671
672func NeedsStar(typ descriptor.FieldDescriptorProto_Type) bool {
673 switch typ {
674 case descriptor.FieldDescriptorProto_TYPE_GROUP:
675 return false
676 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
677 return false
678 case descriptor.FieldDescriptorProto_TYPE_BYTES:
679 return false
680 }
681 return true
682}
683
684// The type name appropriate for an item. If it's in the current file,
685// drop the package name and underscore the rest.
686// Otherwise it's from another package; use the underscored package name
687// followed by the field name. The result has an initial capital.
688func (g *Generator) TypeName(obj Object) string {
689 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.typeName())
690}
691
692// Like TypeName, but always includes the package name even if it's our own package.
693func (g *Generator) TypeNameWithPackage(obj Object) string {
694 return obj.packageName() + CamelCaseSlice(obj.typeName())
695}
696
697// Returns a string representing the type name, and the wire type
698func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
699 // TODO: Options.
700 switch *field.Type {
701 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
702 typ, wire = "float64", "fixed64"
703 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
704 typ, wire = "float32", "fixed32"
705 case descriptor.FieldDescriptorProto_TYPE_INT64:
706 typ, wire = "int64", "varint"
707 case descriptor.FieldDescriptorProto_TYPE_UINT64:
708 typ, wire = "uint64", "varint"
709 case descriptor.FieldDescriptorProto_TYPE_INT32:
710 typ, wire = "int32", "varint"
711 case descriptor.FieldDescriptorProto_TYPE_UINT32:
712 typ, wire = "uint32", "varint"
713 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
714 typ, wire = "uint64", "fixed64"
715 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
716 typ, wire = "uint32", "fixed32"
717 case descriptor.FieldDescriptorProto_TYPE_BOOL:
718 typ, wire = "bool", "varint"
719 case descriptor.FieldDescriptorProto_TYPE_STRING:
720 typ, wire = "string", "bytes"
721 case descriptor.FieldDescriptorProto_TYPE_GROUP:
722 desc := g.objectNamed(proto.GetString(field.TypeName))
723 typ, wire = "*"+g.TypeName(desc), "group"
724 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
725 desc := g.objectNamed(proto.GetString(field.TypeName))
726 typ, wire = "*"+g.TypeName(desc), "bytes"
727 case descriptor.FieldDescriptorProto_TYPE_BYTES:
728 typ, wire = "[]byte", "bytes"
729 case descriptor.FieldDescriptorProto_TYPE_ENUM:
730 desc := g.objectNamed(proto.GetString(field.TypeName))
731 typ, wire = g.TypeName(desc), "varint"
732 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
733 typ, wire = "int32", "fixed32"
734 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
735 typ, wire = "int64", "fixed64"
736 case descriptor.FieldDescriptorProto_TYPE_SINT32:
737 typ, wire = "int32", "zigzag32"
738 case descriptor.FieldDescriptorProto_TYPE_SINT64:
739 typ, wire = "int64", "zigzag64"
740 default:
741 g.fail("unknown type for", proto.GetString(field.Name))
742 }
743 if IsRepeated(field) {
744 typ = "[]" + typ
745 } else if NeedsStar(*field.Type) {
746 typ = "*" + typ
747 }
748 return
749}
750
751// Generate the type and default constant definitions for this Descriptor.
752func (g *Generator) GenerateMessage(message *Descriptor) {
753 // The full type name
754 typeName := message.typeName()
755 // The full type name, CamelCased.
756 ccTypeName := CamelCaseSlice(typeName)
757
758 g.p("type ", ccTypeName, " struct {")
759 g.in()
760 for _, field := range message.Field {
761 fieldname := CamelCase(*field.Name)
762 typename, wiretype := g.GoType(message, field)
763 tag := g.GoTag(field, wiretype)
764 g.p(fieldname, "\t", typename, "\t", tag)
765 }
766 g.p("XXX_unrecognized\t[]byte")
767 g.out()
768 g.p("}")
769
770 // Reset and New functions
771 g.p("func (this *", ccTypeName, ") Reset() {")
772 g.in()
773 g.p("*this = ", ccTypeName, "{}")
774 g.out()
775 g.p("}")
776 g.p("func New", ccTypeName, "() *", ccTypeName, " {")
777 g.in()
778 g.p("return new(", ccTypeName, ")")
779 g.out()
780 g.p("}")
781
782 // Default constants
783 for _, field := range message.Field {
784 def := proto.GetString(field.DefaultValue)
785 if def == "" {
786 continue
787 }
788 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
789 typename, _ := g.GoType(message, field)
790 if typename[0] == '*' {
791 typename = typename[1:]
792 }
793 kind := "const "
794 switch {
795 case typename == "bool":
796 case typename == "string":
797 def = Quote(def)
798 case typename == "[]byte":
799 def = "[]byte(" + Quote(def) + ")"
800 kind = "var "
801 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
802 // Must be an enum. Need to construct the prefixed name.
803 obj := g.objectNamed(proto.GetString(field.TypeName))
804 enum, ok := obj.(*EnumDescriptor)
805 if !ok {
806 log.Stderr("don't know how to generate constant for", fieldname)
807 continue
808 }
809 def = enum.prefix() + def
810 }
811 g.p(kind, fieldname, " ", typename, " = ", def)
812 }
813 g.p()
814}
815
816func (g *Generator) GenerateInitFunction() {
817 g.p("func init() {")
818 g.in()
819 for _, enum := range g.file.enum {
820 g.GenerateEnumRegistration(enum)
821 }
822 g.out()
823 g.p("}")
824}
825
826func (g *Generator) GenerateEnumRegistration(enum *EnumDescriptor) {
827 pkg := g.packageName + "." // We always print the full package name here.
828 // The full type name
829 typeName := enum.typeName()
830 // The full type name, CamelCased.
831 ccTypeName := CamelCaseSlice(typeName)
832 g.p("proto.RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
833}
834
835// And now lots of helper functions.
836
837// Return change foo_bar_Baz to FooBar_Baz.
838func CamelCase(name string) string {
839 elems := strings.Split(name, "_", 0)
840 for i, e := range elems {
841 if e == "" {
842 elems[i] = "_"
843 continue
844 }
845 runes := []int(e)
846 if unicode.IsLower(runes[0]) {
847 runes[0] = unicode.ToUpper(runes[0])
848 elems[i] = string(runes)
849 } else {
850 if i > 0 {
851 elems[i] = "_" + e
852 }
853 }
854 }
855 s := strings.Join(elems, "")
856 // Name must not begin with an underscore.
857 if len(s) > 0 && s[0] == '_' {
858 s = "X" + s[1:]
859 }
860 return s
861}
862
863// Like CamelCase, but the argument is a slice of strings to
864// be concatenated with "_"
865func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
866
867// Turn a sliced name into a dotted name
868func DottedSlice(elem []string) string { return strings.Join(elem, ".") }
869
870// Return a Go-source quoted string representation of s.
871func Quote(s string) string { return fmt.Sprintf("%q", s) }
872
873// Given a .proto file name, return the output name for the generated Go program.
874func GoName(name string) string {
875 if strings.HasSuffix(name, ".proto") {
876 name = name[0 : len(name)-6]
877 }
878 return name + ".pb.go"
879}
880
881// Is this field optional?
882func IsOptional(field *descriptor.FieldDescriptorProto) bool {
883 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
884}
885
886// Is this field required?
887func IsRequired(field *descriptor.FieldDescriptorProto) bool {
888 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
889}
890
891// Is this field repeated?
892func IsRepeated(field *descriptor.FieldDescriptorProto) bool {
893 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
894}
895
896// Mapping function used to generate Go names from package names, which can be dotted.
897func DotToUnderscore(rune int) int {
898 if rune == '.' {
899 return '_'
900 }
901 return rune
902}