blob: 88bb4c5d7b508daedb835de81cfaf7ef3328f52b [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 {
461panicln()
462 g.fail("can't find object with type", typeName)
463 }
464 return f
465}
466
467// Print the arguments, handling indirections because they may be *string, etc.
468func (g *Generator) p(str ...interface{}) {
469 g.WriteString(g.indent)
470 for _, v := range str {
471 switch s := v.(type) {
472 case string:
473 g.WriteString(s)
474 case *string:
475 g.WriteString(*s)
476 case *int32:
477 g.WriteString(fmt.Sprintf("%d", *s))
478 default:
479 g.fail(fmt.Sprintf("unknown type in printer: %T", v))
480 }
481 }
482 g.WriteByte('\n')
483}
484
485// Indent the output one tab stop.
486func (g *Generator) in() { g.indent += "\t" }
487
488// Unindent the output one tab stop.
489func (g *Generator) out() {
490 if len(g.indent) > 0 {
491 g.indent = g.indent[1:]
492 }
493}
494
495// Generate the output for all the files we're generating output for.
496func (g *Generator) GenerateAllFiles() {
497 for i, file := range g.genFiles {
498 g.Reset()
499 g.Generate(file)
500 g.response.File[i] = plugin.NewCodeGeneratorResponse_File()
501 g.response.File[i].Name = proto.String(GoName(*file.Name))
502 g.response.File[i].Content = proto.String(g.String())
503 }
504}
505
506// Return the FileDescriptor for this FileDescriptorProto
507func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
508 for _, file := range g.allFiles {
509 if file.FileDescriptorProto == fd {
510 return file
511 }
512 }
513 g.fail("could not find file in table:", proto.GetString(fd.Name))
514 return nil
515}
516
517// Fill the response protocol buffer with the generated output for all the files we're
518// supposed to generate.
519func (g *Generator) Generate(file *FileDescriptor) {
520 g.file = g.FileOf(file.FileDescriptorProto)
521 g.GenerateHeader()
522 g.GenerateImports()
523 for _, enum := range g.file.enum {
524 g.GenerateEnum(enum)
525 }
526 for _, desc := range g.file.desc {
527 g.GenerateMessage(desc)
528 }
529 g.GenerateInitFunction()
530}
531
532// Generate the header, including package definition and imports
533func (g *Generator) GenerateHeader() {
534 g.p("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
535 g.p("// DO NOT EDIT!")
536 g.p()
537 g.p("package ", g.file.packageName())
538 g.p()
539}
540
541// Generate the header, including package definition and imports
542func (g *Generator) GenerateImports() {
543 if len(g.file.enum) > 0 {
544 g.p(`import "goprotobuf.googlecode.com/hg/proto"`)
545 }
546 for _, s := range g.file.Dependency {
547 // Need to find the descriptor for this file
548 for _, fd := range g.allFiles {
549 if proto.GetString(fd.Name) == s {
550 filename := GoName(s)
551 if strings.HasSuffix(filename, ".go") {
552 filename = filename[0:len(filename)-3]
553 }
554 g.p("import ", fd.packageName(), " ", Quote(filename))
555 break
556 }
557 }
558 }
559 g.p()
560}
561
562// Generate the enum definitions for this EnumDescriptor.
563func (g *Generator) GenerateEnum(enum *EnumDescriptor) {
564 // The full type name
565 typeName := enum.typeName()
566 // The full type name, CamelCased.
567 ccTypeName := CamelCaseSlice(typeName)
568 ccPrefix := enum.prefix()
569 g.p("type ", ccTypeName, " int32")
570 g.p("const (")
571 g.in()
572 for _, e := range enum.Value {
573 g.p(ccPrefix+*e.Name, " = ", e.Number)
574 }
575 g.out()
576 g.p(")")
577 g.p("var ", ccTypeName, "_name = map[int32] string {")
578 g.in()
579 generated := make(map[int32] bool) // avoid duplicate values
580 for _, e := range enum.Value {
581 duplicate := ""
582 if _, present := generated[*e.Number]; present {
583 duplicate = "// Duplicate value: "
584 }
585 g.p(duplicate, e.Number, ": ", Quote(*e.Name), ",")
586 generated[*e.Number] = true
587 }
588 g.out()
589 g.p("}")
590 g.p("var ", ccTypeName, "_value = map[string] int32 {")
591 g.in()
592 for _, e := range enum.Value {
593 g.p(Quote(*e.Name), ": ", e.Number, ",")
594 }
595 g.out()
596 g.p("}")
597 g.p("func New", ccTypeName, "(x int32) *", ccTypeName, " {")
598 g.in()
599 g.p("e := ", ccTypeName, "(x)")
600 g.p("return &e")
601 g.out()
602 g.p("}")
603 g.p()
604}
605
606// The tag is a string like "PB(varint,2,opt,name=fieldname,def=7)" that
607// identifies details of the field for the protocol buffer marshaling and unmarshaling
608// code. The fields are:
609// wire encoding
610// protocol tag number
611// opt,req,rep for optional, required, or repeated
612// name= the original declared name
613// enum= the name of the enum type if it is an enum-typed field.
614// def= string representation of the default value, if any.
615// The default value must be in a representation that can be used at run-time
616// to generate the default value. Thus bools become 0 and 1, for instance.
617func (g *Generator) GoTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
618 optrepreq := ""
619 switch {
620 case IsOptional(field):
621 optrepreq = "opt"
622 case IsRequired(field):
623 optrepreq = "req"
624 case IsRepeated(field):
625 optrepreq = "rep"
626 }
627 defaultValue := proto.GetString(field.DefaultValue)
628 if defaultValue != "" {
629 switch *field.Type {
630 case descriptor.FieldDescriptorProto_TYPE_BOOL:
631 if defaultValue == "true" {
632 defaultValue = "1"
633 } else {
634 defaultValue = "0"
635 }
636 case descriptor.FieldDescriptorProto_TYPE_STRING,
637 descriptor.FieldDescriptorProto_TYPE_BYTES:
638 // Protect frogs.
639 defaultValue = Quote(defaultValue)
640 // Don't need the quotes
641 defaultValue = defaultValue[1 : len(defaultValue)-1]
642 case descriptor.FieldDescriptorProto_TYPE_ENUM:
643 // For enums we need to provide the integer constant.
644 obj := g.objectNamed(proto.GetString(field.TypeName))
645 enum, ok := obj.(*EnumDescriptor)
646 if !ok {
647 g.fail("enum type inconsistent for", CamelCaseSlice(obj.typeName()))
648 }
649 defaultValue = enum.integerValueAsString(defaultValue)
650 }
651 defaultValue = ",def=" + defaultValue
652 }
653 enum := ""
654 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
655 obj := g.objectNamed(proto.GetString(field.TypeName))
656 enum = ",enum=" + obj.packageName() + "." + CamelCaseSlice(obj.typeName())
657 }
658 name := proto.GetString(field.Name)
659 if name == CamelCase(name) {
660 name = ""
661 } else {
662 name = ",name=" + name
663 }
664 return Quote(fmt.Sprintf("PB(%s,%d,%s%s%s%s)",
665 wiretype,
666 proto.GetInt32(field.Number),
667 optrepreq,
668 name,
669 enum,
670 defaultValue))
671}
672
673func NeedsStar(typ descriptor.FieldDescriptorProto_Type) bool {
674 switch typ {
675 case descriptor.FieldDescriptorProto_TYPE_GROUP:
676 return false
677 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
678 return false
679 case descriptor.FieldDescriptorProto_TYPE_BYTES:
680 return false
681 }
682 return true
683}
684
685// The type name appropriate for an item. If it's in the current file,
686// drop the package name and underscore the rest.
687// Otherwise it's from another package; use the underscored package name
688// followed by the field name. The result has an initial capital.
689func (g *Generator) TypeName(obj Object) string {
690 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.typeName())
691}
692
693// Like TypeName, but always includes the package name even if it's our own package.
694func (g *Generator) TypeNameWithPackage(obj Object) string {
695 return obj.packageName() + CamelCaseSlice(obj.typeName())
696}
697
698// Returns a string representing the type name, and the wire type
699func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
700 // TODO: Options.
701 switch *field.Type {
702 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
703 typ, wire = "float64", "fixed64"
704 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
705 typ, wire = "float32", "fixed32"
706 case descriptor.FieldDescriptorProto_TYPE_INT64:
707 typ, wire = "int64", "varint"
708 case descriptor.FieldDescriptorProto_TYPE_UINT64:
709 typ, wire = "uint64", "varint"
710 case descriptor.FieldDescriptorProto_TYPE_INT32:
711 typ, wire = "int32", "varint"
712 case descriptor.FieldDescriptorProto_TYPE_UINT32:
713 typ, wire = "uint32", "varint"
714 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
715 typ, wire = "uint64", "fixed64"
716 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
717 typ, wire = "uint32", "fixed32"
718 case descriptor.FieldDescriptorProto_TYPE_BOOL:
719 typ, wire = "bool", "varint"
720 case descriptor.FieldDescriptorProto_TYPE_STRING:
721 typ, wire = "string", "bytes"
722 case descriptor.FieldDescriptorProto_TYPE_GROUP:
723 desc := g.objectNamed(proto.GetString(field.TypeName))
724 typ, wire = "*"+g.TypeName(desc), "group"
725 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
726 desc := g.objectNamed(proto.GetString(field.TypeName))
727 typ, wire = "*"+g.TypeName(desc), "bytes"
728 case descriptor.FieldDescriptorProto_TYPE_BYTES:
729 typ, wire = "[]byte", "bytes"
730 case descriptor.FieldDescriptorProto_TYPE_ENUM:
731 desc := g.objectNamed(proto.GetString(field.TypeName))
732 typ, wire = g.TypeName(desc), "varint"
733 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
734 typ, wire = "int32", "fixed32"
735 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
736 typ, wire = "int64", "fixed64"
737 case descriptor.FieldDescriptorProto_TYPE_SINT32:
738 typ, wire = "int32", "zigzag32"
739 case descriptor.FieldDescriptorProto_TYPE_SINT64:
740 typ, wire = "int64", "zigzag64"
741 default:
742 g.fail("unknown type for", proto.GetString(field.Name))
743 }
744 if IsRepeated(field) {
745 typ = "[]" + typ
746 } else if NeedsStar(*field.Type) {
747 typ = "*" + typ
748 }
749 return
750}
751
752// Generate the type and default constant definitions for this Descriptor.
753func (g *Generator) GenerateMessage(message *Descriptor) {
754 // The full type name
755 typeName := message.typeName()
756 // The full type name, CamelCased.
757 ccTypeName := CamelCaseSlice(typeName)
758
759 g.p("type ", ccTypeName, " struct {")
760 g.in()
761 for _, field := range message.Field {
762 fieldname := CamelCase(*field.Name)
763 typename, wiretype := g.GoType(message, field)
764 tag := g.GoTag(field, wiretype)
765 g.p(fieldname, "\t", typename, "\t", tag)
766 }
767 g.p("XXX_unrecognized\t[]byte")
768 g.out()
769 g.p("}")
770
771 // Reset and New functions
772 g.p("func (this *", ccTypeName, ") Reset() {")
773 g.in()
774 g.p("*this = ", ccTypeName, "{}")
775 g.out()
776 g.p("}")
777 g.p("func New", ccTypeName, "() *", ccTypeName, " {")
778 g.in()
779 g.p("return new(", ccTypeName, ")")
780 g.out()
781 g.p("}")
782
783 // Default constants
784 for _, field := range message.Field {
785 def := proto.GetString(field.DefaultValue)
786 if def == "" {
787 continue
788 }
789 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
790 typename, _ := g.GoType(message, field)
791 if typename[0] == '*' {
792 typename = typename[1:]
793 }
794 kind := "const "
795 switch {
796 case typename == "bool":
797 case typename == "string":
798 def = Quote(def)
799 case typename == "[]byte":
800 def = "[]byte(" + Quote(def) + ")"
801 kind = "var "
802 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
803 // Must be an enum. Need to construct the prefixed name.
804 obj := g.objectNamed(proto.GetString(field.TypeName))
805 enum, ok := obj.(*EnumDescriptor)
806 if !ok {
807 log.Stderr("don't know how to generate constant for", fieldname)
808 continue
809 }
810 def = enum.prefix() + def
811 }
812 g.p(kind, fieldname, " ", typename, " = ", def)
813 }
814 g.p()
815}
816
817func (g *Generator) GenerateInitFunction() {
818 g.p("func init() {")
819 g.in()
820 for _, enum := range g.file.enum {
821 g.GenerateEnumRegistration(enum)
822 }
823 g.out()
824 g.p("}")
825}
826
827func (g *Generator) GenerateEnumRegistration(enum *EnumDescriptor) {
828 pkg := g.packageName + "." // We always print the full package name here.
829 // The full type name
830 typeName := enum.typeName()
831 // The full type name, CamelCased.
832 ccTypeName := CamelCaseSlice(typeName)
833 g.p("proto.RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
834}
835
836// And now lots of helper functions.
837
838// Return change foo_bar_Baz to FooBar_Baz.
839func CamelCase(name string) string {
840 elems := strings.Split(name, "_", 0)
841 for i, e := range elems {
842 if e == "" {
843 elems[i] = "_"
844 continue
845 }
846 runes := []int(e)
847 if unicode.IsLower(runes[0]) {
848 runes[0] = unicode.ToUpper(runes[0])
849 elems[i] = string(runes)
850 } else {
851 if i > 0 {
852 elems[i] = "_" + e
853 }
854 }
855 }
856 s := strings.Join(elems, "")
857 // Name must not begin with an underscore.
858 if len(s) > 0 && s[0] == '_' {
859 s = "X" + s[1:]
860 }
861 return s
862}
863
864// Like CamelCase, but the argument is a slice of strings to
865// be concatenated with "_"
866func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
867
868// Turn a sliced name into a dotted name
869func DottedSlice(elem []string) string { return strings.Join(elem, ".") }
870
871// Return a Go-source quoted string representation of s.
872func Quote(s string) string { return fmt.Sprintf("%q", s) }
873
874// Given a .proto file name, return the output name for the generated Go program.
875func GoName(name string) string {
876 if strings.HasSuffix(name, ".proto") {
877 name = name[0 : len(name)-6]
878 }
879 return name + ".pb.go"
880}
881
882// Is this field optional?
883func IsOptional(field *descriptor.FieldDescriptorProto) bool {
884 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
885}
886
887// Is this field required?
888func IsRequired(field *descriptor.FieldDescriptorProto) bool {
889 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
890}
891
892// Is this field repeated?
893func IsRepeated(field *descriptor.FieldDescriptorProto) bool {
894 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
895}
896
897// Mapping function used to generate Go names from package names, which can be dotted.
898func DotToUnderscore(rune int) int {
899 if rune == '.' {
900 return '_'
901 }
902 return rune
903}