apifileparser.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package parser
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "strings"
  9. "github.com/tal-tech/go-zero/core/stringx"
  10. "github.com/tal-tech/go-zero/tools/goctl/api/util"
  11. )
  12. const (
  13. tokenInfo = "info"
  14. tokenImport = "import"
  15. tokenType = "type"
  16. tokenService = "service"
  17. tokenServiceAnnotation = "@server"
  18. tokenStruct = "struct"
  19. )
  20. type (
  21. ApiStruct struct {
  22. Info string
  23. Type string
  24. Service string
  25. Imports string
  26. serviceBeginLine int
  27. }
  28. apiFileState interface {
  29. process(api *ApiStruct, token string) (apiFileState, error)
  30. }
  31. apiRootState struct {
  32. *baseState
  33. }
  34. apiInfoState struct {
  35. *baseState
  36. }
  37. apiImportState struct {
  38. *baseState
  39. }
  40. apiTypeState struct {
  41. *baseState
  42. }
  43. apiServiceState struct {
  44. *baseState
  45. }
  46. )
  47. func ParseApi(src string) (*ApiStruct, error) {
  48. var buffer = new(bytes.Buffer)
  49. buffer.WriteString(src)
  50. api := new(ApiStruct)
  51. var lineNumber = api.serviceBeginLine
  52. apiFile := baseState{r: bufio.NewReader(buffer), lineNumber: &lineNumber}
  53. st := apiRootState{&apiFile}
  54. for {
  55. st, err := st.process(api, "")
  56. if err == io.EOF {
  57. return api, nil
  58. }
  59. if err != nil {
  60. return nil, fmt.Errorf("near line: %d, %s", lineNumber, err.Error())
  61. }
  62. if st == nil {
  63. return api, nil
  64. }
  65. }
  66. }
  67. func (s *apiRootState) process(api *ApiStruct, _ string) (apiFileState, error) {
  68. var builder strings.Builder
  69. for {
  70. ch, err := s.readSkipComment()
  71. if err != nil {
  72. return nil, err
  73. }
  74. switch {
  75. case isSpace(ch) || isNewline(ch) || ch == leftParenthesis:
  76. token := builder.String()
  77. token = strings.TrimSpace(token)
  78. if len(token) == 0 {
  79. continue
  80. }
  81. builder.Reset()
  82. switch token {
  83. case tokenInfo:
  84. info := apiInfoState{s.baseState}
  85. return info.process(api, token+string(ch))
  86. case tokenImport:
  87. tp := apiImportState{s.baseState}
  88. return tp.process(api, token+string(ch))
  89. case tokenType:
  90. ty := apiTypeState{s.baseState}
  91. return ty.process(api, token+string(ch))
  92. case tokenService:
  93. server := apiServiceState{s.baseState}
  94. return server.process(api, token+string(ch))
  95. case tokenServiceAnnotation:
  96. server := apiServiceState{s.baseState}
  97. return server.process(api, token+string(ch))
  98. default:
  99. if strings.HasPrefix(token, "//") {
  100. continue
  101. }
  102. return nil, errors.New(fmt.Sprintf("invalid token %s at line %d", token, *s.lineNumber))
  103. }
  104. default:
  105. builder.WriteRune(ch)
  106. }
  107. }
  108. }
  109. func (s *apiInfoState) process(api *ApiStruct, token string) (apiFileState, error) {
  110. for {
  111. line, err := s.readLine()
  112. if err != nil {
  113. return nil, err
  114. }
  115. api.Info += newline + token + line
  116. token = ""
  117. if strings.TrimSpace(line) == string(rightParenthesis) {
  118. return &apiRootState{s.baseState}, nil
  119. }
  120. }
  121. }
  122. func (s *apiImportState) process(api *ApiStruct, token string) (apiFileState, error) {
  123. line, err := s.readLine()
  124. if err != nil {
  125. return nil, err
  126. }
  127. line = token + line
  128. line = util.RemoveComment(line)
  129. if len(strings.Fields(line)) != 2 {
  130. return nil, errors.New("import syntax error: " + line)
  131. }
  132. api.Imports += newline + line
  133. return &apiRootState{s.baseState}, nil
  134. }
  135. func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) {
  136. var blockCount = 0
  137. for {
  138. line, err := s.readLine()
  139. if err != nil {
  140. return nil, err
  141. }
  142. line = token + line
  143. if blockCount <= 1 {
  144. line = mayInsertStructKeyword(line)
  145. }
  146. api.Type += newline + newline + line
  147. line = strings.TrimSpace(line)
  148. line = util.RemoveComment(line)
  149. token = ""
  150. if strings.HasSuffix(line, leftBrace) {
  151. blockCount++
  152. }
  153. if strings.HasSuffix(line, string(leftParenthesis)) {
  154. blockCount++
  155. }
  156. if strings.HasSuffix(line, string(rightBrace)) {
  157. blockCount--
  158. }
  159. if strings.HasSuffix(line, string(rightParenthesis)) {
  160. blockCount--
  161. }
  162. if blockCount == 0 {
  163. return &apiRootState{s.baseState}, nil
  164. }
  165. }
  166. }
  167. func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, error) {
  168. var blockCount = 0
  169. for {
  170. line, err := s.readLineSkipComment()
  171. if err != nil {
  172. return nil, err
  173. }
  174. line = token + line
  175. token = ""
  176. api.Service += newline + line
  177. line = strings.TrimSpace(line)
  178. line = util.RemoveComment(line)
  179. if strings.HasSuffix(line, leftBrace) {
  180. blockCount++
  181. }
  182. if strings.HasSuffix(line, string(leftParenthesis)) {
  183. blockCount++
  184. }
  185. if line == string(rightBrace) {
  186. blockCount--
  187. }
  188. if line == string(rightParenthesis) {
  189. blockCount--
  190. }
  191. if blockCount == 0 {
  192. return &apiRootState{s.baseState}, nil
  193. }
  194. }
  195. }
  196. func mayInsertStructKeyword(line string) string {
  197. line = util.RemoveComment(line)
  198. if !strings.HasSuffix(line, leftBrace) {
  199. return line
  200. }
  201. fields := strings.Fields(line)
  202. if stringx.Contains(fields, tokenStruct) || stringx.Contains(fields, tokenStruct+leftBrace) || len(fields) <= 1 {
  203. return line
  204. }
  205. var insertIndex int
  206. if fields[0] == tokenType {
  207. insertIndex = 2
  208. } else {
  209. insertIndex = 1
  210. }
  211. if insertIndex >= len(fields) {
  212. return line
  213. }
  214. var result []string
  215. result = append(result, fields[:insertIndex]...)
  216. result = append(result, tokenStruct)
  217. result = append(result, fields[insertIndex:]...)
  218. return strings.Join(result, " ")
  219. }