gutil_dump.go 12 KB

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