format.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package format
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "go/format"
  7. "go/scanner"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "github.com/tal-tech/go-zero/core/errorx"
  15. "github.com/tal-tech/go-zero/tools/goctl/api/parser"
  16. "github.com/tal-tech/go-zero/tools/goctl/api/util"
  17. "github.com/urfave/cli"
  18. )
  19. var (
  20. reg = regexp.MustCompile("type (?P<name>.*)[\\s]+{")
  21. )
  22. func GoFormatApi(c *cli.Context) error {
  23. useStdin := c.Bool("stdin")
  24. var be errorx.BatchError
  25. if useStdin {
  26. if err := ApiFormatByStdin(); err != nil {
  27. be.Add(err)
  28. }
  29. } else {
  30. dir := c.String("dir")
  31. if len(dir) == 0 {
  32. return errors.New("missing -dir")
  33. }
  34. _, err := os.Lstat(dir)
  35. if err != nil {
  36. return errors.New(dir + ": No such file or directory")
  37. }
  38. err = filepath.Walk(dir, func(path string, fi os.FileInfo, errBack error) (err error) {
  39. if strings.HasSuffix(path, ".api") {
  40. if err := ApiFormatByPath(path); err != nil {
  41. be.Add(util.WrapErr(err, fi.Name()))
  42. }
  43. }
  44. return nil
  45. })
  46. be.Add(err)
  47. }
  48. if be.NotNil() {
  49. scanner.PrintError(os.Stderr, be.Err())
  50. os.Exit(1)
  51. }
  52. return be.Err()
  53. }
  54. func ApiFormatByStdin() error {
  55. data, err := ioutil.ReadAll(os.Stdin)
  56. if err != nil {
  57. return err
  58. }
  59. result, err := apiFormat(string(data))
  60. if err != nil {
  61. return err
  62. }
  63. _, err = fmt.Print(result)
  64. if err != nil {
  65. return err
  66. }
  67. return nil
  68. }
  69. func ApiFormatByPath(apiFilePath string) error {
  70. data, err := ioutil.ReadFile(apiFilePath)
  71. if err != nil {
  72. return err
  73. }
  74. result, err := apiFormat(string(data))
  75. if err != nil {
  76. return err
  77. }
  78. if err := ioutil.WriteFile(apiFilePath, []byte(result), os.ModePerm); err != nil {
  79. return err
  80. }
  81. return nil
  82. }
  83. func apiFormat(data string) (string, error) {
  84. r := reg.ReplaceAllStringFunc(data, func(m string) string {
  85. parts := reg.FindStringSubmatch(m)
  86. if len(parts) < 2 {
  87. return m
  88. }
  89. if !strings.Contains(m, "struct") {
  90. return "type " + parts[1] + " struct {"
  91. }
  92. return m
  93. })
  94. apiStruct, err := parser.ParseApi(r)
  95. if err != nil {
  96. return "", err
  97. }
  98. info := strings.TrimSpace(apiStruct.Info)
  99. if len(apiStruct.Service) == 0 {
  100. return data, nil
  101. }
  102. fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.StructBody)))
  103. if err != nil {
  104. str := err.Error()
  105. lineNumber := strings.Index(str, ":")
  106. if lineNumber > 0 {
  107. ln, err := strconv.ParseInt(str[:lineNumber], 10, 64)
  108. if err != nil {
  109. return "", err
  110. }
  111. pn := 0
  112. if len(info) > 0 {
  113. pn = countRune(info, '\n') + 1
  114. }
  115. number := int(ln) + pn + 1
  116. return "", errors.New(fmt.Sprintf("line: %d, %s", number, str[lineNumber+1:]))
  117. }
  118. return "", err
  119. }
  120. var result string
  121. if len(strings.TrimSpace(info)) > 0 {
  122. result += strings.TrimSpace(info) + "\n\n"
  123. }
  124. if len(strings.TrimSpace(apiStruct.Imports)) > 0 {
  125. result += strings.TrimSpace(apiStruct.Imports) + "\n\n"
  126. }
  127. if len(strings.TrimSpace(string(fs))) > 0 {
  128. result += strings.TrimSpace(string(fs)) + "\n\n"
  129. }
  130. if len(strings.TrimSpace(apiStruct.Service)) > 0 {
  131. result += formatService(apiStruct.Service) + "\n\n"
  132. }
  133. return strings.TrimSpace(result), nil
  134. }
  135. func formatService(str string) string {
  136. var builder strings.Builder
  137. scanner := bufio.NewScanner(strings.NewReader(str))
  138. var tapCount = 0
  139. for scanner.Scan() {
  140. line := strings.TrimSpace(scanner.Text())
  141. if line == ")" || line == "}" {
  142. tapCount -= 1
  143. }
  144. util.WriteIndent(&builder, tapCount)
  145. builder.WriteString(line + "\n")
  146. if strings.HasSuffix(line, "(") || strings.HasSuffix(line, "{") {
  147. tapCount += 1
  148. }
  149. }
  150. return strings.TrimSpace(builder.String())
  151. }
  152. func countRune(s string, r rune) int {
  153. count := 0
  154. for _, c := range s {
  155. if c == r {
  156. count++
  157. }
  158. }
  159. return count
  160. }