generator.go 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403
  1. // Copyright 2018 Twitch Interactive, Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"). You may not
  4. // use this file except in compliance with the License. A copy of the License is
  5. // located at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // or in the "license" file accompanying this file. This file is distributed on
  10. // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  11. // express or implied. See the License for the specific language governing
  12. // permissions and limitations under the License.
  13. package main
  14. import (
  15. "bufio"
  16. "bytes"
  17. "compress/gzip"
  18. "fmt"
  19. "go/ast"
  20. "go/parser"
  21. "go/printer"
  22. "go/token"
  23. "io/ioutil"
  24. "os"
  25. "path"
  26. "path/filepath"
  27. "reflect"
  28. "sort"
  29. "strconv"
  30. "strings"
  31. "unicode"
  32. "go-common/app/tool/liverpc/protoc-gen-liverpc/gen"
  33. "go-common/app/tool/liverpc/protoc-gen-liverpc/gen/stringutils"
  34. "go-common/app/tool/liverpc/protoc-gen-liverpc/gen/typemap"
  35. "github.com/golang/protobuf/proto"
  36. "github.com/golang/protobuf/protoc-gen-go/descriptor"
  37. plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
  38. "github.com/pkg/errors"
  39. "github.com/siddontang/go/ioutil2"
  40. )
  41. var legacyPathMapping = map[string]string{
  42. "live.webucenter": "live.web-ucenter",
  43. "live.webroom": "live.web-room",
  44. "live.appucenter": "live.app-ucenter",
  45. "live.appblink": "live.app-blink",
  46. "live.approom": "live.app-room",
  47. "live.appinterface": "live.app-interface",
  48. "live.liveadmin": "live.live-admin",
  49. "live.resource": "live.resource",
  50. "live.livedemo": "live.live-demo",
  51. "live.lotteryinterface": "live.lottery-interface",
  52. }
  53. type bm struct {
  54. filesHandled int
  55. reg *typemap.Registry
  56. // Map to record whether we've built each package
  57. pkgs map[string]string
  58. pkgNamesInUse map[string]bool
  59. importPrefix string // String to prefix to imported package file names.
  60. importMap map[string]string // Mapping from .proto file name to import path.
  61. tpl bool // only generate service template file, no docs, no .bm.go, default false
  62. // Package naming:
  63. genPkgName string // Name of the package that we're generating
  64. fileToGoPackageName map[*descriptor.FileDescriptorProto]string
  65. // List of files that were inputs to the generator. We need to hold this in
  66. // the struct so we can write a header for the file that lists its inputs.
  67. genFiles []*descriptor.FileDescriptorProto
  68. // Output buffer that holds the bytes we want to write out for a single file.
  69. // Gets reset after working on a file.
  70. output *bytes.Buffer
  71. deps map[string]string
  72. }
  73. // if current dir is a go-common project
  74. // or is the internal directory of a go-common project
  75. // this present a project info
  76. type projectInfo struct {
  77. absolutePath string
  78. // relative to go-common
  79. importPath string
  80. name string
  81. department string
  82. // interface, service, admin ...
  83. typ string
  84. hasInternalPkg bool
  85. // 从工作目录到project目录的相对路径 比如./ ../
  86. pathRefToProj string
  87. }
  88. // projectInfo for current directory
  89. var projInfo *projectInfo
  90. func bmGenerator() *bm {
  91. t := &bm{
  92. pkgs: make(map[string]string),
  93. pkgNamesInUse: make(map[string]bool),
  94. importMap: make(map[string]string),
  95. fileToGoPackageName: make(map[*descriptor.FileDescriptorProto]string),
  96. output: bytes.NewBuffer(nil),
  97. }
  98. return t
  99. }
  100. func (t *bm) Generate(in *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse {
  101. params, err := parseCommandLineParams(in.GetParameter())
  102. if err != nil {
  103. gen.Fail("could not parse parameters passed to --bm_out", err.Error())
  104. }
  105. t.importPrefix = params.importPrefix
  106. t.importMap = params.importMap
  107. t.tpl = params.tpl
  108. t.genFiles = gen.FilesToGenerate(in)
  109. // Collect information on types.
  110. t.reg = typemap.New(in.ProtoFile)
  111. t.registerPackageName("context")
  112. t.registerPackageName("ioutil")
  113. t.registerPackageName("proto")
  114. // Time to figure out package names of objects defined in protobuf. First,
  115. // we'll figure out the name for the package we're generating.
  116. genPkgName, err := deduceGenPkgName(t.genFiles)
  117. if err != nil {
  118. gen.Fail(err.Error())
  119. }
  120. t.genPkgName = genPkgName
  121. // Next, we need to pick names for all the files that are dependencies.
  122. if len(in.ProtoFile) > 0 {
  123. t.initProjInfo(in.ProtoFile[0])
  124. }
  125. for _, f := range in.ProtoFile {
  126. if fileDescSliceContains(t.genFiles, f) {
  127. // This is a file we are generating. It gets the shared package name.
  128. t.fileToGoPackageName[f] = t.genPkgName
  129. } else {
  130. // This is a dependency. Use its package name.
  131. name := f.GetPackage()
  132. if name == "" {
  133. name = stringutils.BaseName(f.GetName())
  134. }
  135. name = stringutils.CleanIdentifier(name)
  136. alias := t.registerPackageName(name)
  137. t.fileToGoPackageName[f] = alias
  138. }
  139. }
  140. // Showtime! Generate the response.
  141. resp := new(plugin.CodeGeneratorResponse)
  142. for _, f := range t.genFiles {
  143. respFile := t.generate(f)
  144. if respFile != nil {
  145. resp.File = append(resp.File, respFile)
  146. }
  147. for _, s := range f.Service {
  148. docResp := t.generateDoc(f, s)
  149. if docResp != nil {
  150. resp.File = append(resp.File, docResp)
  151. }
  152. }
  153. if t.tpl {
  154. if projInfo != nil {
  155. for _, s := range f.Service {
  156. serviceResp := t.generateServiceImpl(f, s)
  157. if serviceResp != nil {
  158. resp.File = append(resp.File, serviceResp)
  159. }
  160. }
  161. }
  162. }
  163. }
  164. return resp
  165. }
  166. // lookupProjPath get project path by proto absolute path
  167. // assume that proto is in the project's model directory
  168. func lookupProjPath(protoAbs string) (result string) {
  169. lastIndex := len(protoAbs)
  170. curPath := protoAbs
  171. for lastIndex > 0 {
  172. if ioutil2.FileExists(curPath+"/cmd") && ioutil2.FileExists(curPath+"/api") {
  173. result = curPath
  174. return
  175. }
  176. lastIndex = strings.LastIndex(curPath, string(os.PathSeparator))
  177. curPath = protoAbs[:lastIndex]
  178. }
  179. result = ""
  180. return
  181. }
  182. func (t *bm) initProjInfo(file *descriptor.FileDescriptorProto) {
  183. var err error
  184. projInfo = &projectInfo{}
  185. defer func() {
  186. if err != nil {
  187. projInfo = nil
  188. }
  189. }()
  190. wd, err := os.Getwd()
  191. if err != nil {
  192. panic("cannot get working directory")
  193. }
  194. protoAbs := wd + "/" + file.GetName()
  195. appIndex := strings.Index(wd, "go-common/app/")
  196. if appIndex == -1 {
  197. err = errors.New("not in go-common/app/")
  198. return
  199. }
  200. projPath := lookupProjPath(protoAbs)
  201. if projPath == "" {
  202. err = errors.New("not in project")
  203. return
  204. }
  205. if strings.Contains(wd, projPath) {
  206. rest := strings.Replace(wd, projPath, "", 1)
  207. projInfo.pathRefToProj = "./"
  208. if rest != "" {
  209. split := strings.Split(rest, "/")
  210. ref := ""
  211. for i := 0; i < len(split)-1; i++ {
  212. ref = ref + "../"
  213. }
  214. projInfo.pathRefToProj = ref
  215. }
  216. }
  217. projInfo.absolutePath = projPath
  218. if ioutil2.FileExists(projPath + "/internal") {
  219. projInfo.hasInternalPkg = true
  220. }
  221. relativePath := projInfo.absolutePath[appIndex+len("go-common/app/"):]
  222. projInfo.importPath = "go-common/app/" + relativePath
  223. split := strings.Split(relativePath, "/")
  224. projInfo.typ = split[0]
  225. projInfo.department = split[1]
  226. projInfo.name = split[2]
  227. }
  228. // find tag between backtick, start & end is the position of backtick
  229. func getLineTag(line string) (tag reflect.StructTag, start int, end int) {
  230. start = strings.Index(line, "`")
  231. end = strings.LastIndex(line, "`")
  232. if end <= start {
  233. return
  234. }
  235. tag = reflect.StructTag(line[start+1 : end])
  236. return
  237. }
  238. func getCommentWithoutTag(comment string) []string {
  239. var lines []string
  240. if comment == "" {
  241. return lines
  242. }
  243. split := strings.Split(strings.TrimRight(comment, "\n\r"), "\n")
  244. for _, line := range split {
  245. tag, _, _ := getLineTag(line)
  246. if tag == "" {
  247. lines = append(lines, line)
  248. }
  249. }
  250. return lines
  251. }
  252. func getTagsInComment(comment string) []reflect.StructTag {
  253. split := strings.Split(comment, "\n")
  254. var tagsInComment []reflect.StructTag
  255. for _, line := range split {
  256. tag, _, _ := getLineTag(line)
  257. if tag != "" {
  258. tagsInComment = append(tagsInComment, tag)
  259. }
  260. }
  261. return tagsInComment
  262. }
  263. func getTagValue(key string, tags []reflect.StructTag) string {
  264. for _, t := range tags {
  265. val := t.Get(key)
  266. if val != "" {
  267. return val
  268. }
  269. }
  270. return ""
  271. }
  272. // Is this field repeated?
  273. func isRepeated(field *descriptor.FieldDescriptorProto) bool {
  274. return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
  275. }
  276. func (t *bm) isMap(field *descriptor.FieldDescriptorProto) bool {
  277. if field.GetType() != descriptor.FieldDescriptorProto_TYPE_MESSAGE {
  278. return false
  279. }
  280. md := t.reg.MessageDefinition(field.GetTypeName())
  281. if md == nil || !md.Descriptor.GetOptions().GetMapEntry() {
  282. return false
  283. }
  284. return true
  285. }
  286. func (t *bm) generateToc(file *descriptor.FileDescriptorProto, service *descriptor.ServiceDescriptorProto) {
  287. for _, method := range service.Method {
  288. comment, _ := t.reg.MethodComments(file, service, method)
  289. tags := getTagsInComment(comment.Leading)
  290. _, path, newPath := t.getHttpInfo(file, service, method, tags)
  291. cleanComments := getCommentWithoutTag(comment.Leading)
  292. var title string
  293. if len(cleanComments) > 0 {
  294. title = cleanComments[0]
  295. }
  296. // 如果有老的路径,只显示老的路径文档
  297. if path != "" {
  298. anchor := strings.Replace(path, "/", "", -1)
  299. t.P(fmt.Sprintf("- [%s](#%s) %s", path, anchor, title))
  300. } else {
  301. anchor := strings.Replace(newPath, "/", "", -1)
  302. t.P(fmt.Sprintf("- [%s](#%s) %s", newPath, anchor, title))
  303. }
  304. }
  305. }
  306. func (t *bm) generateDoc(file *descriptor.FileDescriptorProto, service *descriptor.ServiceDescriptorProto) *plugin.CodeGeneratorResponse_File {
  307. resp := new(plugin.CodeGeneratorResponse_File)
  308. var name = goFileName(file, "."+lcFirst(service.GetName())+".md")
  309. resp.Name = &name
  310. t.P("<!-- package=" + file.GetPackage() + " -->")
  311. t.generateToc(file, service)
  312. for _, method := range service.Method {
  313. comment, err := t.reg.MethodComments(file, service, method)
  314. tags := getTagsInComment(comment.Leading)
  315. cleanComments := getCommentWithoutTag(comment.Leading)
  316. midwaresStr := getTagValue("midware", tags)
  317. needAuth := false
  318. if midwaresStr != "" {
  319. split := strings.Split(midwaresStr, ",")
  320. for _, m := range split {
  321. if m == "auth" {
  322. needAuth = true
  323. break
  324. }
  325. }
  326. }
  327. t.P()
  328. httpMethod, legacyPath, path := t.getHttpInfo(file, service, method, tags)
  329. if legacyPath != "" {
  330. path = legacyPath
  331. }
  332. t.P("## " + path)
  333. if err == nil {
  334. if len(cleanComments) == 0 {
  335. t.P(`### 无标题`)
  336. } else {
  337. t.P(`###`, strings.Join(cleanComments, "\n"))
  338. }
  339. }
  340. t.P()
  341. if needAuth {
  342. t.P(`> `, "需要登录")
  343. t.P()
  344. }
  345. t.P("#### 方法:" + httpMethod)
  346. t.P()
  347. t.genRequestParam(file, service, method)
  348. t.P("#### 响应")
  349. t.P()
  350. t.P("```javascript")
  351. t.P(`{`)
  352. t.P(` "code": 0,`)
  353. t.P(` "message": "ok",`)
  354. t.P(t.getExampleJson(file, service, method))
  355. t.P(`}`)
  356. t.P("```")
  357. t.P()
  358. }
  359. resp.Content = proto.String(t.output.String())
  360. t.output.Reset()
  361. return resp
  362. }
  363. func (t *bm) genRequestParam(
  364. file *descriptor.FileDescriptorProto,
  365. svc *descriptor.ServiceDescriptorProto,
  366. method *descriptor.MethodDescriptorProto) {
  367. md := t.reg.MessageDefinition(method.GetInputType())
  368. t.P(`#### 请求参数`)
  369. t.P()
  370. var outputs []string
  371. for i, f := range md.Descriptor.Field {
  372. if f.GetType() == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
  373. // 如果有message 只能以json的方式显示参数了
  374. var buf = &[]string{}
  375. t.exampleJsonForMsg(md, file, buf, "", 0, "")
  376. j := strings.Join(*buf, "\n")
  377. t.P("```javascript")
  378. t.P(j)
  379. t.P("```")
  380. t.P()
  381. return
  382. }
  383. if i == 0 {
  384. outputs = append(outputs, `|参数名|必选|类型|描述|`)
  385. outputs = append(outputs, `|:---|:---|:---|:---|`)
  386. }
  387. fComment, _ := t.reg.FieldComments(file, md, f)
  388. var tags []reflect.StructTag
  389. {
  390. //get required info from gogoproto.moretags
  391. moretags := getMoreTags(f)
  392. if moretags != nil {
  393. tags = []reflect.StructTag{reflect.StructTag(*moretags)}
  394. }
  395. }
  396. if len(tags) == 0 {
  397. tags = getTagsInComment(fComment.Leading)
  398. }
  399. validateTag := getTagValue("validate", tags)
  400. var validateRules []string
  401. if validateTag != "" {
  402. validateRules = strings.Split(validateTag, ",")
  403. }
  404. required := false
  405. for _, rule := range validateRules {
  406. if rule == "required" {
  407. required = true
  408. }
  409. }
  410. requiredDesc := "是"
  411. if !required {
  412. requiredDesc = "否"
  413. }
  414. _, typeName := t.mockValueForField(f, tags)
  415. split := strings.Split(fComment.Leading, "\n")
  416. desc := ""
  417. for _, line := range split {
  418. if line != "" {
  419. tag, _, _ := getLineTag(line)
  420. if tag == "" {
  421. desc += line
  422. }
  423. }
  424. }
  425. outputs = append(outputs, fmt.Sprintf(`|%s|%s|%s|%s|`, getJsonTag(f), requiredDesc, typeName, desc))
  426. }
  427. for _, s := range outputs {
  428. t.P(s)
  429. }
  430. t.P()
  431. }
  432. func (t *bm) getExampleJson(file *descriptor.FileDescriptorProto,
  433. svc *descriptor.ServiceDescriptorProto,
  434. method *descriptor.MethodDescriptorProto) string {
  435. md := t.reg.MessageDefinition(method.GetOutputType())
  436. var buf = &[]string{}
  437. t.exampleJsonForMsg(md, file, buf, "data", 4, "")
  438. return strings.Join(*buf, "\n")
  439. }
  440. func makeIndentStr(i int) string {
  441. return strings.Repeat(" ", i)
  442. }
  443. func (t *bm) mockValueForField(field *descriptor.FieldDescriptorProto,
  444. tags []reflect.StructTag) (mockVal string, typeName string) {
  445. tagMock := getTagValue("mock", tags)
  446. mockVal = "\"unknown\""
  447. typeName = "unknown"
  448. switch field.GetType() {
  449. case descriptor.FieldDescriptorProto_TYPE_BOOL:
  450. if tagMock == "true" || tagMock == "false" {
  451. mockVal = tagMock
  452. } else {
  453. mockVal = "true"
  454. }
  455. typeName = "bool"
  456. case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
  457. descriptor.FieldDescriptorProto_TYPE_FLOAT:
  458. mockVal = "0.1"
  459. if tagMock != "" {
  460. if _, err := strconv.ParseFloat(tagMock, 64); err == nil {
  461. mockVal = tagMock
  462. }
  463. }
  464. typeName = "float"
  465. case
  466. descriptor.FieldDescriptorProto_TYPE_INT64,
  467. descriptor.FieldDescriptorProto_TYPE_UINT64,
  468. descriptor.FieldDescriptorProto_TYPE_INT32,
  469. descriptor.FieldDescriptorProto_TYPE_FIXED64,
  470. descriptor.FieldDescriptorProto_TYPE_FIXED32,
  471. descriptor.FieldDescriptorProto_TYPE_ENUM,
  472. descriptor.FieldDescriptorProto_TYPE_UINT32,
  473. descriptor.FieldDescriptorProto_TYPE_SFIXED32,
  474. descriptor.FieldDescriptorProto_TYPE_SFIXED64,
  475. descriptor.FieldDescriptorProto_TYPE_SINT32,
  476. descriptor.FieldDescriptorProto_TYPE_SINT64:
  477. mockVal = "0"
  478. if tagMock != "" {
  479. if _, err := strconv.Atoi(tagMock); err == nil {
  480. mockVal = tagMock
  481. }
  482. }
  483. typeName = "integer"
  484. case
  485. descriptor.FieldDescriptorProto_TYPE_STRING,
  486. descriptor.FieldDescriptorProto_TYPE_BYTES:
  487. mockVal = `""`
  488. if tagMock != "" {
  489. mockVal = strconv.Quote(tagMock)
  490. }
  491. typeName = "string"
  492. }
  493. if isRepeated(field) {
  494. typeName = "多个" + typeName
  495. }
  496. return
  497. }
  498. func (t *bm) exampleJsonForMsg(
  499. msg *typemap.MessageDefinition,
  500. file *descriptor.FileDescriptorProto,
  501. buf *[]string, fieldName string, indent int, outEndComma string) {
  502. if fieldName == "" {
  503. *buf = append(*buf, makeIndentStr(indent)+"{")
  504. } else {
  505. *buf = append(*buf, makeIndentStr(indent)+fmt.Sprintf(`"%s": {`, fieldName))
  506. }
  507. num := len(msg.Descriptor.Field)
  508. for i, f := range msg.Descriptor.Field {
  509. isScalar := isScalar(f)
  510. fComment, _ := t.reg.FieldComments(file, msg, f)
  511. cleanComment := getCommentWithoutTag(fComment.Leading)
  512. for _, line := range cleanComment {
  513. if strings.Trim(line, " \t\n\r") != "" {
  514. *buf = append(*buf, makeIndentStr(indent+4)+"// "+line)
  515. }
  516. }
  517. endComma := ""
  518. if i < (num - 1) {
  519. endComma = ","
  520. }
  521. repeated := isRepeated(f)
  522. tags := getTagsInComment(fComment.Leading)
  523. if isScalar {
  524. mockVal, _ := t.mockValueForField(f, tags)
  525. if repeated {
  526. // "key" : [
  527. // value
  528. // ]
  529. *buf = append(*buf, makeIndentStr(indent+4)+`"`+getJsonTag(f)+`": [`)
  530. *buf = append(*buf, makeIndentStr(indent+8)+mockVal)
  531. *buf = append(*buf, makeIndentStr(indent+4)+`]`+endComma)
  532. } else {
  533. // "key" : value
  534. *buf = append(*buf, makeIndentStr(indent+4)+`"`+getJsonTag(f)+`": `+mockVal+endComma)
  535. }
  536. } else {
  537. isMap := t.isMap(f)
  538. if repeated {
  539. if isMap {
  540. *buf = append(*buf, makeIndentStr(indent+4)+`"`+getJsonTag(f)+`": {`)
  541. } else {
  542. *buf = append(*buf, makeIndentStr(indent+4)+`"`+getJsonTag(f)+`": [`)
  543. }
  544. }
  545. subMsg := t.reg.MessageDefinition(f.GetTypeName())
  546. if subMsg == nil {
  547. panic(fmt.Sprintf("%v%v", f.TypeName, f.Type))
  548. }
  549. nextIndent := indent + 4
  550. nextFname := getJsonTag(f)
  551. if repeated {
  552. nextIndent = indent + 8
  553. nextFname = ""
  554. }
  555. if isMap {
  556. mapKeyField := subMsg.Descriptor.Field[0]
  557. mapValueField := subMsg.Descriptor.Field[1]
  558. keyDesc := "mapKey"
  559. if mapKeyField.GetType() != descriptor.FieldDescriptorProto_TYPE_STRING {
  560. keyDesc = "1"
  561. }
  562. if mapValueField.GetType() == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
  563. // "mapKey" : {
  564. // ...
  565. // }
  566. mapValueMsg := t.reg.MessageDefinition(mapValueField.GetTypeName())
  567. t.exampleJsonForMsg(mapValueMsg, file, buf, keyDesc, nextIndent, "")
  568. } else {
  569. // "mapKey" : "map value"
  570. val, _ := t.mockValueForField(mapValueField, tags)
  571. *buf = append(*buf, makeIndentStr(indent+8)+`"`+keyDesc+`": `+val)
  572. }
  573. *buf = append(*buf, makeIndentStr(indent+4)+`}`+endComma)
  574. } else {
  575. if repeated {
  576. t.exampleJsonForMsg(subMsg, file, buf, nextFname, nextIndent, "")
  577. *buf = append(*buf, makeIndentStr(indent+4)+`]`+endComma)
  578. } else {
  579. t.exampleJsonForMsg(subMsg, file, buf, nextFname, nextIndent, endComma)
  580. }
  581. }
  582. }
  583. }
  584. *buf = append(*buf, makeIndentStr(indent)+"}"+outEndComma)
  585. }
  586. // Is this field a scalar numeric type?
  587. func isScalar(field *descriptor.FieldDescriptorProto) bool {
  588. if field.Type == nil {
  589. return false
  590. }
  591. switch *field.Type {
  592. case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
  593. descriptor.FieldDescriptorProto_TYPE_FLOAT,
  594. descriptor.FieldDescriptorProto_TYPE_INT64,
  595. descriptor.FieldDescriptorProto_TYPE_UINT64,
  596. descriptor.FieldDescriptorProto_TYPE_INT32,
  597. descriptor.FieldDescriptorProto_TYPE_FIXED64,
  598. descriptor.FieldDescriptorProto_TYPE_FIXED32,
  599. descriptor.FieldDescriptorProto_TYPE_BOOL,
  600. descriptor.FieldDescriptorProto_TYPE_UINT32,
  601. descriptor.FieldDescriptorProto_TYPE_ENUM,
  602. descriptor.FieldDescriptorProto_TYPE_SFIXED32,
  603. descriptor.FieldDescriptorProto_TYPE_SFIXED64,
  604. descriptor.FieldDescriptorProto_TYPE_SINT32,
  605. descriptor.FieldDescriptorProto_TYPE_SINT64,
  606. descriptor.FieldDescriptorProto_TYPE_BYTES,
  607. descriptor.FieldDescriptorProto_TYPE_STRING:
  608. return true
  609. default:
  610. return false
  611. }
  612. }
  613. func (t *bm) registerPackageName(name string) (alias string) {
  614. alias = name
  615. i := 1
  616. for t.pkgNamesInUse[alias] {
  617. alias = name + strconv.Itoa(i)
  618. i++
  619. }
  620. t.pkgNamesInUse[alias] = true
  621. t.pkgs[name] = alias
  622. return alias
  623. }
  624. type visitor struct {
  625. funcMap map[string]bool
  626. }
  627. func (v visitor) Visit(n ast.Node) ast.Visitor {
  628. switch d := n.(type) {
  629. case *ast.FuncDecl:
  630. v.funcMap[d.Name.Name] = true
  631. }
  632. return v
  633. }
  634. // generateServiceImpl returns service implementation file service/{prefix}/service.go
  635. // if file not exists
  636. // else it returns nil
  637. func (t *bm) generateServiceImpl(file *descriptor.FileDescriptorProto, svc *descriptor.ServiceDescriptorProto) *plugin.CodeGeneratorResponse_File {
  638. resp := new(plugin.CodeGeneratorResponse_File)
  639. prefix := t.getVersionPrefix()
  640. importPath := t.getPbImportPath(file.GetName())
  641. var alias = t.getPkgAlias()
  642. confPath := projInfo.importPath + "/conf"
  643. if projInfo.hasInternalPkg {
  644. confPath = projInfo.importPath + "/internal/conf"
  645. }
  646. name := "service/" + prefix + "/" + lcFirst(svc.GetName()) + ".go"
  647. if projInfo.hasInternalPkg {
  648. name = "internal/" + name
  649. }
  650. name = projInfo.pathRefToProj + name
  651. resp.Name = &name
  652. if _, err := os.Stat(name); !os.IsNotExist(err) {
  653. // Insert methods if file already exists
  654. fset := token.NewFileSet()
  655. astTree, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
  656. if err != nil {
  657. panic("parse file error: " + name + " err: " + err.Error())
  658. }
  659. v := visitor{funcMap: map[string]bool{}}
  660. ast.Walk(v, astTree)
  661. t.output.Reset()
  662. buf, err := ioutil.ReadFile(name)
  663. if err != nil {
  664. panic("cannot read file:" + name)
  665. }
  666. t.P(string(buf))
  667. t.generateBmImpl(file, svc, v.funcMap)
  668. resp.Content = proto.String(t.formattedOutput())
  669. t.output.Reset()
  670. return resp
  671. }
  672. tplPkg := "service"
  673. if t.genPkgName[:1] == "v" {
  674. tplPkg = t.genPkgName
  675. }
  676. t.P(`package `, tplPkg)
  677. t.P()
  678. t.P(`import (`)
  679. t.P(` `, alias, ` "`, importPath, `"`)
  680. t.P(` "`, confPath, `"`)
  681. t.P(` "context"`)
  682. t.P(`)`)
  683. for pkg, importPath := range t.deps {
  684. t.P(`import `, pkg, ` `, importPath)
  685. }
  686. svcStructName := serviceName(svc) + "Service"
  687. t.P(`// `, svcStructName, ` struct`)
  688. t.P(`type `, svcStructName, ` struct {`)
  689. t.P(` conf *conf.Config`)
  690. t.P(` // optionally add other properties here, such as dao`)
  691. t.P(` // dao *dao.Dao`)
  692. t.P(`}`)
  693. t.P()
  694. t.P(`//New`, svcStructName, ` init`)
  695. t.P(`func New`, svcStructName, `(c *conf.Config) (s *`, svcStructName, `) {`)
  696. t.P(` s = &`, svcStructName, `{`)
  697. t.P(` conf: c,`)
  698. t.P(` }`)
  699. t.P(` return s`)
  700. t.P(`}`)
  701. comments, err := t.reg.ServiceComments(file, svc)
  702. if err == nil {
  703. t.printComments(comments)
  704. }
  705. t.P()
  706. t.generateBmImpl(file, svc, map[string]bool{})
  707. resp.Content = proto.String(t.formattedOutput())
  708. t.output.Reset()
  709. return resp
  710. }
  711. func (t *bm) generate(file *descriptor.FileDescriptorProto) *plugin.CodeGeneratorResponse_File {
  712. resp := new(plugin.CodeGeneratorResponse_File)
  713. if len(file.Service) == 0 {
  714. return nil
  715. }
  716. t.generateFileHeader(file, t.genPkgName)
  717. t.generateImports(file)
  718. t.generateMiddlewareInfo(file)
  719. for i, service := range file.Service {
  720. t.generateService(file, service, i)
  721. t.generateSingleRoute(file, service, i)
  722. }
  723. t.generateFileDescriptor(file)
  724. resp.Name = proto.String(goFileName(file, ".bm.go"))
  725. resp.Content = proto.String(t.formattedOutput())
  726. t.output.Reset()
  727. t.filesHandled++
  728. return resp
  729. }
  730. func (t *bm) generateMiddlewareInfo(file *descriptor.FileDescriptorProto) {
  731. t.P()
  732. for _, service := range file.Service {
  733. name := serviceName(service)
  734. for _, method := range service.Method {
  735. _, _, path := t.getHttpInfo(file, service, method, nil)
  736. t.P(`var Path`, name, methodName(method), ` = "`, path, `"`)
  737. }
  738. t.P()
  739. }
  740. }
  741. func (t *bm) generateFileHeader(file *descriptor.FileDescriptorProto, pkgName string) {
  742. t.P("// Code generated by protoc-gen-bm ", gen.Version, ", DO NOT EDIT.")
  743. t.P("// source: ", file.GetName())
  744. t.P()
  745. if t.filesHandled == 0 {
  746. t.P("/*")
  747. t.P("Package ", t.genPkgName, " is a generated blademaster stub package.")
  748. t.P("This code was generated with go-common/app/tool/bmgen/protoc-gen-bm ", gen.Version, ".")
  749. t.P()
  750. comment, err := t.reg.FileComments(file)
  751. if err == nil && comment.Leading != "" {
  752. for _, line := range strings.Split(comment.Leading, "\n") {
  753. line = strings.TrimPrefix(line, " ")
  754. // ensure we don't escape from the block comment
  755. line = strings.Replace(line, "*/", "* /", -1)
  756. t.P(line)
  757. }
  758. t.P()
  759. }
  760. t.P("It is generated from these files:")
  761. for _, f := range t.genFiles {
  762. t.P("\t", f.GetName())
  763. }
  764. t.P("*/")
  765. }
  766. t.P(`package `, pkgName)
  767. t.P()
  768. }
  769. func (t *bm) generateImports(file *descriptor.FileDescriptorProto) {
  770. if len(file.Service) == 0 {
  771. return
  772. }
  773. t.P(`import (`)
  774. //t.P(` `,t.pkgs["context"], ` "context"`)
  775. t.P(` "context"`)
  776. t.P()
  777. t.P(` bm "go-common/library/net/http/blademaster"`)
  778. t.P(` "go-common/library/net/http/blademaster/binding"`)
  779. t.P(`)`)
  780. // It's legal to import a message and use it as an input or output for a
  781. // method. Make sure to import the package of any such message. First, dedupe
  782. // them.
  783. deps := make(map[string]string) // Map of package name to quoted import path.
  784. ourImportPath := path.Dir(goFileName(file, ""))
  785. for _, s := range file.Service {
  786. for _, m := range s.Method {
  787. defs := []*typemap.MessageDefinition{
  788. t.reg.MethodInputDefinition(m),
  789. t.reg.MethodOutputDefinition(m),
  790. }
  791. for _, def := range defs {
  792. // By default, import path is the dirname of the Go filename.
  793. importPath := path.Dir(goFileName(def.File, ""))
  794. if importPath == ourImportPath {
  795. continue
  796. }
  797. if substitution, ok := t.importMap[def.File.GetName()]; ok {
  798. importPath = substitution
  799. }
  800. importPath = t.importPrefix + importPath
  801. pkg := t.goPackageName(def.File)
  802. deps[pkg] = strconv.Quote(importPath)
  803. }
  804. }
  805. }
  806. t.deps = deps
  807. for pkg, importPath := range deps {
  808. t.P(`import `, pkg, ` `, importPath)
  809. }
  810. if len(deps) > 0 {
  811. t.P()
  812. }
  813. t.P()
  814. t.P(`// to suppressed 'imported but not used warning'`)
  815. t.P(`var _ *bm.Context`)
  816. t.P(`var _ context.Context`)
  817. t.P(`var _ binding.StructValidator`)
  818. }
  819. // P forwards to g.gen.P, which prints output.
  820. func (t *bm) P(args ...string) {
  821. for _, v := range args {
  822. t.output.WriteString(v)
  823. }
  824. t.output.WriteByte('\n')
  825. }
  826. // Big header comments to makes it easier to visually parse a generated file.
  827. func (t *bm) sectionComment(sectionTitle string) {
  828. t.P()
  829. t.P(`// `, strings.Repeat("=", len(sectionTitle)))
  830. t.P(`// `, sectionTitle)
  831. t.P(`// `, strings.Repeat("=", len(sectionTitle)))
  832. t.P()
  833. }
  834. func (t *bm) generateService(file *descriptor.FileDescriptorProto, service *descriptor.ServiceDescriptorProto, index int) {
  835. servName := serviceName(service)
  836. t.sectionComment(servName + ` Interface`)
  837. t.generateBMInterface(file, service)
  838. }
  839. // import project/api的路径
  840. func (t *bm) getPbImportPath(filename string) (importPath string) {
  841. wd, err := os.Getwd()
  842. if err != nil {
  843. panic("cannot get working directory")
  844. }
  845. index := strings.Index(wd, "go-common")
  846. if index == -1 {
  847. gen.Fail("must use inside go-common")
  848. }
  849. dir := filepath.Dir(filename)
  850. if dir != "." {
  851. importPath = wd + "/" + dir
  852. } else {
  853. importPath = wd
  854. }
  855. importPath = importPath[index:]
  856. return
  857. }
  858. // getProjPath return project path relative to GOPATH
  859. func (t *bm) getProjPath() string {
  860. wd, err := os.Getwd()
  861. if err != nil {
  862. panic("cannot get working directory")
  863. }
  864. index := strings.Index(wd, "go-common")
  865. if index == -1 {
  866. gen.Fail("must use inside go-common")
  867. }
  868. projPkgPath := wd[index:]
  869. return projPkgPath
  870. }
  871. func lcFirst(str string) string {
  872. for i, v := range str {
  873. return string(unicode.ToLower(v)) + str[i+1:]
  874. }
  875. return ""
  876. }
  877. // TODO rename
  878. func (t *bm) getLegacyPathPrefix(
  879. svc *descriptor.ServiceDescriptorProto, pathParts []string, isInternal bool) (uriPrefix string) {
  880. var parts []string
  881. parts = append(parts, pathParts[0])
  882. if isInternal {
  883. parts = append(parts, "internal")
  884. }
  885. parts = append(parts, pathParts[1:]...)
  886. uriPrefix = fmt.Sprintf("/x%s/%s", strings.Join(parts, "/"), lcFirst(svc.GetName()))
  887. return
  888. }
  889. func (t *bm) getHttpInfo(
  890. file *descriptor.FileDescriptorProto,
  891. service *descriptor.ServiceDescriptorProto,
  892. method *descriptor.MethodDescriptorProto,
  893. tags []reflect.StructTag,
  894. ) (httpMethod string, oldPath string, newPath string) {
  895. googleOptionInfo, err := ParseBMMethod(method)
  896. if err == nil {
  897. httpMethod = strings.ToUpper(googleOptionInfo.Method)
  898. p := googleOptionInfo.PathPattern
  899. if p != "" {
  900. oldPath = p
  901. newPath = p
  902. return
  903. }
  904. }
  905. if httpMethod == "" {
  906. // resolve http method
  907. httpMethod = getTagValue("method", tags)
  908. if httpMethod == "" {
  909. httpMethod = "GET"
  910. } else {
  911. httpMethod = strings.ToUpper(httpMethod)
  912. }
  913. }
  914. isLegacy, parts := t.convertLegacyPackage(file.GetPackage())
  915. if isLegacy {
  916. apiInternal := getTagValue("internal", tags) == "true"
  917. pathPrefix := t.getLegacyPathPrefix(service, parts, apiInternal)
  918. oldPath = pathPrefix + `/` + method.GetName()
  919. }
  920. newPath = "/" + file.GetPackage() + "." + service.GetName() + "/" + method.GetName()
  921. return
  922. }
  923. // 返回空,则不用考虑历史package
  924. // 如果非空,则表示按照返回的pathParts做url规则
  925. func (t *bm) convertLegacyPackage(pkgName string) (isLegacy bool, pathParts []string) {
  926. var splits = strings.Split(pkgName, ".")
  927. var remain []string
  928. if len(splits) >= 2 {
  929. splits = splits[0:2]
  930. remain = splits[2:]
  931. }
  932. var pkgPrefix = strings.Join(splits, ".")
  933. legacyPkg, isLegacy := legacyPathMapping[pkgPrefix]
  934. if isLegacy {
  935. legacyPkg = strings.Replace(pkgName, pkgPrefix, legacyPkg, 1)
  936. pathParts = append(pathParts, strings.Split(legacyPkg, ".")...)
  937. pathParts = append(pathParts, remain...)
  938. }
  939. return
  940. }
  941. func (t *bm) generateSingleRoute(
  942. file *descriptor.FileDescriptorProto,
  943. service *descriptor.ServiceDescriptorProto,
  944. index int) {
  945. // old mode is generate xx.route.go in the http pkg
  946. // new mode is generate route code in the same .bm.go
  947. // route rule /x{department}/{project-name}/{path_prefix}/method_name
  948. // generate each route method
  949. servName := serviceName(service)
  950. versionPrefix := t.getVersionPrefix()
  951. svcName := lcFirst(stringutils.CamelCase(versionPrefix)) + servName + "Svc"
  952. t.P(`var `, svcName, ` `, servName, `BMServer`)
  953. type methodInfo struct {
  954. httpMethod string
  955. midwares []string
  956. routeFuncName string
  957. path string
  958. legacyPath string
  959. methodName string
  960. }
  961. var methList []methodInfo
  962. var allMidwareMap = make(map[string]bool)
  963. var isLegacyPkg = false
  964. for _, method := range service.Method {
  965. var httpMethod string
  966. var midwares []string
  967. comments, _ := t.reg.MethodComments(file, service, method)
  968. tags := getTagsInComment(comments.Leading)
  969. if getTagValue("dynamic", tags) == "true" {
  970. continue
  971. }
  972. httpMethod, legacyPath, path := t.getHttpInfo(file, service, method, tags)
  973. if legacyPath != "" {
  974. isLegacyPkg = true
  975. }
  976. midStr := getTagValue("midware", tags)
  977. if midStr != "" {
  978. midwares = strings.Split(midStr, ",")
  979. for _, m := range midwares {
  980. allMidwareMap[m] = true
  981. }
  982. }
  983. methName := methodName(method)
  984. inputType := t.goTypeName(method.GetInputType())
  985. routeName := lcFirst(stringutils.CamelCase(servName) +
  986. stringutils.CamelCase(methName))
  987. methList = append(methList, methodInfo{
  988. httpMethod: httpMethod,
  989. midwares: midwares,
  990. routeFuncName: routeName,
  991. path: path,
  992. legacyPath: legacyPath,
  993. methodName: method.GetName(),
  994. })
  995. t.P(fmt.Sprintf("func %s (c *bm.Context) {", routeName))
  996. t.P(` p := new(`, inputType, `)`)
  997. t.P(` if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))); err != nil {`)
  998. t.P(` return`)
  999. t.P(` }`)
  1000. t.P(` resp, err := `, svcName, `.`, methName, `(c, p)`)
  1001. t.P(` c.JSON(resp, err)`)
  1002. t.P(`}`)
  1003. t.P(``)
  1004. }
  1005. // generate route group
  1006. var midList []string
  1007. for m := range allMidwareMap {
  1008. midList = append(midList, m+" bm.HandlerFunc")
  1009. }
  1010. sort.Strings(midList)
  1011. // 注册老的路由的方法
  1012. if isLegacyPkg {
  1013. funcName := `Register` + stringutils.CamelCase(versionPrefix) + servName + `Service`
  1014. t.P(`// `, funcName, ` Register the blademaster route with middleware map`)
  1015. t.P(`// midMap is the middleware map, the key is defined in proto`)
  1016. t.P(`func `, funcName, `(e *bm.Engine, svc `, servName, "BMServer, midMap map[string]bm.HandlerFunc)", ` {`)
  1017. var keys []string
  1018. for m := range allMidwareMap {
  1019. keys = append(keys, m)
  1020. }
  1021. // to keep generated code consistent
  1022. sort.Strings(keys)
  1023. for _, m := range keys {
  1024. t.P(m, ` := midMap["`, m, `"]`)
  1025. }
  1026. t.P(svcName, ` = svc`)
  1027. for _, methInfo := range methList {
  1028. var midArgStr string
  1029. if len(methInfo.midwares) == 0 {
  1030. midArgStr = ""
  1031. } else {
  1032. midArgStr = strings.Join(methInfo.midwares, ", ") + ", "
  1033. }
  1034. t.P(`e.`, methInfo.httpMethod, `("`, methInfo.legacyPath, `", `, midArgStr, methInfo.routeFuncName, `)`)
  1035. }
  1036. t.P(` }`)
  1037. }
  1038. // 新的注册路由的方法
  1039. var bmFuncName = fmt.Sprintf("Register%sBMServer", servName)
  1040. t.P(`// `, bmFuncName, ` Register the blademaster route`)
  1041. t.P(`func `, bmFuncName, `(e *bm.Engine, server `, servName, `BMServer) {`)
  1042. t.P(svcName, ` = server`)
  1043. for _, methInfo := range methList {
  1044. t.P(`e.`, methInfo.httpMethod, `("`, methInfo.path, `",`, methInfo.routeFuncName, ` )`)
  1045. }
  1046. t.P(` }`)
  1047. }
  1048. func (t *bm) generateBMInterface(file *descriptor.FileDescriptorProto, service *descriptor.ServiceDescriptorProto) {
  1049. servName := serviceName(service)
  1050. comments, err := t.reg.ServiceComments(file, service)
  1051. if err == nil {
  1052. t.printComments(comments)
  1053. }
  1054. t.P(`type `, servName, `BMServer interface {`)
  1055. for _, method := range service.Method {
  1056. t.generateSignature(file, service, method, comments)
  1057. t.P()
  1058. }
  1059. t.P(`}`)
  1060. }
  1061. // pb包的别名
  1062. // 用户生成service实现模板时,对pb文件的引用
  1063. // 如果是v*的package 则为v*pb
  1064. // 其他为pb
  1065. func (t *bm) getPkgAlias() string {
  1066. if t.genPkgName == "" {
  1067. return "pb"
  1068. }
  1069. if t.genPkgName[:1] == "v" {
  1070. return t.genPkgName + "pb"
  1071. }
  1072. return "pb"
  1073. }
  1074. // 如果是v*开始的 返回v*
  1075. // 否则返回空
  1076. func (t *bm) getVersionPrefix() string {
  1077. if t.genPkgName == "" {
  1078. return ""
  1079. }
  1080. if t.genPkgName[:1] == "v" {
  1081. return t.genPkgName
  1082. }
  1083. return ""
  1084. }
  1085. func (t *bm) generateBmImpl(file *descriptor.FileDescriptorProto, service *descriptor.ServiceDescriptorProto,
  1086. existMap map[string]bool) {
  1087. var pkgName = t.getPkgAlias()
  1088. svcName := serviceName(service) + "Service"
  1089. for _, method := range service.Method {
  1090. methName := methodName(method)
  1091. if existMap[methName] {
  1092. continue
  1093. }
  1094. comments, err := t.reg.MethodComments(file, service, method)
  1095. tags := getTagsInComment(comments.Leading)
  1096. respDynamic := getTagValue("dynamic_resp", tags) == "true"
  1097. genImp := func(dynamicRet bool) {
  1098. t.P(`// `, methName, " implementation")
  1099. if err == nil {
  1100. t.printComments(comments)
  1101. }
  1102. outputType := t.goTypeName(method.GetOutputType())
  1103. inputType := t.goTypeName(method.GetInputType())
  1104. var body string
  1105. var ownPkg = t.isOwnPackage(method.GetOutputType())
  1106. var respType string
  1107. if ownPkg {
  1108. respType = pkgName + "." + outputType
  1109. } else {
  1110. respType = outputType
  1111. }
  1112. if dynamicRet {
  1113. body = fmt.Sprintf(`func (s *%s) %s(ctx context.Context, req *%s.%s) (resp interface{}, err error) {`,
  1114. svcName, methName, pkgName, inputType)
  1115. } else {
  1116. body = fmt.Sprintf(`func (s *%s) %s(ctx context.Context, req *%s.%s) (resp *%s, err error) {`,
  1117. svcName, methName, pkgName, inputType, respType)
  1118. }
  1119. t.P(body)
  1120. t.P(fmt.Sprintf("resp = &%s{}", respType))
  1121. t.P(` return`)
  1122. t.P(`}`)
  1123. t.P()
  1124. }
  1125. genImp(respDynamic)
  1126. }
  1127. }
  1128. func (t *bm) generateSignature(file *descriptor.FileDescriptorProto,
  1129. service *descriptor.ServiceDescriptorProto,
  1130. method *descriptor.MethodDescriptorProto,
  1131. comments typemap.DefinitionComments) {
  1132. comments, err := t.reg.MethodComments(file, service, method)
  1133. methName := methodName(method)
  1134. outputType := t.goTypeName(method.GetOutputType())
  1135. inputType := t.goTypeName(method.GetInputType())
  1136. tags := getTagsInComment(comments.Leading)
  1137. if getTagValue("dynamic", tags) == "true" {
  1138. return
  1139. }
  1140. if err == nil {
  1141. t.printComments(comments)
  1142. }
  1143. respDynamic := getTagValue("dynamic_resp", tags) == "true"
  1144. if respDynamic {
  1145. t.P(fmt.Sprintf(` %s(ctx context.Context, req *%s) (resp interface{}, err error)`,
  1146. methName, inputType))
  1147. } else {
  1148. t.P(fmt.Sprintf(` %s(ctx context.Context, req *%s) (resp *%s, err error)`,
  1149. methName, inputType, outputType))
  1150. }
  1151. }
  1152. func (t *bm) generateFileDescriptor(file *descriptor.FileDescriptorProto) {
  1153. // Copied straight of of protoc-gen-go, which trims out comments.
  1154. pb := proto.Clone(file).(*descriptor.FileDescriptorProto)
  1155. pb.SourceCodeInfo = nil
  1156. b, err := proto.Marshal(pb)
  1157. if err != nil {
  1158. gen.Fail(err.Error())
  1159. }
  1160. var buf bytes.Buffer
  1161. w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
  1162. w.Write(b)
  1163. w.Close()
  1164. buf.Bytes()
  1165. }
  1166. func (t *bm) printComments(comments typemap.DefinitionComments) bool {
  1167. text := strings.TrimSuffix(comments.Leading, "\n")
  1168. if len(strings.TrimSpace(text)) == 0 {
  1169. return false
  1170. }
  1171. split := strings.Split(text, "\n")
  1172. for _, line := range split {
  1173. t.P("// ", strings.TrimPrefix(line, " "))
  1174. }
  1175. return len(split) > 0
  1176. }
  1177. // Given a protobuf name for a Message, return the Go name we will use for that
  1178. // type, including its package prefix.
  1179. func (t *bm) goTypeName(protoName string) string {
  1180. def := t.reg.MessageDefinition(protoName)
  1181. if def == nil {
  1182. gen.Fail("could not find message for", protoName)
  1183. }
  1184. var prefix string
  1185. if pkg := t.goPackageName(def.File); pkg != t.genPkgName {
  1186. prefix = pkg + "."
  1187. }
  1188. var name string
  1189. for _, parent := range def.Lineage() {
  1190. name += parent.Descriptor.GetName() + "_"
  1191. }
  1192. name += def.Descriptor.GetName()
  1193. return prefix + name
  1194. }
  1195. func (t *bm) isOwnPackage(protoName string) bool {
  1196. def := t.reg.MessageDefinition(protoName)
  1197. if def == nil {
  1198. gen.Fail("could not find message for", protoName)
  1199. }
  1200. pkg := t.goPackageName(def.File)
  1201. return pkg == t.genPkgName
  1202. }
  1203. func (t *bm) goPackageName(file *descriptor.FileDescriptorProto) string {
  1204. return t.fileToGoPackageName[file]
  1205. }
  1206. func (t *bm) formattedOutput() string {
  1207. // Reformat generated code.
  1208. fset := token.NewFileSet()
  1209. raw := t.output.Bytes()
  1210. ast, err := parser.ParseFile(fset, "", raw, parser.ParseComments)
  1211. if err != nil {
  1212. // Print out the bad code with line numbers.
  1213. // This should never happen in practice, but it can while changing generated code,
  1214. // so consider this a debugging aid.
  1215. var src bytes.Buffer
  1216. s := bufio.NewScanner(bytes.NewReader(raw))
  1217. for line := 1; s.Scan(); line++ {
  1218. fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes())
  1219. }
  1220. gen.Fail("bad Go source code was generated:", err.Error(), "\n"+src.String())
  1221. }
  1222. out := bytes.NewBuffer(nil)
  1223. err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(out, fset, ast)
  1224. if err != nil {
  1225. gen.Fail("generated Go source code could not be reformatted:", err.Error())
  1226. }
  1227. return out.String()
  1228. }
  1229. func serviceName(service *descriptor.ServiceDescriptorProto) string {
  1230. return stringutils.CamelCase(service.GetName())
  1231. }
  1232. func methodName(method *descriptor.MethodDescriptorProto) string {
  1233. return stringutils.CamelCase(method.GetName())
  1234. }
  1235. func fileDescSliceContains(slice []*descriptor.FileDescriptorProto, f *descriptor.FileDescriptorProto) bool {
  1236. for _, sf := range slice {
  1237. if f == sf {
  1238. return true
  1239. }
  1240. }
  1241. return false
  1242. }
  1243. // deduceGenPkgName figures out the go package name to use for generated code.
  1244. // Will try to use the explicit go_package setting in a file (if set, must be
  1245. // consistent in all files). If no files have go_package set, then use the
  1246. // protobuf package name (must be consistent in all files)
  1247. func deduceGenPkgName(genFiles []*descriptor.FileDescriptorProto) (string, error) {
  1248. var genPkgName string
  1249. for _, f := range genFiles {
  1250. name, explicit := goPackageName(f)
  1251. if explicit {
  1252. name = stringutils.CleanIdentifier(name)
  1253. if genPkgName != "" && genPkgName != name {
  1254. // Make sure they're all set consistently.
  1255. return "", errors.Errorf("files have conflicting go_package settings, must be the same: %q and %q", genPkgName, name)
  1256. }
  1257. genPkgName = name
  1258. }
  1259. }
  1260. if genPkgName != "" {
  1261. return genPkgName, nil
  1262. }
  1263. // If there is no explicit setting, then check the implicit package name
  1264. // (derived from the protobuf package name) of the files and make sure it's
  1265. // consistent.
  1266. for _, f := range genFiles {
  1267. name, _ := goPackageName(f)
  1268. name = stringutils.CleanIdentifier(name)
  1269. if genPkgName != "" && genPkgName != name {
  1270. return "", errors.Errorf("files have conflicting package names, must be the same or overridden with go_package: %q and %q", genPkgName, name)
  1271. }
  1272. genPkgName = name
  1273. }
  1274. // All the files have the same name, so we're good.
  1275. return genPkgName, nil
  1276. }