proto.go 6.5 KB

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