parser.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package parser
  2. import (
  3. "go/token"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "unicode"
  8. "unicode/utf8"
  9. "github.com/emicklei/proto"
  10. )
  11. type (
  12. // DefaultProtoParser types an empty struct
  13. DefaultProtoParser struct{}
  14. )
  15. // NewDefaultProtoParser creates a new instance
  16. func NewDefaultProtoParser() *DefaultProtoParser {
  17. return &DefaultProtoParser{}
  18. }
  19. // Parse provides to parse the proto file into a golang structure,
  20. // which is convenient for subsequent rpc generation and use
  21. func (p *DefaultProtoParser) Parse(src string, multiple ...bool) (Proto, error) {
  22. var ret Proto
  23. abs, err := filepath.Abs(src)
  24. if err != nil {
  25. return Proto{}, err
  26. }
  27. r, err := os.Open(abs)
  28. if err != nil {
  29. return ret, err
  30. }
  31. defer r.Close()
  32. parser := proto.NewParser(r)
  33. set, err := parser.Parse()
  34. if err != nil {
  35. return ret, err
  36. }
  37. var serviceList Services
  38. proto.Walk(
  39. set,
  40. proto.WithImport(func(i *proto.Import) {
  41. ret.Import = append(ret.Import, Import{Import: i})
  42. }),
  43. proto.WithMessage(func(message *proto.Message) {
  44. ret.Message = append(ret.Message, Message{Message: message})
  45. }),
  46. proto.WithPackage(func(p *proto.Package) {
  47. ret.Package = Package{Package: p}
  48. }),
  49. proto.WithService(func(service *proto.Service) {
  50. serv := Service{Service: service}
  51. elements := service.Elements
  52. for _, el := range elements {
  53. v, _ := el.(*proto.RPC)
  54. if v == nil {
  55. continue
  56. }
  57. serv.RPC = append(serv.RPC, &RPC{RPC: v})
  58. }
  59. serviceList = append(serviceList, serv)
  60. }),
  61. proto.WithOption(func(option *proto.Option) {
  62. if option.Name == "go_package" {
  63. ret.GoPackage = option.Constant.Source
  64. }
  65. }),
  66. )
  67. if err = serviceList.validate(abs, multiple...); err != nil {
  68. return ret, err
  69. }
  70. if len(ret.GoPackage) == 0 {
  71. ret.GoPackage = ret.Package.Name
  72. }
  73. ret.PbPackage = GoSanitized(filepath.Base(ret.GoPackage))
  74. ret.Src = abs
  75. ret.Name = filepath.Base(abs)
  76. ret.Service = serviceList
  77. return ret, nil
  78. }
  79. // GoSanitized copy from protobuf, for more information, please see google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71
  80. func GoSanitized(s string) string {
  81. // Sanitize the input to the set of valid characters,
  82. // which must be '_' or be in the Unicode L or N categories.
  83. s = strings.Map(func(r rune) rune {
  84. if unicode.IsLetter(r) || unicode.IsDigit(r) {
  85. return r
  86. }
  87. return '_'
  88. }, s)
  89. // Prepend '_' in the event of a Go keyword conflict or if
  90. // the identifier is invalid (does not start in the Unicode L category).
  91. r, _ := utf8.DecodeRuneInString(s)
  92. if token.Lookup(s).IsKeyword() || !unicode.IsLetter(r) {
  93. return "_" + s
  94. }
  95. return s
  96. }
  97. // CamelCase copy from protobuf, for more information, please see github.com/golang/protobuf@v1.4.2/protoc-gen-go/generator/generator.go:2648
  98. func CamelCase(s string) string {
  99. if s == "" {
  100. return ""
  101. }
  102. t := make([]byte, 0, 32)
  103. i := 0
  104. if s[0] == '_' {
  105. // Need a capital letter; drop the '_'.
  106. t = append(t, 'X')
  107. i++
  108. }
  109. // Invariant: if the next letter is lower case, it must be converted
  110. // to upper case.
  111. // That is, we process a word at a time, where words are marked by _ or
  112. // upper case letter. Digits are treated as words.
  113. for ; i < len(s); i++ {
  114. c := s[i]
  115. if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
  116. continue // Skip the underscore in s.
  117. }
  118. if isASCIIDigit(c) {
  119. t = append(t, c)
  120. continue
  121. }
  122. // Assume we have a letter now - if not, it's a bogus identifier.
  123. // The next word is a sequence of characters that must start upper case.
  124. if isASCIILower(c) {
  125. c ^= ' ' // Make it a capital letter.
  126. }
  127. t = append(t, c) // Guaranteed not lower case.
  128. // Accept lower case sequence that follows.
  129. for i+1 < len(s) && isASCIILower(s[i+1]) {
  130. i++
  131. t = append(t, s[i])
  132. }
  133. }
  134. return string(t)
  135. }
  136. func isASCIILower(c byte) bool {
  137. return 'a' <= c && c <= 'z'
  138. }
  139. // Is c an ASCII digit?
  140. func isASCIIDigit(c byte) bool {
  141. return '0' <= c && c <= '9'
  142. }