format.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package errors
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. )
  8. type formatInfo struct {
  9. code int
  10. message string
  11. err string
  12. stack *stack
  13. }
  14. func (w *withCode) Format(state fmt.State, verb rune) {
  15. switch verb {
  16. case 'v':
  17. str := bytes.NewBuffer([]byte{})
  18. jsonData := []map[string]interface{}{}
  19. var (
  20. flagDetail bool
  21. flagTrace bool
  22. modeJSON bool
  23. )
  24. if state.Flag('#') {
  25. modeJSON = true
  26. }
  27. if state.Flag('-') {
  28. flagDetail = true
  29. }
  30. if state.Flag('+') {
  31. flagTrace = true
  32. }
  33. sep := ""
  34. errs := list(w)
  35. length := len(errs)
  36. for k, e := range errs {
  37. finfo := buildFormatInfo(e)
  38. jsonData, str = format(length-k-1, jsonData, str, finfo, sep, flagDetail, flagTrace, modeJSON)
  39. sep = "; "
  40. if !flagTrace {
  41. break
  42. }
  43. if !flagDetail && !flagTrace && !modeJSON {
  44. break
  45. }
  46. }
  47. if modeJSON {
  48. var byts []byte
  49. byts, _ = json.Marshal(jsonData)
  50. str.Write(byts)
  51. }
  52. fmt.Fprintf(state, "%s", strings.Trim(str.String(), "\r\n\t"))
  53. default:
  54. finfo := buildFormatInfo(w)
  55. // Externally-safe error message
  56. fmt.Fprintf(state, finfo.message)
  57. }
  58. }
  59. func format(k int, jsonData []map[string]interface{}, str *bytes.Buffer, finfo *formatInfo,
  60. sep string, flagDetail, flagTrace, modeJSON bool) ([]map[string]interface{}, *bytes.Buffer) {
  61. if modeJSON {
  62. data := map[string]interface{}{}
  63. if flagDetail || flagTrace {
  64. data = map[string]interface{}{
  65. "message": finfo.message,
  66. "code": finfo.code,
  67. "error": finfo.err,
  68. }
  69. caller := fmt.Sprintf("#%d", k)
  70. if finfo.stack != nil {
  71. f := Frame((*finfo.stack)[0])
  72. caller = fmt.Sprintf("%s %s:%d (%s)",
  73. caller,
  74. f.file(),
  75. f.line(),
  76. f.name(),
  77. )
  78. }
  79. data["caller"] = caller
  80. } else {
  81. data["error"] = finfo.message
  82. }
  83. jsonData = append(jsonData, data)
  84. } else {
  85. if flagDetail || flagTrace {
  86. if finfo.stack != nil {
  87. f := Frame((*finfo.stack)[0])
  88. fmt.Fprintf(str, "%s%s - #%d [%s:%d (%s)] (%d) %s",
  89. sep,
  90. finfo.err,
  91. k,
  92. f.file(),
  93. f.line(),
  94. f.name(),
  95. finfo.code,
  96. finfo.message,
  97. )
  98. } else {
  99. fmt.Fprintf(str, "%s%s - #%d %s", sep, finfo.err, k, finfo.message)
  100. }
  101. } else {
  102. fmt.Fprintf(str, finfo.message)
  103. }
  104. }
  105. return jsonData, str
  106. }
  107. func list(e error) []error {
  108. var ret []error
  109. if e != nil {
  110. if w, ok := e.(interface{ Unwrap() error }); ok {
  111. ret = append(ret, e)
  112. ret = append(ret, list(w.Unwrap())...)
  113. } else {
  114. ret = append(ret, e)
  115. }
  116. }
  117. return ret
  118. }
  119. func buildFormatInfo(e error) *formatInfo {
  120. var finfo *formatInfo
  121. err, ok := e.(*withCode)
  122. if !ok {
  123. return &formatInfo{
  124. code: unknownCoder.Code(),
  125. message: err.Error(),
  126. err: err.Error(),
  127. }
  128. }
  129. coder, ok := codes[err.code]
  130. if !ok {
  131. coder = unknownCoder
  132. }
  133. extMsg := coder.String()
  134. if extMsg == "" {
  135. extMsg = err.err.Error()
  136. }
  137. finfo = &formatInfo{
  138. code: coder.Code(),
  139. message: extMsg,
  140. err: err.err.Error(),
  141. stack: err.stack,
  142. }
  143. return finfo
  144. }