gutil_dump.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package gutil
  7. import (
  8. "bytes"
  9. "fmt"
  10. "io"
  11. "reflect"
  12. "strings"
  13. "github.com/gogf/gf/v2/internal/reflection"
  14. "github.com/gogf/gf/v2/os/gstructs"
  15. "github.com/gogf/gf/v2/text/gstr"
  16. )
  17. // iString is used for type assert api for String().
  18. type iString interface {
  19. String() string
  20. }
  21. // iError is used for type assert api for Error().
  22. type iError interface {
  23. Error() string
  24. }
  25. // iMarshalJSON is the interface for custom Json marshaling.
  26. type iMarshalJSON interface {
  27. MarshalJSON() ([]byte, error)
  28. }
  29. // DumpOption specifies the behavior of function Export.
  30. type DumpOption struct {
  31. WithType bool // WithType specifies dumping content with type information.
  32. ExportedOnly bool // Only dump Exported fields for structs.
  33. }
  34. // Dump prints variables `values` to stdout with more manually readable.
  35. func Dump(values ...interface{}) {
  36. for _, value := range values {
  37. DumpWithOption(value, DumpOption{
  38. WithType: false,
  39. ExportedOnly: false,
  40. })
  41. }
  42. }
  43. // DumpWithType acts like Dump, but with type information.
  44. // Also see Dump.
  45. func DumpWithType(values ...interface{}) {
  46. for _, value := range values {
  47. DumpWithOption(value, DumpOption{
  48. WithType: true,
  49. ExportedOnly: false,
  50. })
  51. }
  52. }
  53. // DumpWithOption returns variables `values` as a string with more manually readable.
  54. func DumpWithOption(value interface{}, option DumpOption) {
  55. buffer := bytes.NewBuffer(nil)
  56. DumpTo(buffer, value, DumpOption{
  57. WithType: option.WithType,
  58. ExportedOnly: option.ExportedOnly,
  59. })
  60. fmt.Println(buffer.String())
  61. }
  62. // DumpTo writes variables `values` as a string in to `writer` with more manually readable
  63. func DumpTo(writer io.Writer, value interface{}, option DumpOption) {
  64. buffer := bytes.NewBuffer(nil)
  65. doDump(value, "", buffer, doDumpOption{
  66. WithType: option.WithType,
  67. ExportedOnly: option.ExportedOnly,
  68. })
  69. _, _ = writer.Write(buffer.Bytes())
  70. }
  71. type doDumpOption struct {
  72. WithType bool
  73. ExportedOnly bool
  74. }
  75. func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDumpOption) {
  76. if value == nil {
  77. buffer.WriteString(`<nil>`)
  78. return
  79. }
  80. var reflectValue reflect.Value
  81. if v, ok := value.(reflect.Value); ok {
  82. reflectValue = v
  83. if v.IsValid() && v.CanInterface() {
  84. value = v.Interface()
  85. } else {
  86. if convertedValue, ok := reflection.ValueToInterface(v); ok {
  87. value = convertedValue
  88. }
  89. }
  90. } else {
  91. reflectValue = reflect.ValueOf(value)
  92. }
  93. // Double check nil value.
  94. if value == nil {
  95. buffer.WriteString(`<nil>`)
  96. return
  97. }
  98. var (
  99. reflectKind = reflectValue.Kind()
  100. reflectTypeName = reflectValue.Type().String()
  101. newIndent = indent + dumpIndent
  102. )
  103. reflectTypeName = strings.ReplaceAll(reflectTypeName, `[]uint8`, `[]byte`)
  104. if !option.WithType {
  105. reflectTypeName = ""
  106. }
  107. for reflectKind == reflect.Ptr {
  108. reflectValue = reflectValue.Elem()
  109. reflectKind = reflectValue.Kind()
  110. }
  111. var (
  112. exportInternalInput = doDumpInternalInput{
  113. Value: value,
  114. Indent: indent,
  115. NewIndent: newIndent,
  116. Buffer: buffer,
  117. Option: option,
  118. ReflectValue: reflectValue,
  119. ReflectTypeName: reflectTypeName,
  120. ExportedOnly: option.ExportedOnly,
  121. }
  122. )
  123. switch reflectKind {
  124. case reflect.Slice, reflect.Array:
  125. doDumpSlice(exportInternalInput)
  126. case reflect.Map:
  127. doDumpMap(exportInternalInput)
  128. case reflect.Struct:
  129. doDumpStruct(exportInternalInput)
  130. case reflect.String:
  131. doDumpString(exportInternalInput)
  132. case reflect.Bool:
  133. doDumpBool(exportInternalInput)
  134. case
  135. reflect.Int,
  136. reflect.Int8,
  137. reflect.Int16,
  138. reflect.Int32,
  139. reflect.Int64,
  140. reflect.Uint,
  141. reflect.Uint8,
  142. reflect.Uint16,
  143. reflect.Uint32,
  144. reflect.Uint64,
  145. reflect.Float32,
  146. reflect.Float64,
  147. reflect.Complex64,
  148. reflect.Complex128:
  149. doDumpNumber(exportInternalInput)
  150. case reflect.Chan:
  151. buffer.WriteString(fmt.Sprintf(`<%s>`, reflectValue.Type().String()))
  152. case reflect.Func:
  153. if reflectValue.IsNil() || !reflectValue.IsValid() {
  154. buffer.WriteString(`<nil>`)
  155. } else {
  156. buffer.WriteString(fmt.Sprintf(`<%s>`, reflectValue.Type().String()))
  157. }
  158. case reflect.Interface:
  159. doDump(exportInternalInput.ReflectValue.Elem(), indent, buffer, option)
  160. default:
  161. doDumpDefault(exportInternalInput)
  162. }
  163. }
  164. type doDumpInternalInput struct {
  165. Value interface{}
  166. Indent string
  167. NewIndent string
  168. Buffer *bytes.Buffer
  169. Option doDumpOption
  170. ReflectValue reflect.Value
  171. ReflectTypeName string
  172. ExportedOnly bool
  173. }
  174. func doDumpSlice(in doDumpInternalInput) {
  175. if b, ok := in.Value.([]byte); ok {
  176. if !in.Option.WithType {
  177. in.Buffer.WriteString(fmt.Sprintf(`"%s"`, addSlashesForString(string(b))))
  178. } else {
  179. in.Buffer.WriteString(fmt.Sprintf(
  180. `%s(%d) "%s"`,
  181. in.ReflectTypeName,
  182. len(string(b)),
  183. string(b),
  184. ))
  185. }
  186. return
  187. }
  188. if in.ReflectValue.Len() == 0 {
  189. if !in.Option.WithType {
  190. in.Buffer.WriteString("[]")
  191. } else {
  192. in.Buffer.WriteString(fmt.Sprintf("%s(0) []", in.ReflectTypeName))
  193. }
  194. return
  195. }
  196. if !in.Option.WithType {
  197. in.Buffer.WriteString("[\n")
  198. } else {
  199. in.Buffer.WriteString(fmt.Sprintf("%s(%d) [\n", in.ReflectTypeName, in.ReflectValue.Len()))
  200. }
  201. for i := 0; i < in.ReflectValue.Len(); i++ {
  202. in.Buffer.WriteString(in.NewIndent)
  203. doDump(in.ReflectValue.Index(i), in.NewIndent, in.Buffer, in.Option)
  204. in.Buffer.WriteString(",\n")
  205. }
  206. in.Buffer.WriteString(fmt.Sprintf("%s]", in.Indent))
  207. }
  208. func doDumpMap(in doDumpInternalInput) {
  209. var mapKeys = make([]reflect.Value, 0)
  210. for _, key := range in.ReflectValue.MapKeys() {
  211. if !key.CanInterface() {
  212. continue
  213. }
  214. mapKey := key
  215. mapKeys = append(mapKeys, mapKey)
  216. }
  217. if len(mapKeys) == 0 {
  218. if !in.Option.WithType {
  219. in.Buffer.WriteString("{}")
  220. } else {
  221. in.Buffer.WriteString(fmt.Sprintf("%s(0) {}", in.ReflectTypeName))
  222. }
  223. return
  224. }
  225. var (
  226. maxSpaceNum = 0
  227. tmpSpaceNum = 0
  228. mapKeyStr = ""
  229. )
  230. for _, key := range mapKeys {
  231. tmpSpaceNum = len(fmt.Sprintf(`%v`, key.Interface()))
  232. if tmpSpaceNum > maxSpaceNum {
  233. maxSpaceNum = tmpSpaceNum
  234. }
  235. }
  236. if !in.Option.WithType {
  237. in.Buffer.WriteString("{\n")
  238. } else {
  239. in.Buffer.WriteString(fmt.Sprintf("%s(%d) {\n", in.ReflectTypeName, len(mapKeys)))
  240. }
  241. for _, mapKey := range mapKeys {
  242. tmpSpaceNum = len(fmt.Sprintf(`%v`, mapKey.Interface()))
  243. if mapKey.Kind() == reflect.String {
  244. mapKeyStr = fmt.Sprintf(`"%v"`, mapKey.Interface())
  245. } else {
  246. mapKeyStr = fmt.Sprintf(`%v`, mapKey.Interface())
  247. }
  248. // Map key and indent string dump.
  249. if !in.Option.WithType {
  250. in.Buffer.WriteString(fmt.Sprintf(
  251. "%s%v:%s",
  252. in.NewIndent,
  253. mapKeyStr,
  254. strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
  255. ))
  256. } else {
  257. in.Buffer.WriteString(fmt.Sprintf(
  258. "%s%s(%v):%s",
  259. in.NewIndent,
  260. mapKey.Type().String(),
  261. mapKeyStr,
  262. strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
  263. ))
  264. }
  265. // Map value dump.
  266. doDump(in.ReflectValue.MapIndex(mapKey), in.NewIndent, in.Buffer, in.Option)
  267. in.Buffer.WriteString(",\n")
  268. }
  269. in.Buffer.WriteString(fmt.Sprintf("%s}", in.Indent))
  270. }
  271. func doDumpStruct(in doDumpInternalInput) {
  272. structFields, _ := gstructs.Fields(gstructs.FieldsInput{
  273. Pointer: in.Value,
  274. RecursiveOption: gstructs.RecursiveOptionEmbedded,
  275. })
  276. var (
  277. hasNoExportedFields = true
  278. _, isReflectValue = in.Value.(reflect.Value)
  279. )
  280. for _, field := range structFields {
  281. if field.IsExported() {
  282. hasNoExportedFields = false
  283. break
  284. }
  285. }
  286. if !isReflectValue && (len(structFields) == 0 || hasNoExportedFields) {
  287. var (
  288. structContentStr = ""
  289. attributeCountStr = "0"
  290. )
  291. if v, ok := in.Value.(iString); ok {
  292. structContentStr = v.String()
  293. } else if v, ok := in.Value.(iError); ok {
  294. structContentStr = v.Error()
  295. } else if v, ok := in.Value.(iMarshalJSON); ok {
  296. b, _ := v.MarshalJSON()
  297. structContentStr = string(b)
  298. } else {
  299. // Has no common interface implements.
  300. if len(structFields) != 0 {
  301. goto dumpStructFields
  302. }
  303. }
  304. if structContentStr == "" {
  305. structContentStr = "{}"
  306. } else {
  307. structContentStr = fmt.Sprintf(`"%s"`, addSlashesForString(structContentStr))
  308. attributeCountStr = fmt.Sprintf(`%d`, len(structContentStr)-2)
  309. }
  310. if !in.Option.WithType {
  311. in.Buffer.WriteString(structContentStr)
  312. } else {
  313. in.Buffer.WriteString(fmt.Sprintf(
  314. "%s(%s) %s",
  315. in.ReflectTypeName,
  316. attributeCountStr,
  317. structContentStr,
  318. ))
  319. }
  320. return
  321. }
  322. dumpStructFields:
  323. var (
  324. maxSpaceNum = 0
  325. tmpSpaceNum = 0
  326. )
  327. for _, field := range structFields {
  328. if in.ExportedOnly && !field.IsExported() {
  329. continue
  330. }
  331. tmpSpaceNum = len(field.Name())
  332. if tmpSpaceNum > maxSpaceNum {
  333. maxSpaceNum = tmpSpaceNum
  334. }
  335. }
  336. if !in.Option.WithType {
  337. in.Buffer.WriteString("{\n")
  338. } else {
  339. in.Buffer.WriteString(fmt.Sprintf("%s(%d) {\n", in.ReflectTypeName, len(structFields)))
  340. }
  341. for _, field := range structFields {
  342. if in.ExportedOnly && !field.IsExported() {
  343. continue
  344. }
  345. tmpSpaceNum = len(fmt.Sprintf(`%v`, field.Name()))
  346. in.Buffer.WriteString(fmt.Sprintf(
  347. "%s%s:%s",
  348. in.NewIndent,
  349. field.Name(),
  350. strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
  351. ))
  352. doDump(field.Value, in.NewIndent, in.Buffer, in.Option)
  353. in.Buffer.WriteString(",\n")
  354. }
  355. in.Buffer.WriteString(fmt.Sprintf("%s}", in.Indent))
  356. }
  357. func doDumpNumber(in doDumpInternalInput) {
  358. if v, ok := in.Value.(iString); ok {
  359. s := v.String()
  360. if !in.Option.WithType {
  361. in.Buffer.WriteString(fmt.Sprintf(`"%v"`, addSlashesForString(s)))
  362. } else {
  363. in.Buffer.WriteString(fmt.Sprintf(
  364. `%s(%d) "%v"`,
  365. in.ReflectTypeName,
  366. len(s),
  367. addSlashesForString(s),
  368. ))
  369. }
  370. } else {
  371. doDumpDefault(in)
  372. }
  373. }
  374. func doDumpString(in doDumpInternalInput) {
  375. s := in.ReflectValue.String()
  376. if !in.Option.WithType {
  377. in.Buffer.WriteString(fmt.Sprintf(`"%v"`, addSlashesForString(s)))
  378. } else {
  379. in.Buffer.WriteString(fmt.Sprintf(
  380. `%s(%d) "%v"`,
  381. in.ReflectTypeName,
  382. len(s),
  383. addSlashesForString(s),
  384. ))
  385. }
  386. }
  387. func doDumpBool(in doDumpInternalInput) {
  388. var s string
  389. if in.ReflectValue.Bool() {
  390. s = `true`
  391. } else {
  392. s = `false`
  393. }
  394. if in.Option.WithType {
  395. s = fmt.Sprintf(`bool(%s)`, s)
  396. }
  397. in.Buffer.WriteString(s)
  398. }
  399. func doDumpDefault(in doDumpInternalInput) {
  400. var s string
  401. if in.ReflectValue.IsValid() && in.ReflectValue.CanInterface() {
  402. s = fmt.Sprintf("%v", in.ReflectValue.Interface())
  403. }
  404. if s == "" {
  405. s = fmt.Sprintf("%v", in.Value)
  406. }
  407. s = gstr.Trim(s, `<>`)
  408. if !in.Option.WithType {
  409. in.Buffer.WriteString(s)
  410. } else {
  411. in.Buffer.WriteString(fmt.Sprintf("%s(%s)", in.ReflectTypeName, s))
  412. }
  413. }
  414. func addSlashesForString(s string) string {
  415. return gstr.ReplaceByMap(s, map[string]string{
  416. `"`: `\"`,
  417. "\r": `\r`,
  418. "\t": `\t`,
  419. "\n": `\n`,
  420. })
  421. }