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