helper.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. // Protocol Buffers for Go with Gadgets
  2. //
  3. // Copyright (c) 2013, The GoGo Authors. All rights reserved.
  4. // http://github.com/gogo/protobuf
  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. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  20. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  21. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  22. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  23. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. package generator
  29. import (
  30. "bytes"
  31. "go/parser"
  32. "go/printer"
  33. "go/token"
  34. "path"
  35. "strings"
  36. "github.com/gogo/protobuf/gogoproto"
  37. "github.com/gogo/protobuf/proto"
  38. descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
  39. plugin "github.com/gogo/protobuf/protoc-gen-gogo/plugin"
  40. )
  41. func (d *FileDescriptor) Messages() []*Descriptor {
  42. return d.desc
  43. }
  44. func (d *FileDescriptor) Enums() []*EnumDescriptor {
  45. return d.enum
  46. }
  47. func (d *Descriptor) IsGroup() bool {
  48. return d.group
  49. }
  50. func (g *Generator) IsGroup(field *descriptor.FieldDescriptorProto) bool {
  51. if d, ok := g.typeNameToObject[field.GetTypeName()].(*Descriptor); ok {
  52. return d.IsGroup()
  53. }
  54. return false
  55. }
  56. func (g *Generator) TypeNameByObject(typeName string) Object {
  57. o, ok := g.typeNameToObject[typeName]
  58. if !ok {
  59. g.Fail("can't find object with type", typeName)
  60. }
  61. return o
  62. }
  63. func (g *Generator) OneOfTypeName(message *Descriptor, field *descriptor.FieldDescriptorProto) string {
  64. typeName := message.TypeName()
  65. ccTypeName := CamelCaseSlice(typeName)
  66. fieldName := g.GetOneOfFieldName(message, field)
  67. tname := ccTypeName + "_" + fieldName
  68. // It is possible for this to collide with a message or enum
  69. // nested in this message. Check for collisions.
  70. ok := true
  71. for _, desc := range message.nested {
  72. if strings.Join(desc.TypeName(), "_") == tname {
  73. ok = false
  74. break
  75. }
  76. }
  77. for _, enum := range message.enums {
  78. if strings.Join(enum.TypeName(), "_") == tname {
  79. ok = false
  80. break
  81. }
  82. }
  83. if !ok {
  84. tname += "_"
  85. }
  86. return tname
  87. }
  88. type PluginImports interface {
  89. NewImport(pkg string) Single
  90. GenerateImports(file *FileDescriptor)
  91. }
  92. type pluginImports struct {
  93. generator *Generator
  94. singles []Single
  95. }
  96. func NewPluginImports(generator *Generator) *pluginImports {
  97. return &pluginImports{generator, make([]Single, 0)}
  98. }
  99. func (this *pluginImports) NewImport(pkg string) Single {
  100. imp := newImportedPackage(this.generator.ImportPrefix, pkg)
  101. this.singles = append(this.singles, imp)
  102. return imp
  103. }
  104. func (this *pluginImports) GenerateImports(file *FileDescriptor) {
  105. for _, s := range this.singles {
  106. if s.IsUsed() {
  107. this.generator.PrintImport(GoPackageName(s.Name()), GoImportPath(s.Location()))
  108. }
  109. }
  110. }
  111. type Single interface {
  112. Use() string
  113. IsUsed() bool
  114. Name() string
  115. Location() string
  116. }
  117. type importedPackage struct {
  118. used bool
  119. pkg string
  120. name string
  121. importPrefix string
  122. }
  123. func newImportedPackage(importPrefix string, pkg string) *importedPackage {
  124. return &importedPackage{
  125. pkg: pkg,
  126. importPrefix: importPrefix,
  127. }
  128. }
  129. func (this *importedPackage) Use() string {
  130. if !this.used {
  131. this.name = string(cleanPackageName(this.pkg))
  132. this.used = true
  133. }
  134. return this.name
  135. }
  136. func (this *importedPackage) IsUsed() bool {
  137. return this.used
  138. }
  139. func (this *importedPackage) Name() string {
  140. return this.name
  141. }
  142. func (this *importedPackage) Location() string {
  143. return this.importPrefix + this.pkg
  144. }
  145. func (g *Generator) GetFieldName(message *Descriptor, field *descriptor.FieldDescriptorProto) string {
  146. goTyp, _ := g.GoType(message, field)
  147. fieldname := CamelCase(*field.Name)
  148. if gogoproto.IsCustomName(field) {
  149. fieldname = gogoproto.GetCustomName(field)
  150. }
  151. if gogoproto.IsEmbed(field) {
  152. fieldname = EmbedFieldName(goTyp)
  153. }
  154. if field.OneofIndex != nil {
  155. fieldname = message.OneofDecl[int(*field.OneofIndex)].GetName()
  156. fieldname = CamelCase(fieldname)
  157. }
  158. for _, f := range methodNames {
  159. if f == fieldname {
  160. return fieldname + "_"
  161. }
  162. }
  163. if !gogoproto.IsProtoSizer(message.file.FileDescriptorProto, message.DescriptorProto) {
  164. if fieldname == "Size" {
  165. return fieldname + "_"
  166. }
  167. }
  168. return fieldname
  169. }
  170. func (g *Generator) GetOneOfFieldName(message *Descriptor, field *descriptor.FieldDescriptorProto) string {
  171. goTyp, _ := g.GoType(message, field)
  172. fieldname := CamelCase(*field.Name)
  173. if gogoproto.IsCustomName(field) {
  174. fieldname = gogoproto.GetCustomName(field)
  175. }
  176. if gogoproto.IsEmbed(field) {
  177. fieldname = EmbedFieldName(goTyp)
  178. }
  179. for _, f := range methodNames {
  180. if f == fieldname {
  181. return fieldname + "_"
  182. }
  183. }
  184. if !gogoproto.IsProtoSizer(message.file.FileDescriptorProto, message.DescriptorProto) {
  185. if fieldname == "Size" {
  186. return fieldname + "_"
  187. }
  188. }
  189. return fieldname
  190. }
  191. func (g *Generator) IsMap(field *descriptor.FieldDescriptorProto) bool {
  192. if !field.IsMessage() {
  193. return false
  194. }
  195. byName := g.ObjectNamed(field.GetTypeName())
  196. desc, ok := byName.(*Descriptor)
  197. if byName == nil || !ok || !desc.GetOptions().GetMapEntry() {
  198. return false
  199. }
  200. return true
  201. }
  202. func (g *Generator) GetMapKeyField(field, keyField *descriptor.FieldDescriptorProto) *descriptor.FieldDescriptorProto {
  203. if !gogoproto.IsCastKey(field) {
  204. return keyField
  205. }
  206. keyField = proto.Clone(keyField).(*descriptor.FieldDescriptorProto)
  207. if keyField.Options == nil {
  208. keyField.Options = &descriptor.FieldOptions{}
  209. }
  210. keyType := gogoproto.GetCastKey(field)
  211. if err := proto.SetExtension(keyField.Options, gogoproto.E_Casttype, &keyType); err != nil {
  212. g.Fail(err.Error())
  213. }
  214. return keyField
  215. }
  216. func (g *Generator) GetMapValueField(field, valField *descriptor.FieldDescriptorProto) *descriptor.FieldDescriptorProto {
  217. if gogoproto.IsCustomType(field) && gogoproto.IsCastValue(field) {
  218. g.Fail("cannot have a customtype and casttype: ", field.String())
  219. }
  220. valField = proto.Clone(valField).(*descriptor.FieldDescriptorProto)
  221. if valField.Options == nil {
  222. valField.Options = &descriptor.FieldOptions{}
  223. }
  224. stdtime := gogoproto.IsStdTime(field)
  225. if stdtime {
  226. if err := proto.SetExtension(valField.Options, gogoproto.E_Stdtime, &stdtime); err != nil {
  227. g.Fail(err.Error())
  228. }
  229. }
  230. stddur := gogoproto.IsStdDuration(field)
  231. if stddur {
  232. if err := proto.SetExtension(valField.Options, gogoproto.E_Stdduration, &stddur); err != nil {
  233. g.Fail(err.Error())
  234. }
  235. }
  236. if valType := gogoproto.GetCastValue(field); len(valType) > 0 {
  237. if err := proto.SetExtension(valField.Options, gogoproto.E_Casttype, &valType); err != nil {
  238. g.Fail(err.Error())
  239. }
  240. }
  241. if valType := gogoproto.GetCustomType(field); len(valType) > 0 {
  242. if err := proto.SetExtension(valField.Options, gogoproto.E_Customtype, &valType); err != nil {
  243. g.Fail(err.Error())
  244. }
  245. }
  246. nullable := gogoproto.IsNullable(field)
  247. if err := proto.SetExtension(valField.Options, gogoproto.E_Nullable, &nullable); err != nil {
  248. g.Fail(err.Error())
  249. }
  250. return valField
  251. }
  252. // GoMapValueTypes returns the map value Go type and the alias map value Go type (for casting), taking into
  253. // account whether the map is nullable or the value is a message.
  254. func GoMapValueTypes(mapField, valueField *descriptor.FieldDescriptorProto, goValueType, goValueAliasType string) (nullable bool, outGoType string, outGoAliasType string) {
  255. nullable = gogoproto.IsNullable(mapField) && (valueField.IsMessage() || gogoproto.IsCustomType(mapField))
  256. if nullable {
  257. // ensure the non-aliased Go value type is a pointer for consistency
  258. if strings.HasPrefix(goValueType, "*") {
  259. outGoType = goValueType
  260. } else {
  261. outGoType = "*" + goValueType
  262. }
  263. outGoAliasType = goValueAliasType
  264. } else {
  265. outGoType = strings.Replace(goValueType, "*", "", 1)
  266. outGoAliasType = strings.Replace(goValueAliasType, "*", "", 1)
  267. }
  268. return
  269. }
  270. func GoTypeToName(goTyp string) string {
  271. return strings.Replace(strings.Replace(goTyp, "*", "", -1), "[]", "", -1)
  272. }
  273. func EmbedFieldName(goTyp string) string {
  274. goTyp = GoTypeToName(goTyp)
  275. goTyps := strings.Split(goTyp, ".")
  276. if len(goTyps) == 1 {
  277. return goTyp
  278. }
  279. if len(goTyps) == 2 {
  280. return goTyps[1]
  281. }
  282. panic("unreachable")
  283. }
  284. func (g *Generator) GeneratePlugin(p Plugin) {
  285. plugins = []Plugin{p}
  286. p.Init(g)
  287. // Generate the output. The generator runs for every file, even the files
  288. // that we don't generate output for, so that we can collate the full list
  289. // of exported symbols to support public imports.
  290. genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles))
  291. for _, file := range g.genFiles {
  292. genFileMap[file] = true
  293. }
  294. for _, file := range g.allFiles {
  295. g.Reset()
  296. g.writeOutput = genFileMap[file]
  297. g.generatePlugin(file, p)
  298. if !g.writeOutput {
  299. continue
  300. }
  301. g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{
  302. Name: proto.String(file.goFileName(g.pathType)),
  303. Content: proto.String(g.String()),
  304. })
  305. }
  306. }
  307. func (g *Generator) generatePlugin(file *FileDescriptor, p Plugin) {
  308. g.writtenImports = make(map[string]bool)
  309. g.file = file
  310. // Run the plugins before the imports so we know which imports are necessary.
  311. p.Generate(file)
  312. // Generate header and imports last, though they appear first in the output.
  313. rem := g.Buffer
  314. g.Buffer = new(bytes.Buffer)
  315. g.generateHeader()
  316. p.GenerateImports(g.file)
  317. g.generateImports()
  318. if !g.writeOutput {
  319. return
  320. }
  321. g.Write(rem.Bytes())
  322. // Reformat generated code.
  323. contents := string(g.Buffer.Bytes())
  324. fset := token.NewFileSet()
  325. ast, err := parser.ParseFile(fset, "", g, parser.ParseComments)
  326. if err != nil {
  327. g.Fail("bad Go source code was generated:", contents, err.Error())
  328. return
  329. }
  330. g.Reset()
  331. err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(g, fset, ast)
  332. if err != nil {
  333. g.Fail("generated Go source code could not be reformatted:", err.Error())
  334. }
  335. }
  336. func GetCustomType(field *descriptor.FieldDescriptorProto) (packageName string, typ string, err error) {
  337. return getCustomType(field)
  338. }
  339. func getCustomType(field *descriptor.FieldDescriptorProto) (packageName string, typ string, err error) {
  340. if field.Options != nil {
  341. var v interface{}
  342. v, err = proto.GetExtension(field.Options, gogoproto.E_Customtype)
  343. if err == nil && v.(*string) != nil {
  344. ctype := *(v.(*string))
  345. packageName, typ = splitCPackageType(ctype)
  346. return packageName, typ, nil
  347. }
  348. }
  349. return "", "", err
  350. }
  351. func splitCPackageType(ctype string) (packageName string, typ string) {
  352. ss := strings.Split(ctype, ".")
  353. if len(ss) == 1 {
  354. return "", ctype
  355. }
  356. packageName = strings.Join(ss[0:len(ss)-1], ".")
  357. typeName := ss[len(ss)-1]
  358. importStr := strings.Map(badToUnderscore, packageName)
  359. typ = importStr + "." + typeName
  360. return packageName, typ
  361. }
  362. func getCastType(field *descriptor.FieldDescriptorProto) (packageName string, typ string, err error) {
  363. if field.Options != nil {
  364. var v interface{}
  365. v, err = proto.GetExtension(field.Options, gogoproto.E_Casttype)
  366. if err == nil && v.(*string) != nil {
  367. ctype := *(v.(*string))
  368. packageName, typ = splitCPackageType(ctype)
  369. return packageName, typ, nil
  370. }
  371. }
  372. return "", "", err
  373. }
  374. func FileName(file *FileDescriptor) string {
  375. fname := path.Base(file.FileDescriptorProto.GetName())
  376. fname = strings.Replace(fname, ".proto", "", -1)
  377. fname = strings.Replace(fname, "-", "_", -1)
  378. fname = strings.Replace(fname, ".", "_", -1)
  379. return CamelCase(fname)
  380. }
  381. func (g *Generator) AllFiles() *descriptor.FileDescriptorSet {
  382. set := &descriptor.FileDescriptorSet{}
  383. set.File = make([]*descriptor.FileDescriptorProto, len(g.allFiles))
  384. for i := range g.allFiles {
  385. set.File[i] = g.allFiles[i].FileDescriptorProto
  386. }
  387. return set
  388. }
  389. func (d *Descriptor) Path() string {
  390. return d.path
  391. }
  392. func (g *Generator) useTypes() string {
  393. pkg := strings.Map(badToUnderscore, "github.com/gogo/protobuf/types")
  394. g.customImports = append(g.customImports, "github.com/gogo/protobuf/types")
  395. return pkg
  396. }
  397. func (d *FileDescriptor) GoPackageName() string {
  398. return string(d.packageName)
  399. }