proto.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. package parser
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "github.com/emicklei/proto"
  9. "github.com/tal-tech/go-zero/core/collection"
  10. "github.com/tal-tech/go-zero/core/lang"
  11. "github.com/tal-tech/go-zero/tools/goctl/templatex"
  12. "github.com/tal-tech/go-zero/tools/goctl/util"
  13. "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
  14. )
  15. const (
  16. AnyImport = "google/protobuf/any.proto"
  17. )
  18. var (
  19. enumTypeTemplate = `{{.name}} int32`
  20. enumTemplate = `const (
  21. {{.element}}
  22. )`
  23. enumFiledTemplate = `{{.key}} {{.name}} = {{.value}}`
  24. )
  25. type (
  26. MessageField struct {
  27. Type string
  28. Name stringx.String
  29. }
  30. Message struct {
  31. Name stringx.String
  32. Element []*MessageField
  33. *proto.Message
  34. }
  35. Enum struct {
  36. Name stringx.String
  37. Element []*EnumField
  38. *proto.Enum
  39. }
  40. EnumField struct {
  41. Key string
  42. Value int
  43. }
  44. Proto struct {
  45. Package string
  46. Import []*Import
  47. PbSrc string
  48. ContainsAny bool
  49. Message map[string]lang.PlaceholderType
  50. Enum map[string]*Enum
  51. }
  52. Import struct {
  53. ProtoImportName string
  54. PbImportName string
  55. OriginalDir string
  56. OriginalProtoPath string
  57. OriginalPbPath string
  58. BridgeImport string
  59. exists bool
  60. //xx.proto
  61. protoName string
  62. // xx.pb.go
  63. pbName string
  64. }
  65. )
  66. func checkImport(src string) error {
  67. r, err := os.Open(src)
  68. if err != nil {
  69. return err
  70. }
  71. defer r.Close()
  72. parser := proto.NewParser(r)
  73. parseRet, err := parser.Parse()
  74. if err != nil {
  75. return err
  76. }
  77. var base = filepath.Base(src)
  78. proto.Walk(parseRet, proto.WithImport(func(i *proto.Import) {
  79. if err != nil {
  80. return
  81. }
  82. err = fmt.Errorf("%v:%v the external proto cannot import other proto files", base, i.Position.Line)
  83. }))
  84. if err != nil {
  85. return err
  86. }
  87. return nil
  88. }
  89. func ParseImport(src string) ([]*Import, bool, error) {
  90. bridgeImportM := make(map[string]string)
  91. r, err := os.Open(src)
  92. if err != nil {
  93. return nil, false, err
  94. }
  95. defer r.Close()
  96. workDir := filepath.Dir(src)
  97. parser := proto.NewParser(r)
  98. parseRet, err := parser.Parse()
  99. if err != nil {
  100. return nil, false, err
  101. }
  102. protoImportSet := collection.NewSet()
  103. var containsAny bool
  104. proto.Walk(parseRet, proto.WithImport(func(i *proto.Import) {
  105. if i.Filename == AnyImport {
  106. containsAny = true
  107. return
  108. }
  109. protoImportSet.AddStr(i.Filename)
  110. if i.Comment != nil {
  111. lines := i.Comment.Lines
  112. for _, line := range lines {
  113. line = strings.TrimSpace(line)
  114. if !strings.HasPrefix(line, "@") {
  115. continue
  116. }
  117. line = strings.TrimPrefix(line, "@")
  118. bridgeImportM[i.Filename] = line
  119. }
  120. }
  121. }))
  122. var importList []*Import
  123. for _, item := range protoImportSet.KeysStr() {
  124. pb := strings.TrimSuffix(filepath.Base(item), filepath.Ext(item)) + ".pb.go"
  125. var pbImportName, brideImport string
  126. if v, ok := bridgeImportM[item]; ok {
  127. pbImportName = v
  128. brideImport = "M" + item + "=" + v
  129. } else {
  130. pbImportName = item
  131. }
  132. var impo = Import{
  133. ProtoImportName: item,
  134. PbImportName: pbImportName,
  135. BridgeImport: brideImport,
  136. }
  137. protoSource := filepath.Join(workDir, item)
  138. pbSource := filepath.Join(filepath.Dir(protoSource), pb)
  139. if util.FileExists(protoSource) && util.FileExists(pbSource) {
  140. impo.OriginalProtoPath = protoSource
  141. impo.OriginalPbPath = pbSource
  142. impo.OriginalDir = filepath.Dir(protoSource)
  143. impo.exists = true
  144. impo.protoName = filepath.Base(item)
  145. impo.pbName = pb
  146. } else {
  147. return nil, false, fmt.Errorf("「%v」: import must be found in the relative directory of 「%v」", item, filepath.Base(src))
  148. }
  149. importList = append(importList, &impo)
  150. }
  151. return importList, containsAny, nil
  152. }
  153. func parseProto(src string, messageM map[string]lang.PlaceholderType, enumM map[string]*Enum) (*Proto, error) {
  154. if !filepath.IsAbs(src) {
  155. return nil, fmt.Errorf("expected absolute path,but found: %v", src)
  156. }
  157. r, err := os.Open(src)
  158. if err != nil {
  159. return nil, err
  160. }
  161. defer r.Close()
  162. parser := proto.NewParser(r)
  163. parseRet, err := parser.Parse()
  164. if err != nil {
  165. return nil, err
  166. }
  167. // xx.proto
  168. fileBase := filepath.Base(src)
  169. var resp Proto
  170. proto.Walk(parseRet, proto.WithPackage(func(p *proto.Package) {
  171. if err != nil {
  172. return
  173. }
  174. if len(resp.Package) != 0 {
  175. err = fmt.Errorf("%v:%v duplicate package「%v」", fileBase, p.Position.Line, p.Name)
  176. }
  177. if len(p.Name) == 0 {
  178. err = errors.New("package not found")
  179. }
  180. resp.Package = p.Name
  181. }), proto.WithMessage(func(message *proto.Message) {
  182. if err != nil {
  183. return
  184. }
  185. for _, item := range message.Elements {
  186. switch item.(type) {
  187. case *proto.NormalField, *proto.MapField, *proto.Comment:
  188. continue
  189. default:
  190. err = fmt.Errorf("%v: unsupport inline declaration", fileBase)
  191. return
  192. }
  193. }
  194. name := stringx.From(message.Name)
  195. if _, ok := messageM[name.Lower()]; ok {
  196. err = fmt.Errorf("%v:%v duplicate message 「%v」", fileBase, message.Position.Line, message.Name)
  197. return
  198. }
  199. messageM[name.Lower()] = lang.Placeholder
  200. }), proto.WithEnum(func(enum *proto.Enum) {
  201. if err != nil {
  202. return
  203. }
  204. var node Enum
  205. node.Enum = enum
  206. node.Name = stringx.From(enum.Name)
  207. for _, item := range enum.Elements {
  208. v, ok := item.(*proto.EnumField)
  209. if !ok {
  210. continue
  211. }
  212. node.Element = append(node.Element, &EnumField{
  213. Key: v.Name,
  214. Value: v.Integer,
  215. })
  216. }
  217. if _, ok := enumM[node.Name.Lower()]; ok {
  218. err = fmt.Errorf("%v:%v duplicate enum 「%v」", fileBase, node.Position.Line, node.Name.Source())
  219. return
  220. }
  221. lower := stringx.From(enum.Name).Lower()
  222. enumM[lower] = &node
  223. }))
  224. if err != nil {
  225. return nil, err
  226. }
  227. resp.Message = messageM
  228. resp.Enum = enumM
  229. return &resp, nil
  230. }
  231. func (e *Enum) GenEnumCode() (string, error) {
  232. var element []string
  233. for _, item := range e.Element {
  234. code, err := item.GenEnumFieldCode(e.Name.Source())
  235. if err != nil {
  236. return "", err
  237. }
  238. element = append(element, code)
  239. }
  240. buffer, err := templatex.With("enum").Parse(enumTemplate).Execute(map[string]interface{}{
  241. "element": strings.Join(element, util.NL),
  242. })
  243. if err != nil {
  244. return "", err
  245. }
  246. return buffer.String(), nil
  247. }
  248. func (e *Enum) GenEnumTypeCode() (string, error) {
  249. buffer, err := templatex.With("enumAlias").Parse(enumTypeTemplate).Execute(map[string]interface{}{
  250. "name": e.Name.Source(),
  251. })
  252. if err != nil {
  253. return "", err
  254. }
  255. return buffer.String(), nil
  256. }
  257. func (e *EnumField) GenEnumFieldCode(parentName string) (string, error) {
  258. buffer, err := templatex.With("enumField").Parse(enumFiledTemplate).Execute(map[string]interface{}{
  259. "key": e.Key,
  260. "name": parentName,
  261. "value": e.Value,
  262. })
  263. if err != nil {
  264. return "", err
  265. }
  266. return buffer.String(), nil
  267. }