blob: c26d3249e1d4338dfe7362da6c3461615152e232 [file] [log] [blame]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33 The code generator for the plugin for the Google protocol buffer compiler.
34 It generates Go code from the protocol buffer description files read by the
35 main routine.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070036*/
37package generator
38
39import (
40 "bytes"
41 "fmt"
David Symondsb1d55a02011-04-08 09:55:06 +100042 "go/parser"
43 "go/printer"
44 "go/token"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070045 "log"
46 "os"
Rob Pike87af39e2010-07-19 10:48:02 -070047 "path"
David Symonds79eae332010-10-16 11:33:20 +110048 "strconv"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070049 "strings"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070050
51 "goprotobuf.googlecode.com/hg/proto"
David Symonds9f402812011-04-28 18:08:44 +100052 plugin "goprotobuf.googlecode.com/hg/compiler/plugin"
Rob Pikeaf82b4e2010-04-30 15:19:25 -070053 descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor"
54)
55
56// A Plugin provides functionality to add to the output during Go code generation,
57// such as to produce RPC stubs.
58type Plugin interface {
59 // Name identifies the plugin.
Rob Pikec9e7d972010-06-10 10:30:22 -070060 Name() string
61 // Init is called once after data structures are built but before
62 // code generation begins.
63 Init(g *Generator)
64 // Generate produces the code generated by the plugin for this file,
65 // except for the imports, by calling the generator's methods P, In, and Out.
66 Generate(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070067 // GenerateImports produces the import declarations for this file.
Rob Pikec9e7d972010-06-10 10:30:22 -070068 // It is called after Generate.
69 GenerateImports(file *FileDescriptor)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070070}
71
72var plugins []Plugin
73
74// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated.
75// It is typically called during initialization.
76func RegisterPlugin(p Plugin) {
David Symondscc7142e2010-11-06 14:37:15 +110077 plugins = append(plugins, p)
Rob Pikeaf82b4e2010-04-30 15:19:25 -070078}
79
80// Each type we import as a protocol buffer (other than FileDescriptorProto) needs
81// a pointer to the FileDescriptorProto that represents it. These types achieve that
82// wrapping by placing each Proto inside a struct with the pointer to its File. The
83// structs have the same names as their contents, with "Proto" removed.
84// FileDescriptor is used to store the things that it points to.
85
86// The file and package name method are common to messages and enums.
87type common struct {
David Symonds4decd802011-08-04 11:27:07 +100088 file *descriptor.FileDescriptorProto // File this object comes from.
Rob Pikeaf82b4e2010-04-30 15:19:25 -070089}
90
91// PackageName is name in the package clause in the generated file.
David Symonds4decd802011-08-04 11:27:07 +100092func (c *common) PackageName() string { return uniquePackageOf(c.file) }
93
94func (c *common) File() *descriptor.FileDescriptorProto { return c.file }
Rob Pikeaf82b4e2010-04-30 15:19:25 -070095
96// Descriptor represents a protocol buffer message.
97type Descriptor struct {
98 common
99 *descriptor.DescriptorProto
100 parent *Descriptor // The containing message, if any.
101 nested []*Descriptor // Inner messages, if any.
102 ext []*ExtensionDescriptor // Extensions, if any.
103 typename []string // Cached typename vector.
104}
105
106// TypeName returns the elements of the dotted type name.
107// The package name is not part of this name.
108func (d *Descriptor) TypeName() []string {
109 if d.typename != nil {
110 return d.typename
111 }
112 n := 0
113 for parent := d; parent != nil; parent = parent.parent {
114 n++
115 }
116 s := make([]string, n, n)
117 for parent := d; parent != nil; parent = parent.parent {
118 n--
119 s[n] = proto.GetString(parent.Name)
120 }
121 d.typename = s
122 return s
123}
124
125// EnumDescriptor describes an enum. If it's at top level, its parent will be nil.
126// Otherwise it will be the descriptor of the message in which it is defined.
127type EnumDescriptor struct {
128 common
129 *descriptor.EnumDescriptorProto
130 parent *Descriptor // The containing message, if any.
131 typename []string // Cached typename vector.
132}
133
134// TypeName returns the elements of the dotted type name.
135// The package name is not part of this name.
136func (e *EnumDescriptor) TypeName() (s []string) {
137 if e.typename != nil {
138 return e.typename
139 }
140 name := proto.GetString(e.Name)
141 if e.parent == nil {
142 s = make([]string, 1)
143 } else {
144 pname := e.parent.TypeName()
145 s = make([]string, len(pname)+1)
146 copy(s, pname)
147 }
148 s[len(s)-1] = name
149 e.typename = s
150 return s
151}
152
153// Everything but the last element of the full type name, CamelCased.
154// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... .
155func (e *EnumDescriptor) prefix() string {
156 typeName := e.TypeName()
157 ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_"
158 if e.parent == nil {
159 // If the enum is not part of a message, the prefix is just the type name.
160 ccPrefix = CamelCase(*e.Name) + "_"
161 }
162 return ccPrefix
163}
164
165// The integer value of the named constant in this enumerated type.
166func (e *EnumDescriptor) integerValueAsString(name string) string {
167 for _, c := range e.Value {
168 if proto.GetString(c.Name) == name {
169 return fmt.Sprint(proto.GetInt32(c.Number))
170 }
171 }
David Symonds9d0000e2011-02-03 10:48:14 +1100172 log.Fatal("cannot find value for enum constant")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700173 return ""
174}
175
David Symonds4decd802011-08-04 11:27:07 +1000176// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700177// Otherwise it will be the descriptor of the message in which it is defined.
178type ExtensionDescriptor struct {
179 common
180 *descriptor.FieldDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700181 parent *Descriptor // The containing message, if any.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700182}
183
184// TypeName returns the elements of the dotted type name.
185// The package name is not part of this name.
186func (e *ExtensionDescriptor) TypeName() (s []string) {
187 name := proto.GetString(e.Name)
188 if e.parent == nil {
189 // top-level extension
190 s = make([]string, 1)
191 } else {
192 pname := e.parent.TypeName()
193 s = make([]string, len(pname)+1)
194 copy(s, pname)
195 }
196 s[len(s)-1] = name
197 return s
198}
199
David Symondse37856c2011-06-22 12:52:53 +1000200// DescName returns the variable name used for the generated descriptor.
201func (e *ExtensionDescriptor) DescName() string {
202 // The full type name.
203 typeName := e.TypeName()
204 // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix.
205 for i, s := range typeName {
206 typeName[i] = CamelCase(s)
207 }
208 return "E_" + strings.Join(typeName, "_")
209}
210
David Symonds4decd802011-08-04 11:27:07 +1000211// ImportedDescriptor describes a type that has been publicly imported from another file.
212type ImportedDescriptor struct {
213 common
214 o Object
215}
216
217func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() }
218
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700219// FileDescriptor describes an protocol buffer descriptor file (.proto).
220// It includes slices of all the messages and enums defined within it.
221// Those slices are constructed by WrapTypes.
222type FileDescriptor struct {
223 *descriptor.FileDescriptorProto
Rob Pikec9e7d972010-06-10 10:30:22 -0700224 desc []*Descriptor // All the messages defined in this file.
225 enum []*EnumDescriptor // All the enums defined in this file.
226 ext []*ExtensionDescriptor // All the top-level extensions defined in this file.
David Symonds4decd802011-08-04 11:27:07 +1000227 imp []*ImportedDescriptor // All types defined in files publicly imported by this file.
David Symonds31d58a22011-01-20 18:33:21 +1100228
David Symondsb2a00c82011-08-04 16:52:58 +1000229 // The full list of symbols that are exported,
230 // as a map from the exported object to its symbols.
David Symonds31d58a22011-01-20 18:33:21 +1100231 // This is used for supporting public imports.
David Symondsb2a00c82011-08-04 16:52:58 +1000232 exported map[Object][]Symbol
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700233}
234
235// PackageName is the package name we'll use in the generated code to refer to this file.
236func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) }
237
238// The package named defined in the input for this file, possibly dotted.
Rob Pikec9e7d972010-06-10 10:30:22 -0700239// If the file does not define a package, use the base of the file name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700240func (d *FileDescriptor) originalPackageName() string {
Rob Pikec9e7d972010-06-10 10:30:22 -0700241 // Does the file have a package clause?
David Symonds7d5c8242011-03-14 12:03:50 -0700242 if pkg := proto.GetString(d.Package); pkg != "" {
Rob Pikec9e7d972010-06-10 10:30:22 -0700243 return pkg
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700244 }
Rob Pikec9e7d972010-06-10 10:30:22 -0700245 // Use the file base name.
246 return BaseName(proto.GetString(d.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700247}
248
David Symondsb2a00c82011-08-04 16:52:58 +1000249func (d *FileDescriptor) addExport(obj Object, symbol Symbol) {
250 d.exported[obj] = append(d.exported[obj], symbol)
David Symonds31d58a22011-01-20 18:33:21 +1100251}
252
253// Symbol is an interface representing an exported Go symbol.
254type Symbol interface {
255 // GenerateAlias should generate an appropriate alias
256 // for the symbol from the named package.
257 GenerateAlias(g *Generator, pkg string)
258}
259
260type messageSymbol struct {
261 sym string
262 hasExtensions, isMessageSet bool
263}
264
265func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) {
266 remoteSym := pkg + "." + ms.sym
267
268 g.P("type ", ms.sym, " ", remoteSym)
269 g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }")
David Symondse37856c2011-06-22 12:52:53 +1000270 g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }")
David Symonds31d58a22011-01-20 18:33:21 +1100271 if ms.hasExtensions {
272 g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ",
273 "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }")
David Symonds1d72f7a2011-08-19 18:28:52 +1000274 g.P("func (this *", ms.sym, ") ExtensionMap() map[int32]", g.ProtoPkg, ".Extension ",
David Symonds31d58a22011-01-20 18:33:21 +1100275 "{ return (*", remoteSym, ")(this).ExtensionMap() }")
276 if ms.isMessageSet {
277 g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ",
278 "{ return (*", remoteSym, ")(this).Marshal() }")
279 g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ",
280 "{ return (*", remoteSym, ")(this).Unmarshal(buf) }")
281 }
282 }
283}
284
285type enumSymbol string
286
287func (es enumSymbol) GenerateAlias(g *Generator, pkg string) {
288 s := string(es)
289 g.P("type ", s, " ", pkg, ".", s)
290 g.P("var ", s, "_name = ", pkg, ".", s, "_name")
291 g.P("var ", s, "_value = ", pkg, ".", s, "_value")
David Symondsdd9ca042011-08-29 16:07:16 +1000292 g.P("func New", s, "(x ", s, ") *", s, " { e := ", s, "(x); return &e }")
David Symonds31d58a22011-01-20 18:33:21 +1100293}
294
295type constOrVarSymbol struct {
296 sym string
297 typ string // either "const" or "var"
298}
299
300func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) {
301 g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym)
302}
303
David Symonds4decd802011-08-04 11:27:07 +1000304// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700305type Object interface {
306 PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness.
307 TypeName() []string
David Symonds4decd802011-08-04 11:27:07 +1000308 File() *descriptor.FileDescriptorProto
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700309}
310
311// Each package name we generate must be unique. The package we're generating
David Symonds4decd802011-08-04 11:27:07 +1000312// gets its own name but every other package must have a unique name that does
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700313// not conflict in the code we generate. These names are chosen globally (although
314// they don't have to be, it simplifies things to do them globally).
315func uniquePackageOf(fd *descriptor.FileDescriptorProto) string {
316 s, ok := uniquePackageName[fd]
317 if !ok {
David Symonds9d0000e2011-02-03 10:48:14 +1100318 log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700319 }
320 return s
321}
322
323// Generator is the type whose methods generate the output, stored in the associated response structure.
324type Generator struct {
David Symondsf90e3382010-05-05 10:53:44 +1000325 *bytes.Buffer
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700326
327 Request *plugin.CodeGeneratorRequest // The input.
328 Response *plugin.CodeGeneratorResponse // The output.
329
Rob Pikec9e7d972010-06-10 10:30:22 -0700330 Param map[string]string // Command-line parameters.
331 ImportPrefix string // String to prefix to imported package file names.
332 ImportMap map[string]string // Mapping from import name to generated name
333
334 ProtoPkg string // The name under which we import the library's package proto.
335
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700336 packageName string // What we're calling ourselves.
337 allFiles []*FileDescriptor // All files in the tree
338 genFiles []*FileDescriptor // Those files we will generate output for.
339 file *FileDescriptor // The file we are compiling now.
David Symondsf90e3382010-05-05 10:53:44 +1000340 usedPackages map[string]bool // Names of packages used in current file.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700341 typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax.
342 indent string
343}
344
345// New creates a new generator and allocates the request and response protobufs.
346func New() *Generator {
347 g := new(Generator)
David Symondsf90e3382010-05-05 10:53:44 +1000348 g.Buffer = new(bytes.Buffer)
David Symondsb0127532010-11-09 11:10:46 +1100349 g.Request = new(plugin.CodeGeneratorRequest)
350 g.Response = new(plugin.CodeGeneratorResponse)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700351 return g
352}
353
354// Error reports a problem, including an os.Error, and exits the program.
355func (g *Generator) Error(err os.Error, msgs ...string) {
356 s := strings.Join(msgs, " ") + ":" + err.String()
Rob Pike5194c512010-10-14 13:02:16 -0700357 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700358 g.Response.Error = proto.String(s)
359 os.Exit(1)
360}
361
362// Fail reports a problem and exits the program.
363func (g *Generator) Fail(msgs ...string) {
364 s := strings.Join(msgs, " ")
Rob Pike5194c512010-10-14 13:02:16 -0700365 log.Println("protoc-gen-go: error:", s)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700366 g.Response.Error = proto.String(s)
367 os.Exit(1)
368}
369
Rob Pikec9e7d972010-06-10 10:30:22 -0700370// CommandLineParameters breaks the comma-separated list of key=value pairs
371// in the parameter (a member of the request protobuf) into a key/value map.
372// It then sets file name mappings defined by those entries.
373func (g *Generator) CommandLineParameters(parameter string) {
374 g.Param = make(map[string]string)
David Symonds8935abf2011-07-04 15:53:16 +1000375 for _, p := range strings.Split(parameter, ",") {
Rob Pikec9e7d972010-06-10 10:30:22 -0700376 if i := strings.Index(p, "="); i < 0 {
377 g.Param[p] = ""
378 } else {
379 g.Param[p[0:i]] = p[i+1:]
380 }
381 }
382
383 g.ImportMap = make(map[string]string)
384 for k, v := range g.Param {
385 if k == "import_prefix" {
386 g.ImportPrefix = v
387 } else if len(k) > 0 && k[0] == 'M' {
388 g.ImportMap[k[1:]] = v
389 }
390 }
391}
392
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700393// DefaultPackageName returns the package name printed for the object.
394// If its file is in a different package, it returns the package name we're using for this file, plus ".".
395// Otherwise it returns the empty string.
396func (g *Generator) DefaultPackageName(obj Object) string {
397 pkg := obj.PackageName()
398 if pkg == g.packageName {
399 return ""
400 }
401 return pkg + "."
402}
403
404// For each input file, the unique package name to use, underscored.
405var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string)
Rob Pikec9e7d972010-06-10 10:30:22 -0700406// Package names already registered. Key is the name from the .proto file;
407// value is the name that appears in the generated code.
408var pkgNamesInUse = make(map[string]bool)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700409
Rob Pikec9e7d972010-06-10 10:30:22 -0700410// Create and remember a guaranteed unique package name for this file descriptor.
411// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and
412// has no file descriptor.
413func RegisterUniquePackageName(pkg string, f *FileDescriptor) string {
David Symonds41389812011-07-19 14:57:40 +1000414 // Convert dots to underscores before finding a unique alias.
415 pkg = strings.Map(DotToUnderscore, pkg)
416
David Symonds79eae332010-10-16 11:33:20 +1100417 for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ {
Rob Pikec9e7d972010-06-10 10:30:22 -0700418 // It's a duplicate; must rename.
David Symonds79eae332010-10-16 11:33:20 +1100419 pkg = orig + strconv.Itoa(i)
Rob Pikec9e7d972010-06-10 10:30:22 -0700420 }
421 // Install it.
422 pkgNamesInUse[pkg] = true
Rob Pikec9e7d972010-06-10 10:30:22 -0700423 if f != nil {
424 uniquePackageName[f.FileDescriptorProto] = pkg
425 }
426 return pkg
427}
428
429// SetPackageNames sets the package name for this run.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700430// The package name must agree across all files being generated.
431// It also defines unique package names for all imported files.
432func (g *Generator) SetPackageNames() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700433 // Register the name for this package. It will be the first name
434 // registered so is guaranteed to be unmodified.
435 pkg := g.genFiles[0].originalPackageName()
436 g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0])
437 // Register the proto package name. It might collide with the
438 // name of a package we import.
439 g.ProtoPkg = RegisterUniquePackageName("proto", nil)
David Symonds7d5c8242011-03-14 12:03:50 -0700440 // Verify that we are generating output for a single package.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700441 for _, f := range g.genFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700442 thisPkg := f.originalPackageName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700443 if thisPkg != pkg {
444 g.Fail("inconsistent package names:", thisPkg, pkg)
445 }
446 }
447AllFiles:
448 for _, f := range g.allFiles {
449 for _, genf := range g.genFiles {
450 if f == genf {
451 // In this package already.
452 uniquePackageName[f.FileDescriptorProto] = g.packageName
453 continue AllFiles
454 }
455 }
David Symonds7d5c8242011-03-14 12:03:50 -0700456 // The file is a dependency, so we want to ignore its go_package option
457 // because that is only relevant for its specific generated output.
458 pkg := proto.GetString(f.Package)
459 if pkg == "" {
460 pkg = BaseName(*f.Name)
461 }
462 RegisterUniquePackageName(pkg, f)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700463 }
464}
465
466// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos
467// and FileDescriptorProtos into file-referenced objects within the Generator.
468// It also creates the list of files to generate and so should be called before GenerateAllFiles.
469func (g *Generator) WrapTypes() {
470 g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile))
471 for i, f := range g.Request.ProtoFile {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700472 // We must wrap the descriptors before we wrap the enums
473 descs := wrapDescriptors(f)
474 g.buildNestedDescriptors(descs)
475 enums := wrapEnumDescriptors(f, descs)
476 exts := wrapExtensions(f)
David Symonds4decd802011-08-04 11:27:07 +1000477 imps := wrapImported(f, g)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700478 g.allFiles[i] = &FileDescriptor{
479 FileDescriptorProto: f,
480 desc: descs,
481 enum: enums,
482 ext: exts,
David Symonds4decd802011-08-04 11:27:07 +1000483 imp: imps,
David Symondsb2a00c82011-08-04 16:52:58 +1000484 exported: make(map[Object][]Symbol),
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700485 }
486 }
487
488 g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate))
489FindFiles:
490 for i, fileName := range g.Request.FileToGenerate {
491 // Search the list. This algorithm is n^2 but n is tiny.
492 for _, file := range g.allFiles {
493 if fileName == proto.GetString(file.Name) {
494 g.genFiles[i] = file
495 continue FindFiles
496 }
497 }
498 g.Fail("could not find file named", fileName)
499 }
500 g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles))
501}
502
503// Scan the descriptors in this file. For each one, build the slice of nested descriptors
504func (g *Generator) buildNestedDescriptors(descs []*Descriptor) {
505 for _, desc := range descs {
506 if len(desc.NestedType) != 0 {
507 desc.nested = make([]*Descriptor, len(desc.NestedType))
508 n := 0
509 for _, nest := range descs {
510 if nest.parent == desc {
511 desc.nested[n] = nest
512 n++
513 }
514 }
515 if n != len(desc.NestedType) {
516 g.Fail("internal error: nesting failure for", proto.GetString(desc.Name))
517 }
518 }
519 }
520}
521
522// Construct the Descriptor and add it to the slice
523func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
David Symonds4decd802011-08-04 11:27:07 +1000524 d := &Descriptor{common{file}, desc, parent, nil, nil, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700525
526 d.ext = make([]*ExtensionDescriptor, len(desc.Extension))
527 for i, field := range desc.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000528 d.ext[i] = &ExtensionDescriptor{common{file}, field, d}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700529 }
530
David Symondscc7142e2010-11-06 14:37:15 +1100531 return append(sl, d)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700532}
533
534// Return a slice of all the Descriptors defined within this file
535func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor {
536 sl := make([]*Descriptor, 0, len(file.MessageType)+10)
537 for _, desc := range file.MessageType {
538 sl = wrapThisDescriptor(sl, desc, nil, file)
539 }
540 return sl
541}
542
543// Wrap this Descriptor, recursively
544func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor {
545 sl = addDescriptor(sl, desc, parent, file)
546 me := sl[len(sl)-1]
547 for _, nested := range desc.NestedType {
548 sl = wrapThisDescriptor(sl, nested, me, file)
549 }
550 return sl
551}
552
553// Construct the EnumDescriptor and add it to the slice
554func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor {
David Symonds4decd802011-08-04 11:27:07 +1000555 return append(sl, &EnumDescriptor{common{file}, desc, parent, nil})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700556}
557
558// Return a slice of all the EnumDescriptors defined within this file
559func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor {
560 sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10)
David Symonds5256cf62010-06-27 10:33:42 +1000561 // Top-level enums.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700562 for _, enum := range file.EnumType {
563 sl = addEnumDescriptor(sl, enum, nil, file)
564 }
David Symonds5256cf62010-06-27 10:33:42 +1000565 // Enums within messages. Enums within embedded messages appear in the outer-most message.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700566 for _, nested := range descs {
David Symonds5256cf62010-06-27 10:33:42 +1000567 for _, enum := range nested.EnumType {
568 sl = addEnumDescriptor(sl, enum, nested, file)
569 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700570 }
571 return sl
572}
573
574// Return a slice of all the top-level ExtensionDescriptors defined within this file.
575func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor {
576 sl := make([]*ExtensionDescriptor, len(file.Extension))
577 for i, field := range file.Extension {
David Symonds4decd802011-08-04 11:27:07 +1000578 sl[i] = &ExtensionDescriptor{common{file}, field, nil}
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700579 }
580 return sl
581}
582
David Symonds4decd802011-08-04 11:27:07 +1000583// Return a slice of all the types that are publicly imported into this file.
584func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) {
585 for _, index := range file.PublicDependency {
586 df := g.fileByName(file.Dependency[index])
587 for _, d := range df.desc {
588 sl = append(sl, &ImportedDescriptor{common{file}, d})
589 }
590 for _, e := range df.enum {
591 sl = append(sl, &ImportedDescriptor{common{file}, e})
592 }
593 for _, ext := range df.ext {
594 sl = append(sl, &ImportedDescriptor{common{file}, ext})
595 }
596 }
597 return
598}
599
Rob Pikec9e7d972010-06-10 10:30:22 -0700600// BuildTypeNameMap builds the map from fully qualified type names to objects.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700601// The key names for the map come from the input data, which puts a period at the beginning.
602// It should be called after SetPackageNames and before GenerateAllFiles.
603func (g *Generator) BuildTypeNameMap() {
604 g.typeNameToObject = make(map[string]Object)
605 for _, f := range g.allFiles {
Rob Pikec9e7d972010-06-10 10:30:22 -0700606 // The names in this loop are defined by the proto world, not us, so the
607 // package name may be empty. If so, the dotted package name of X will
608 // be ".X"; otherwise it will be ".pkg.X".
609 dottedPkg := "." + proto.GetString(f.Package)
610 if dottedPkg != "." {
611 dottedPkg += "."
612 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700613 for _, enum := range f.enum {
614 name := dottedPkg + dottedSlice(enum.TypeName())
615 g.typeNameToObject[name] = enum
616 }
617 for _, desc := range f.desc {
618 name := dottedPkg + dottedSlice(desc.TypeName())
619 g.typeNameToObject[name] = desc
620 }
621 }
622}
623
624// ObjectNamed, given a fully-qualified input type name as it appears in the input data,
625// returns the descriptor for the message or enum with that name.
626func (g *Generator) ObjectNamed(typeName string) Object {
David Symonds4decd802011-08-04 11:27:07 +1000627 o, ok := g.typeNameToObject[typeName]
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700628 if !ok {
629 g.Fail("can't find object with type", typeName)
630 }
David Symonds4decd802011-08-04 11:27:07 +1000631
632 // If the file of this object isn't a direct dependency of the current file,
633 // or in the current file, then this object has been publicly imported into
634 // a dependency of the current file.
635 // We should return the ImportedDescriptor object for it instead.
636 direct := *o.File().Name == *g.file.Name
637 if !direct {
638 for _, dep := range g.file.Dependency {
639 if *g.fileByName(dep).Name == *o.File().Name {
640 direct = true
641 break
642 }
643 }
644 }
645 if !direct {
646 found := false
647 Loop:
648 for _, dep := range g.file.Dependency {
649 df := g.fileByName(*g.fileByName(dep).Name)
650 for _, td := range df.imp {
651 if td.o == o {
652 // Found it!
653 o = td
654 found = true
655 break Loop
656 }
657 }
658 }
659 if !found {
660 log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name)
661 }
662 }
663
664 return o
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700665}
666
667// P prints the arguments to the generated output. It handles strings and int32s, plus
668// handling indirections because they may be *string, etc.
669func (g *Generator) P(str ...interface{}) {
670 g.WriteString(g.indent)
671 for _, v := range str {
672 switch s := v.(type) {
673 case string:
674 g.WriteString(s)
675 case *string:
676 g.WriteString(*s)
Rob Pikec9e7d972010-06-10 10:30:22 -0700677 case bool:
678 g.WriteString(fmt.Sprintf("%t", s))
679 case *bool:
680 g.WriteString(fmt.Sprintf("%t", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700681 case *int32:
682 g.WriteString(fmt.Sprintf("%d", *s))
Rob Pikec9e7d972010-06-10 10:30:22 -0700683 case float64:
684 g.WriteString(fmt.Sprintf("%g", s))
685 case *float64:
686 g.WriteString(fmt.Sprintf("%g", *s))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700687 default:
688 g.Fail(fmt.Sprintf("unknown type in printer: %T", v))
689 }
690 }
691 g.WriteByte('\n')
692}
693
694// In Indents the output one tab stop.
695func (g *Generator) In() { g.indent += "\t" }
696
697// Out unindents the output one tab stop.
698func (g *Generator) Out() {
699 if len(g.indent) > 0 {
700 g.indent = g.indent[1:]
701 }
702}
703
704// GenerateAllFiles generates the output for all the files we're outputting.
705func (g *Generator) GenerateAllFiles() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700706 // Initialize the plugins
707 for _, p := range plugins {
708 p.Init(g)
709 }
David Symonds31d58a22011-01-20 18:33:21 +1100710 // Generate the output. The generator runs for every file, even the files
711 // that we don't generate output for, so that we can collate the full list
712 // of exported symbols to support public imports.
713 genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
714 for _, file := range g.genFiles {
715 genFileMap[file] = true
716 }
717 i := 0
718 for _, file := range g.allFiles {
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700719 g.Reset()
720 g.generate(file)
David Symonds31d58a22011-01-20 18:33:21 +1100721 if _, ok := genFileMap[file]; !ok {
722 continue
723 }
David Symondsb0127532010-11-09 11:10:46 +1100724 g.Response.File[i] = new(plugin.CodeGeneratorResponse_File)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700725 g.Response.File[i].Name = proto.String(goFileName(*file.Name))
726 g.Response.File[i].Content = proto.String(g.String())
David Symonds31d58a22011-01-20 18:33:21 +1100727 i++
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700728 }
729}
730
731// Run all the plugins associated with the file.
732func (g *Generator) runPlugins(file *FileDescriptor) {
733 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700734 p.Generate(file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700735 }
736}
737
738
739// FileOf return the FileDescriptor for this FileDescriptorProto.
740func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor {
741 for _, file := range g.allFiles {
742 if file.FileDescriptorProto == fd {
743 return file
744 }
745 }
746 g.Fail("could not find file in table:", proto.GetString(fd.Name))
747 return nil
748}
749
750// Fill the response protocol buffer with the generated output for all the files we're
751// supposed to generate.
752func (g *Generator) generate(file *FileDescriptor) {
753 g.file = g.FileOf(file.FileDescriptorProto)
David Symondsf90e3382010-05-05 10:53:44 +1000754 g.usedPackages = make(map[string]bool)
755
David Symonds4decd802011-08-04 11:27:07 +1000756 for _, td := range g.file.imp {
757 g.generateImported(td)
758 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700759 for _, enum := range g.file.enum {
760 g.generateEnum(enum)
761 }
762 for _, desc := range g.file.desc {
763 g.generateMessage(desc)
764 }
765 for _, ext := range g.file.ext {
766 g.generateExtension(ext)
767 }
768 g.generateInitFunction()
David Symondsf90e3382010-05-05 10:53:44 +1000769
Rob Pikec9e7d972010-06-10 10:30:22 -0700770 // Run the plugins before the imports so we know which imports are necessary.
771 g.runPlugins(file)
772
David Symondsf90e3382010-05-05 10:53:44 +1000773 // Generate header and imports last, though they appear first in the output.
774 rem := g.Buffer
775 g.Buffer = new(bytes.Buffer)
776 g.generateHeader()
777 g.generateImports()
778 g.Write(rem.Bytes())
David Symondsb1d55a02011-04-08 09:55:06 +1000779
780 // Reformat generated code.
781 fset := token.NewFileSet()
782 ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
783 if err != nil {
784 g.Fail("bad Go source code was generated:", err.String())
785 return
786 }
787 g.Reset()
David Symonds2ed81c42011-08-12 17:06:51 +1000788 _, err = (&printer.Config{printer.TabIndent | printer.UseSpaces, 8}).Fprint(g, fset, ast)
David Symondsb1d55a02011-04-08 09:55:06 +1000789 if err != nil {
790 g.Fail("generated Go source code could not be reformatted:", err.String())
791 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700792}
793
794// Generate the header, including package definition and imports
795func (g *Generator) generateHeader() {
796 g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name))
797 g.P("// DO NOT EDIT!")
798 g.P()
799 g.P("package ", g.file.PackageName())
800 g.P()
801}
802
David Symonds31d58a22011-01-20 18:33:21 +1100803func (g *Generator) fileByName(filename string) *FileDescriptor {
804 for _, fd := range g.allFiles {
805 if proto.GetString(fd.Name) == filename {
806 return fd
807 }
808 }
809 return nil
810}
811
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700812// Generate the header, including package definition and imports
813func (g *Generator) generateImports() {
Rob Pikec9e7d972010-06-10 10:30:22 -0700814 // We almost always need a proto import. Rather than computing when we
815 // do, which is tricky when there's a plugin, just import it and
David Symonds4fee3b12010-11-11 10:00:13 +1100816 // reference it later. The same argument applies to the os package.
Rob Pike809831a2010-06-16 10:10:58 -0700817 g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto"))
David Symondscea785b2011-01-07 11:02:30 +1100818 g.P(`import "math"`)
David Symonds4fee3b12010-11-11 10:00:13 +1100819 g.P(`import "os"`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700820 for _, s := range g.file.Dependency {
David Symonds31d58a22011-01-20 18:33:21 +1100821 fd := g.fileByName(s)
822 // Do not import our own package.
823 if fd.PackageName() == g.packageName {
824 continue
825 }
826 filename := goFileName(s)
827 if substitution, ok := g.ImportMap[s]; ok {
828 filename = substitution
829 }
830 filename = g.ImportPrefix + filename
831 if strings.HasSuffix(filename, ".go") {
832 filename = filename[0 : len(filename)-3]
833 }
834 if _, ok := g.usedPackages[fd.PackageName()]; ok {
835 g.P("import ", fd.PackageName(), " ", Quote(filename))
836 } else {
David Symonds3fa055f2011-05-05 15:19:04 -0700837 // TODO: Re-enable this when we are more feature-complete.
838 // For instance, some protos use foreign field extensions, which we don't support.
839 // Until then, this is just annoying spam.
840 //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s)
David Symonds4decd802011-08-04 11:27:07 +1000841 g.P("// discarding unused import ", fd.PackageName(), " ", Quote(filename))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700842 }
843 }
844 g.P()
845 // TODO: may need to worry about uniqueness across plugins
846 for _, p := range plugins {
Rob Pikec9e7d972010-06-10 10:30:22 -0700847 p.GenerateImports(g.file)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700848 g.P()
849 }
David Symondscea785b2011-01-07 11:02:30 +1100850 g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.")
Rob Pikec9e7d972010-06-10 10:30:22 -0700851 g.P("var _ = ", g.ProtoPkg, ".GetString")
David Symondscea785b2011-01-07 11:02:30 +1100852 g.P("var _ = math.Inf")
David Symonds4fee3b12010-11-11 10:00:13 +1100853 g.P("var _ os.Error")
Rob Pikec9e7d972010-06-10 10:30:22 -0700854 g.P()
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700855}
856
David Symonds4decd802011-08-04 11:27:07 +1000857func (g *Generator) generateImported(id *ImportedDescriptor) {
David Symondsb2a00c82011-08-04 16:52:58 +1000858 // Don't generate public import symbols for files that we are generating
859 // code for, since those symbols will already be in this package.
860 // We can't simply avoid creating the ImportedDescriptor objects,
861 // because g.genFiles isn't populated at that stage.
David Symonds4decd802011-08-04 11:27:07 +1000862 tn := id.TypeName()
863 sn := tn[len(tn)-1]
David Symondsb2a00c82011-08-04 16:52:58 +1000864 df := g.FileOf(id.o.File())
865 filename := *df.Name
866 for _, fd := range g.genFiles {
867 if *fd.Name == filename {
868 g.P("// Ignoring public import of ", sn, " from ", filename)
869 g.P()
870 return
871 }
872 }
873 g.P("// ", sn, " from public import ", filename)
874 g.usedPackages[df.PackageName()] = true
David Symonds4decd802011-08-04 11:27:07 +1000875
David Symondsb2a00c82011-08-04 16:52:58 +1000876 for _, sym := range df.exported[id.o] {
877 sym.GenerateAlias(g, df.PackageName())
878 }
879
880 g.P()
David Symonds4decd802011-08-04 11:27:07 +1000881}
882
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700883// Generate the enum definitions for this EnumDescriptor.
884func (g *Generator) generateEnum(enum *EnumDescriptor) {
885 // The full type name
886 typeName := enum.TypeName()
887 // The full type name, CamelCased.
888 ccTypeName := CamelCaseSlice(typeName)
889 ccPrefix := enum.prefix()
890 g.P("type ", ccTypeName, " int32")
David Symondsb2a00c82011-08-04 16:52:58 +1000891 g.file.addExport(enum, enumSymbol(ccTypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700892 g.P("const (")
893 g.In()
894 for _, e := range enum.Value {
David Symonds31d58a22011-01-20 18:33:21 +1100895 name := ccPrefix + *e.Name
David Symondsdd9ca042011-08-29 16:07:16 +1000896 g.P(name, " ", ccTypeName, " = ", e.Number)
David Symondsb2a00c82011-08-04 16:52:58 +1000897 g.file.addExport(enum, constOrVarSymbol{name, "const"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700898 }
899 g.Out()
900 g.P(")")
David Symonds940b9612011-04-01 10:45:23 +1100901 g.P("var ", ccTypeName, "_name = map[int32]string{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700902 g.In()
Rob Pikec9e7d972010-06-10 10:30:22 -0700903 generated := make(map[int32]bool) // avoid duplicate values
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700904 for _, e := range enum.Value {
905 duplicate := ""
906 if _, present := generated[*e.Number]; present {
907 duplicate = "// Duplicate value: "
908 }
909 g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",")
910 generated[*e.Number] = true
911 }
912 g.Out()
913 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100914 g.P("var ", ccTypeName, "_value = map[string]int32{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700915 g.In()
916 for _, e := range enum.Value {
917 g.P(Quote(*e.Name), ": ", e.Number, ",")
918 }
919 g.Out()
920 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100921
David Symondsdd9ca042011-08-29 16:07:16 +1000922 g.P("func New", ccTypeName, "(x ", ccTypeName, ") *", ccTypeName, " {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700923 g.In()
924 g.P("e := ", ccTypeName, "(x)")
925 g.P("return &e")
926 g.Out()
927 g.P("}")
David Symonds940b9612011-04-01 10:45:23 +1100928
929 g.P("func (x ", ccTypeName, ") String() string {")
930 g.In()
931 g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))")
932 g.Out()
933 g.P("}")
934
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700935 g.P()
936}
937
David Symonds8935abf2011-07-04 15:53:16 +1000938// The tag is a string like "varint,2,opt,name=fieldname,def=7" that
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700939// identifies details of the field for the protocol buffer marshaling and unmarshaling
940// code. The fields are:
941// wire encoding
942// protocol tag number
943// opt,req,rep for optional, required, or repeated
David Symonds5b7775e2010-12-01 10:09:04 +1100944// packed whether the encoding is "packed" (optional; repeated primitives only)
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700945// name= the original declared name
946// enum= the name of the enum type if it is an enum-typed field.
947// def= string representation of the default value, if any.
948// The default value must be in a representation that can be used at run-time
949// to generate the default value. Thus bools become 0 and 1, for instance.
950func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string {
951 optrepreq := ""
952 switch {
953 case isOptional(field):
954 optrepreq = "opt"
955 case isRequired(field):
956 optrepreq = "req"
957 case isRepeated(field):
958 optrepreq = "rep"
959 }
960 defaultValue := proto.GetString(field.DefaultValue)
961 if defaultValue != "" {
962 switch *field.Type {
963 case descriptor.FieldDescriptorProto_TYPE_BOOL:
964 if defaultValue == "true" {
965 defaultValue = "1"
966 } else {
967 defaultValue = "0"
968 }
969 case descriptor.FieldDescriptorProto_TYPE_STRING,
970 descriptor.FieldDescriptorProto_TYPE_BYTES:
David Symondsb79d99b2011-08-29 16:38:49 +1000971 // Nothing to do. Quoting is done for the whole tag.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700972 case descriptor.FieldDescriptorProto_TYPE_ENUM:
973 // For enums we need to provide the integer constant.
974 obj := g.ObjectNamed(proto.GetString(field.TypeName))
975 enum, ok := obj.(*EnumDescriptor)
976 if !ok {
977 g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName()))
978 }
979 defaultValue = enum.integerValueAsString(defaultValue)
980 }
981 defaultValue = ",def=" + defaultValue
982 }
983 enum := ""
984 if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
David Symonds4decd802011-08-04 11:27:07 +1000985 // We avoid using obj.PackageName(), because we want to use the
986 // original (proto-world) package name.
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700987 obj := g.ObjectNamed(proto.GetString(field.TypeName))
David Symonds4decd802011-08-04 11:27:07 +1000988 enum = ",enum="
989 if pkg := proto.GetString(obj.File().Package); pkg != "" {
990 enum += pkg + "."
991 }
992 enum += CamelCaseSlice(obj.TypeName())
Rob Pikeaf82b4e2010-04-30 15:19:25 -0700993 }
David Symonds5b7775e2010-12-01 10:09:04 +1100994 packed := ""
995 if field.Options != nil && proto.GetBool(field.Options.Packed) {
996 packed = ",packed"
997 }
David Symondse37856c2011-06-22 12:52:53 +1000998 fieldName := proto.GetString(field.Name)
999 name := fieldName
David Symonds9f402812011-04-28 18:08:44 +10001000 if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP {
1001 // We must use the type name for groups instead of
1002 // the field name to preserve capitalization.
1003 // type_name in FieldDescriptorProto is fully-qualified,
1004 // but we only want the local part.
1005 name = *field.TypeName
1006 if i := strings.LastIndex(name, "."); i >= 0 {
1007 name = name[i+1:]
1008 }
David Symondse37856c2011-06-22 12:52:53 +10001009 }
1010 if name == CamelCase(fieldName) {
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001011 name = ""
1012 } else {
1013 name = ",name=" + name
1014 }
David Symonds8935abf2011-07-04 15:53:16 +10001015 return Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001016 wiretype,
1017 proto.GetInt32(field.Number),
1018 optrepreq,
David Symonds5b7775e2010-12-01 10:09:04 +11001019 packed,
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001020 name,
1021 enum,
1022 defaultValue))
1023}
1024
1025func needsStar(typ descriptor.FieldDescriptorProto_Type) bool {
1026 switch typ {
1027 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1028 return false
1029 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1030 return false
1031 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1032 return false
1033 }
1034 return true
1035}
1036
1037// TypeName is the printed name appropriate for an item. If the object is in the current file,
1038// TypeName drops the package name and underscores the rest.
David Symonds7d5c8242011-03-14 12:03:50 -07001039// Otherwise the object is from another package; and the result is the underscored
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001040// package name followed by the item name.
1041// The result always has an initial capital.
1042func (g *Generator) TypeName(obj Object) string {
1043 return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName())
1044}
1045
1046// TypeNameWithPackage is like TypeName, but always includes the package
1047// name even if the object is in our own package.
1048func (g *Generator) TypeNameWithPackage(obj Object) string {
1049 return obj.PackageName() + CamelCaseSlice(obj.TypeName())
1050}
1051
1052// GoType returns a string representing the type name, and the wire type
1053func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) {
1054 // TODO: Options.
1055 switch *field.Type {
1056 case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
1057 typ, wire = "float64", "fixed64"
1058 case descriptor.FieldDescriptorProto_TYPE_FLOAT:
1059 typ, wire = "float32", "fixed32"
1060 case descriptor.FieldDescriptorProto_TYPE_INT64:
1061 typ, wire = "int64", "varint"
1062 case descriptor.FieldDescriptorProto_TYPE_UINT64:
1063 typ, wire = "uint64", "varint"
1064 case descriptor.FieldDescriptorProto_TYPE_INT32:
1065 typ, wire = "int32", "varint"
1066 case descriptor.FieldDescriptorProto_TYPE_UINT32:
1067 typ, wire = "uint32", "varint"
1068 case descriptor.FieldDescriptorProto_TYPE_FIXED64:
1069 typ, wire = "uint64", "fixed64"
1070 case descriptor.FieldDescriptorProto_TYPE_FIXED32:
1071 typ, wire = "uint32", "fixed32"
1072 case descriptor.FieldDescriptorProto_TYPE_BOOL:
1073 typ, wire = "bool", "varint"
1074 case descriptor.FieldDescriptorProto_TYPE_STRING:
1075 typ, wire = "string", "bytes"
1076 case descriptor.FieldDescriptorProto_TYPE_GROUP:
1077 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1078 typ, wire = "*"+g.TypeName(desc), "group"
1079 case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
1080 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1081 typ, wire = "*"+g.TypeName(desc), "bytes"
1082 case descriptor.FieldDescriptorProto_TYPE_BYTES:
1083 typ, wire = "[]byte", "bytes"
1084 case descriptor.FieldDescriptorProto_TYPE_ENUM:
1085 desc := g.ObjectNamed(proto.GetString(field.TypeName))
1086 typ, wire = g.TypeName(desc), "varint"
1087 case descriptor.FieldDescriptorProto_TYPE_SFIXED32:
1088 typ, wire = "int32", "fixed32"
1089 case descriptor.FieldDescriptorProto_TYPE_SFIXED64:
1090 typ, wire = "int64", "fixed64"
1091 case descriptor.FieldDescriptorProto_TYPE_SINT32:
1092 typ, wire = "int32", "zigzag32"
1093 case descriptor.FieldDescriptorProto_TYPE_SINT64:
1094 typ, wire = "int64", "zigzag64"
1095 default:
1096 g.Fail("unknown type for", proto.GetString(field.Name))
1097 }
1098 if isRepeated(field) {
1099 typ = "[]" + typ
1100 } else if needsStar(*field.Type) {
1101 typ = "*" + typ
1102 }
1103 return
1104}
1105
David Symondsf90e3382010-05-05 10:53:44 +10001106func (g *Generator) RecordTypeUse(t string) {
1107 if obj, ok := g.typeNameToObject[t]; ok {
David Symonds4decd802011-08-04 11:27:07 +10001108 // Call ObjectNamed to get the true object to record the use.
1109 obj = g.ObjectNamed(t)
David Symondsf90e3382010-05-05 10:53:44 +10001110 g.usedPackages[obj.PackageName()] = true
1111 }
1112}
1113
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001114// Generate the type and default constant definitions for this Descriptor.
1115func (g *Generator) generateMessage(message *Descriptor) {
1116 // The full type name
1117 typeName := message.TypeName()
1118 // The full type name, CamelCased.
1119 ccTypeName := CamelCaseSlice(typeName)
1120
1121 g.P("type ", ccTypeName, " struct {")
1122 g.In()
1123 for _, field := range message.Field {
1124 fieldname := CamelCase(*field.Name)
1125 typename, wiretype := g.GoType(message, field)
David Symondsb2a00c82011-08-04 16:52:58 +10001126 jsonName := *field.Name
1127 tag := fmt.Sprintf("`protobuf:%s json:%q`", g.goTag(field, wiretype), jsonName)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001128 g.P(fieldname, "\t", typename, "\t", tag)
David Symondsf90e3382010-05-05 10:53:44 +10001129 g.RecordTypeUse(proto.GetString(field.TypeName))
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001130 }
1131 if len(message.ExtensionRange) > 0 {
David Symonds1d72f7a2011-08-19 18:28:52 +10001132 g.P("XXX_extensions\t\tmap[int32]", g.ProtoPkg, ".Extension")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001133 }
1134 g.P("XXX_unrecognized\t[]byte")
1135 g.Out()
1136 g.P("}")
1137
David Symondse37856c2011-06-22 12:52:53 +10001138 // Reset and String functions
1139 g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }")
1140 g.P("func (this *", ccTypeName, ") String() string { return ", g.ProtoPkg, ".CompactTextString(this) }")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001141
1142 // Extension support methods
David Symonds31d58a22011-01-20 18:33:21 +11001143 var hasExtensions, isMessageSet bool
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001144 if len(message.ExtensionRange) > 0 {
David Symonds31d58a22011-01-20 18:33:21 +11001145 hasExtensions = true
David Symonds4fee3b12010-11-11 10:00:13 +11001146 // message_set_wire_format only makes sense when extensions are defined.
1147 if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) {
David Symonds31d58a22011-01-20 18:33:21 +11001148 isMessageSet = true
David Symonds4fee3b12010-11-11 10:00:13 +11001149 g.P()
1150 g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {")
1151 g.In()
1152 g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())")
1153 g.Out()
1154 g.P("}")
1155 g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {")
1156 g.In()
1157 g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())")
1158 g.Out()
1159 g.P("}")
1160 g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler")
1161 g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)")
1162 g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)")
1163 }
1164
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001165 g.P()
Rob Pikec9e7d972010-06-10 10:30:22 -07001166 g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001167 g.In()
1168 for _, r := range message.ExtensionRange {
Rob Pikec9e7d972010-06-10 10:30:22 -07001169 end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends
David Symonds1d72f7a2011-08-19 18:28:52 +10001170 g.P("{", r.Start, ", ", end, "},")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001171 }
1172 g.Out()
1173 g.P("}")
Rob Pikec9e7d972010-06-10 10:30:22 -07001174 g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001175 g.In()
1176 g.P("return extRange_", ccTypeName)
1177 g.Out()
1178 g.P("}")
David Symonds1d72f7a2011-08-19 18:28:52 +10001179 g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32]", g.ProtoPkg, ".Extension {")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001180 g.In()
1181 g.P("if this.XXX_extensions == nil {")
1182 g.In()
David Symonds1d72f7a2011-08-19 18:28:52 +10001183 g.P("this.XXX_extensions = make(map[int32]", g.ProtoPkg, ".Extension)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001184 g.Out()
1185 g.P("}")
1186 g.P("return this.XXX_extensions")
1187 g.Out()
1188 g.P("}")
1189 }
1190
David Symondsb2a00c82011-08-04 16:52:58 +10001191 g.file.addExport(message, messageSymbol{ccTypeName, hasExtensions, isMessageSet})
David Symonds31d58a22011-01-20 18:33:21 +11001192
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001193 // Default constants
1194 for _, field := range message.Field {
1195 def := proto.GetString(field.DefaultValue)
1196 if def == "" {
1197 continue
1198 }
1199 fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name)
1200 typename, _ := g.GoType(message, field)
1201 if typename[0] == '*' {
1202 typename = typename[1:]
1203 }
1204 kind := "const "
1205 switch {
1206 case typename == "bool":
1207 case typename == "string":
1208 def = Quote(def)
1209 case typename == "[]byte":
1210 def = "[]byte(" + Quote(def) + ")"
1211 kind = "var "
David Symondscea785b2011-01-07 11:02:30 +11001212 case def == "inf", def == "-inf", def == "nan":
1213 // These names are known to, and defined by, the protocol language.
1214 switch def {
1215 case "inf":
1216 def = "math.Inf(1)"
1217 case "-inf":
1218 def = "math.Inf(-1)"
1219 case "nan":
1220 def = "math.NaN()"
1221 }
1222 if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT {
1223 def = "float32(" + def + ")"
1224 }
1225 kind = "var "
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001226 case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM:
1227 // Must be an enum. Need to construct the prefixed name.
1228 obj := g.ObjectNamed(proto.GetString(field.TypeName))
1229 enum, ok := obj.(*EnumDescriptor)
1230 if !ok {
Rob Pike5194c512010-10-14 13:02:16 -07001231 log.Println("don't know how to generate constant for", fieldname)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001232 continue
1233 }
Rob Pike87af39e2010-07-19 10:48:02 -07001234 def = g.DefaultPackageName(enum) + enum.prefix() + def
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001235 }
1236 g.P(kind, fieldname, " ", typename, " = ", def)
David Symondsb2a00c82011-08-04 16:52:58 +10001237 g.file.addExport(message, constOrVarSymbol{fieldname, kind})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001238 }
1239 g.P()
1240
1241 for _, ext := range message.ext {
1242 g.generateExtension(ext)
1243 }
1244}
1245
1246func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
David Symondse37856c2011-06-22 12:52:53 +10001247 ccTypeName := ext.DescName()
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001248
1249 extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
1250 field := ext.FieldDescriptorProto
1251 fieldType, wireType := g.GoType(ext.parent, field)
1252 tag := g.goTag(field, wireType)
David Symondsf90e3382010-05-05 10:53:44 +10001253 g.RecordTypeUse(*ext.Extendee)
David Symonds9f402812011-04-28 18:08:44 +10001254 if n := ext.FieldDescriptorProto.TypeName; n != nil {
1255 // foreign extension type
1256 g.RecordTypeUse(*n)
1257 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001258
Rob Pikec9e7d972010-06-10 10:30:22 -07001259 g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001260 g.In()
1261 g.P("ExtendedType: (", extendedType, ")(nil),")
1262 g.P("ExtensionType: (", fieldType, ")(nil),")
1263 g.P("Field: ", field.Number, ",")
David Symonds1d72f7a2011-08-19 18:28:52 +10001264 g.P(`Name: "`, g.packageName, ".", strings.Join(ext.TypeName(), "."), `",`)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001265 g.P("Tag: ", tag, ",")
1266
1267 g.Out()
1268 g.P("}")
1269 g.P()
David Symonds31d58a22011-01-20 18:33:21 +11001270
David Symondsb2a00c82011-08-04 16:52:58 +10001271 g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var"})
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001272}
1273
1274func (g *Generator) generateInitFunction() {
1275 g.P("func init() {")
1276 g.In()
1277 for _, enum := range g.file.enum {
1278 g.generateEnumRegistration(enum)
1279 }
David Symondse37856c2011-06-22 12:52:53 +10001280 for _, d := range g.file.desc {
1281 for _, ext := range d.ext {
1282 g.generateExtensionRegistration(ext)
1283 }
1284 }
1285 for _, ext := range g.file.ext {
1286 g.generateExtensionRegistration(ext)
1287 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001288 g.Out()
1289 g.P("}")
1290}
1291
1292func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) {
David Symonds4decd802011-08-04 11:27:07 +10001293 // // We always print the full (proto-world) package name here.
1294 pkg := proto.GetString(enum.File().Package)
1295 if pkg != "" {
1296 pkg += "."
1297 }
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001298 // The full type name
1299 typeName := enum.TypeName()
1300 // The full type name, CamelCased.
1301 ccTypeName := CamelCaseSlice(typeName)
Rob Pikec9e7d972010-06-10 10:30:22 -07001302 g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)")
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001303}
1304
David Symondse37856c2011-06-22 12:52:53 +10001305func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) {
1306 g.P(g.ProtoPkg+".RegisterExtension(", ext.DescName(), ")")
1307}
1308
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001309// And now lots of helper functions.
1310
Rob Pike2c7bafc2010-06-10 16:07:14 -07001311// Is c an ASCII lower-case letter?
1312func isASCIILower(c byte) bool {
1313 return 'a' <= c && c <= 'z'
1314}
1315
1316// Is c an ASCII digit?
1317func isASCIIDigit(c byte) bool {
1318 return '0' <= c && c <= '9'
1319}
1320
1321// CamelCase returns the CamelCased name.
1322// If there is an interior underscore followed by a lower case letter,
1323// drop the underscore and convert the letter to upper case.
1324// There is a remote possibility of this rewrite causing a name collision,
1325// but it's so remote we're prepared to pretend it's nonexistent - since the
1326// C++ generator lowercases names, it's extremely unlikely to have two fields
1327// with different capitalizations.
1328// In short, _my_field_name_2 becomes XMyFieldName2.
1329func CamelCase(s string) string {
1330 if s == "" {
1331 return ""
1332 }
1333 t := make([]byte, 0, 32)
Rob Pike2c7bafc2010-06-10 16:07:14 -07001334 i := 0
1335 if s[0] == '_' {
1336 // Need a capital letter; drop the '_'.
Rob Pike99fa2b62010-12-02 10:39:42 -08001337 t = append(t, 'X')
Rob Pike2c7bafc2010-06-10 16:07:14 -07001338 i++
1339 }
1340 // Invariant: if the next letter is lower case, it must be converted
1341 // to upper case.
1342 // That is, we process a word at a time, where words are marked by _ or
1343 // upper case letter. Digits are treated as words.
1344 for ; i < len(s); i++ {
1345 c := s[i]
Rob Pike2c7bafc2010-06-10 16:07:14 -07001346 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
1347 continue // Skip the underscore in s.
1348 }
1349 if isASCIIDigit(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001350 t = append(t, c)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001351 continue
1352 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001353 // Assume we have a letter now - if not, it's a bogus identifier.
1354 // The next word is a sequence of characters that must start upper case.
1355 if isASCIILower(c) {
Rob Pike99fa2b62010-12-02 10:39:42 -08001356 c ^= ' ' // Make it a capital letter.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001357 }
Rob Pike99fa2b62010-12-02 10:39:42 -08001358 t = append(t, c) // Guaranteed not lower case.
Rob Pike2c7bafc2010-06-10 16:07:14 -07001359 // Accept lower case sequence that follows.
1360 for i+1 < len(s) && isASCIILower(s[i+1]) {
1361 i++
Rob Pike99fa2b62010-12-02 10:39:42 -08001362 t = append(t, s[i])
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001363 }
1364 }
Rob Pike2c7bafc2010-06-10 16:07:14 -07001365 return string(t)
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001366}
1367
1368// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
1369// be joined with "_".
1370func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
1371
1372// dottedSlice turns a sliced name into a dotted name.
1373func dottedSlice(elem []string) string { return strings.Join(elem, ".") }
1374
1375// Quote returns a Go-source quoted string representation of s.
1376func Quote(s string) string { return fmt.Sprintf("%q", s) }
1377
1378// Given a .proto file name, return the output name for the generated Go program.
1379func goFileName(name string) string {
Rob Pike87af39e2010-07-19 10:48:02 -07001380 ext := path.Ext(name)
1381 if ext == ".proto" || ext == ".protodevel" {
1382 name = name[0 : len(name)-len(ext)]
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001383 }
1384 return name + ".pb.go"
1385}
1386
1387// Is this field optional?
1388func isOptional(field *descriptor.FieldDescriptorProto) bool {
1389 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL
1390}
1391
1392// Is this field required?
1393func isRequired(field *descriptor.FieldDescriptorProto) bool {
1394 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED
1395}
1396
1397// Is this field repeated?
1398func isRepeated(field *descriptor.FieldDescriptorProto) bool {
1399 return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
1400}
1401
1402// DotToUnderscore is the mapping function used to generate Go names from package names,
Rob Pikec9e7d972010-06-10 10:30:22 -07001403// which can be dotted in the input .proto file. It maps dots to underscores.
1404// Because we also get here from package names generated from file names, it also maps
1405// minus signs to underscores.
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001406func DotToUnderscore(rune int) int {
Rob Pikec9e7d972010-06-10 10:30:22 -07001407 switch rune {
1408 case '.', '-':
Rob Pikeaf82b4e2010-04-30 15:19:25 -07001409 return '_'
1410 }
1411 return rune
1412}
Rob Pikec9e7d972010-06-10 10:30:22 -07001413
1414// BaseName returns the last path element of the name, with the last dotted suffix removed.
1415func BaseName(name string) string {
1416 // First, find the last element
1417 if i := strings.LastIndex(name, "/"); i >= 0 {
1418 name = name[i+1:]
1419 }
1420 // Now drop the suffix
1421 if i := strings.LastIndex(name, "."); i >= 0 {
1422 name = name[0:i]
1423 }
1424 return name
1425}