Files
gf/util/gutil/gutil_dump.go

391 lines
9.2 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gutil
import (
"bytes"
"fmt"
2021-11-16 20:41:31 +08:00
"io"
"reflect"
"strings"
2021-11-13 23:30:31 +08:00
"github.com/gogf/gf/v2/os/gstructs"
2021-11-13 23:30:31 +08:00
"github.com/gogf/gf/v2/text/gstr"
)
2021-11-07 00:32:16 +08:00
// iString is used for type assert api for String().
type iString interface {
String() string
}
// iError is used for type assert api for Error().
type iError interface {
Error() string
}
2021-11-07 00:32:16 +08:00
// iMarshalJSON is the interface for custom Json marshaling.
type iMarshalJSON interface {
MarshalJSON() ([]byte, error)
}
2021-11-18 15:20:37 +08:00
// DumpOption specifies the behavior of function Export.
type DumpOption struct {
WithType bool // WithType specifies dumping content with type information.
2021-10-21 22:07:43 +08:00
}
// Dump prints variables `values` to stdout with more manually readable.
func Dump(values ...interface{}) {
for _, value := range values {
2021-11-18 15:25:24 +08:00
DumpWithOption(value, DumpOption{
WithType: false,
2021-11-18 15:25:24 +08:00
})
2021-10-21 22:07:43 +08:00
}
}
2021-10-30 20:35:55 +08:00
// DumpWithType acts like Dump, but with type information.
2021-10-21 22:07:43 +08:00
// Also see Dump.
2021-10-30 20:35:55 +08:00
func DumpWithType(values ...interface{}) {
2021-10-21 22:07:43 +08:00
for _, value := range values {
2021-11-18 15:25:24 +08:00
DumpWithOption(value, DumpOption{
WithType: true,
2021-11-18 15:25:24 +08:00
})
}
}
2021-11-18 15:20:37 +08:00
// DumpWithOption returns variables `values` as a string with more manually readable.
2021-11-18 15:25:24 +08:00
func DumpWithOption(value interface{}, option DumpOption) {
buffer := bytes.NewBuffer(nil)
2021-11-18 15:20:37 +08:00
DumpTo(buffer, value, DumpOption{
WithType: option.WithType,
2021-10-21 22:07:43 +08:00
})
2021-11-18 15:25:24 +08:00
fmt.Println(buffer.String())
2021-10-21 22:07:43 +08:00
}
2021-11-18 15:20:37 +08:00
// DumpTo writes variables `values` as a string in to `writer` with more manually readable
func DumpTo(writer io.Writer, value interface{}, option DumpOption) {
2021-11-16 20:41:31 +08:00
buffer := bytes.NewBuffer(nil)
2021-11-18 15:20:37 +08:00
doDump(value, "", buffer, doDumpOption{
WithType: option.WithType,
2021-11-16 20:41:31 +08:00
})
_, _ = writer.Write(buffer.Bytes())
}
2021-11-18 15:20:37 +08:00
type doDumpOption struct {
WithType bool
2021-10-21 22:07:43 +08:00
}
2021-11-18 15:20:37 +08:00
func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDumpOption) {
if value == nil {
buffer.WriteString(`<nil>`)
return
}
2021-10-21 22:07:43 +08:00
var (
reflectValue = reflect.ValueOf(value)
reflectKind = reflectValue.Kind()
reflectTypeName = reflect.TypeOf(value).String()
2021-10-21 22:07:43 +08:00
newIndent = indent + dumpIndent
)
reflectTypeName = strings.ReplaceAll(reflectTypeName, `[]uint8`, `[]byte`)
if !option.WithType {
2021-10-21 22:07:43 +08:00
reflectTypeName = ""
}
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
2021-11-18 15:20:37 +08:00
var (
exportInternalInput = doDumpInternalInput{
Value: value,
Indent: indent,
NewIndent: newIndent,
Buffer: buffer,
Option: option,
ReflectValue: reflectValue,
ReflectTypeName: reflectTypeName,
}
)
2021-10-21 22:07:43 +08:00
switch reflectKind {
case reflect.Slice, reflect.Array:
2021-11-18 15:20:37 +08:00
doDumpSlice(exportInternalInput)
case reflect.Map:
doDumpMap(exportInternalInput)
case reflect.Struct:
doDumpStruct(exportInternalInput)
case reflect.String:
doDumpString(exportInternalInput)
case reflect.Bool:
2021-11-22 23:05:32 +08:00
doDumpBool(exportInternalInput)
2021-11-18 15:20:37 +08:00
case
reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Float32,
reflect.Float64,
reflect.Complex64,
reflect.Complex128:
doDumpNumber(exportInternalInput)
case reflect.Chan:
buffer.WriteString(fmt.Sprintf(`<%s>`, reflect.TypeOf(value).String()))
2021-11-18 15:20:37 +08:00
case reflect.Func:
if reflectValue.IsNil() || !reflectValue.IsValid() {
buffer.WriteString(`<nil>`)
} else {
buffer.WriteString(fmt.Sprintf(`<%s>`, reflect.TypeOf(value).String()))
}
2021-11-18 15:20:37 +08:00
default:
doDumpDefault(exportInternalInput)
}
}
type doDumpInternalInput struct {
Value interface{}
Indent string
NewIndent string
Buffer *bytes.Buffer
Option doDumpOption
ReflectValue reflect.Value
ReflectTypeName string
}
func doDumpSlice(in doDumpInternalInput) {
if b, ok := in.Value.([]byte); ok {
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(fmt.Sprintf(`"%s"`, addSlashesForString(string(b))))
2021-10-21 22:07:43 +08:00
} else {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(fmt.Sprintf(
`%s(%d) "%s"`,
in.ReflectTypeName,
len(string(b)),
string(b),
))
2021-10-21 22:07:43 +08:00
}
2021-11-18 15:20:37 +08:00
return
}
if in.ReflectValue.Len() == 0 {
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString("[]")
} else {
in.Buffer.WriteString(fmt.Sprintf("%s(0) []", in.ReflectTypeName))
2021-10-21 22:07:43 +08:00
}
2021-11-18 15:20:37 +08:00
return
}
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString("[\n")
} else {
in.Buffer.WriteString(fmt.Sprintf("%s(%d) [\n", in.ReflectTypeName, in.ReflectValue.Len()))
}
for i := 0; i < in.ReflectValue.Len(); i++ {
in.Buffer.WriteString(in.NewIndent)
doDump(in.ReflectValue.Index(i).Interface(), in.NewIndent, in.Buffer, in.Option)
in.Buffer.WriteString(",\n")
}
in.Buffer.WriteString(fmt.Sprintf("%s]", in.Indent))
}
2021-10-21 22:07:43 +08:00
2021-11-18 15:20:37 +08:00
func doDumpMap(in doDumpInternalInput) {
var (
mapKeys = in.ReflectValue.MapKeys()
)
if len(mapKeys) == 0 {
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString("{}")
} else {
in.Buffer.WriteString(fmt.Sprintf("%s(0) {}", in.ReflectTypeName))
2021-10-21 22:07:43 +08:00
}
2021-11-18 15:20:37 +08:00
return
}
var (
maxSpaceNum = 0
tmpSpaceNum = 0
mapKeyStr = ""
)
for _, key := range mapKeys {
tmpSpaceNum = len(fmt.Sprintf(`%v`, key.Interface()))
if tmpSpaceNum > maxSpaceNum {
maxSpaceNum = tmpSpaceNum
2021-10-21 22:07:43 +08:00
}
2021-11-18 15:20:37 +08:00
}
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString("{\n")
} else {
in.Buffer.WriteString(fmt.Sprintf("%s(%d) {\n", in.ReflectTypeName, len(mapKeys)))
}
for _, mapKey := range mapKeys {
tmpSpaceNum = len(fmt.Sprintf(`%v`, mapKey.Interface()))
if mapKey.Kind() == reflect.String {
mapKeyStr = fmt.Sprintf(`"%v"`, mapKey.Interface())
2021-10-21 22:07:43 +08:00
} else {
2021-11-18 15:20:37 +08:00
mapKeyStr = fmt.Sprintf(`%v`, mapKey.Interface())
2021-10-21 22:07:43 +08:00
}
// Map key and indent string dump.
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(fmt.Sprintf(
"%s%v:%s",
in.NewIndent,
mapKeyStr,
strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
))
} else {
in.Buffer.WriteString(fmt.Sprintf(
"%s%s(%v):%s",
in.NewIndent,
mapKey.Type().String(),
mapKeyStr,
strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
))
2021-10-21 22:07:43 +08:00
}
// Map value dump.
2021-11-18 15:20:37 +08:00
doDump(in.ReflectValue.MapIndex(mapKey).Interface(), in.NewIndent, in.Buffer, in.Option)
in.Buffer.WriteString(",\n")
}
in.Buffer.WriteString(fmt.Sprintf("%s}", in.Indent))
}
2021-10-21 22:07:43 +08:00
2021-11-18 15:20:37 +08:00
func doDumpStruct(in doDumpInternalInput) {
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
2021-11-18 15:20:37 +08:00
Pointer: in.Value,
RecursiveOption: gstructs.RecursiveOptionEmbedded,
2021-11-18 15:20:37 +08:00
})
if len(structFields) == 0 {
2021-10-21 22:07:43 +08:00
var (
2021-11-18 15:20:37 +08:00
structContentStr = ""
attributeCountStr = "0"
2021-10-21 22:07:43 +08:00
)
2021-11-18 15:20:37 +08:00
if v, ok := in.Value.(iString); ok {
structContentStr = v.String()
} else if v, ok := in.Value.(iError); ok {
structContentStr = v.Error()
2021-11-18 15:20:37 +08:00
} else if v, ok := in.Value.(iMarshalJSON); ok {
b, _ := v.MarshalJSON()
structContentStr = string(b)
2021-10-21 22:07:43 +08:00
}
2021-11-18 15:20:37 +08:00
if structContentStr == "" {
structContentStr = "{}"
2021-10-21 22:07:43 +08:00
} else {
2021-11-18 15:20:37 +08:00
structContentStr = fmt.Sprintf(`"%s"`, addSlashesForString(structContentStr))
attributeCountStr = fmt.Sprintf(`%d`, len(structContentStr)-2)
2021-10-21 22:07:43 +08:00
}
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(structContentStr)
} else {
in.Buffer.WriteString(fmt.Sprintf(
"%s(%s) %s",
in.ReflectTypeName,
attributeCountStr,
structContentStr,
2021-10-21 22:07:43 +08:00
))
}
2021-11-18 15:20:37 +08:00
return
}
var (
maxSpaceNum = 0
tmpSpaceNum = 0
)
for _, field := range structFields {
tmpSpaceNum = len(field.Name())
if tmpSpaceNum > maxSpaceNum {
maxSpaceNum = tmpSpaceNum
}
}
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString("{\n")
} else {
in.Buffer.WriteString(fmt.Sprintf("%s(%d) {\n", in.ReflectTypeName, len(structFields)))
}
for _, field := range structFields {
tmpSpaceNum = len(fmt.Sprintf(`%v`, field.Name()))
in.Buffer.WriteString(fmt.Sprintf(
"%s%s:%s",
in.NewIndent,
field.Name(),
strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
))
doDump(field.Value.Interface(), in.NewIndent, in.Buffer, in.Option)
in.Buffer.WriteString(",\n")
}
in.Buffer.WriteString(fmt.Sprintf("%s}", in.Indent))
}
2021-10-21 22:07:43 +08:00
2021-11-18 15:20:37 +08:00
func doDumpNumber(in doDumpInternalInput) {
if v, ok := in.Value.(iString); ok {
s := v.String()
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(fmt.Sprintf(`"%v"`, addSlashesForString(s)))
2021-10-21 22:07:43 +08:00
} else {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(fmt.Sprintf(
2021-11-16 20:41:31 +08:00
`%s(%d) "%v"`,
2021-11-18 15:20:37 +08:00
in.ReflectTypeName,
len(s),
addSlashesForString(s),
2021-10-21 22:07:43 +08:00
))
}
2021-11-18 15:20:37 +08:00
} else {
doDumpDefault(in)
}
}
2021-10-21 22:07:43 +08:00
2021-11-18 15:20:37 +08:00
func doDumpString(in doDumpInternalInput) {
s := in.ReflectValue.String()
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(fmt.Sprintf(`"%v"`, addSlashesForString(s)))
} else {
in.Buffer.WriteString(fmt.Sprintf(
`%s(%d) "%v"`,
in.ReflectTypeName,
len(s),
addSlashesForString(s),
))
}
}
2021-11-22 23:05:32 +08:00
func doDumpBool(in doDumpInternalInput) {
var s string
if in.ReflectValue.Bool() {
s = `true`
} else {
s = `false`
}
if in.Option.WithType {
s = fmt.Sprintf(`bool(%s)`, s)
}
in.Buffer.WriteString(s)
}
2021-11-18 15:20:37 +08:00
func doDumpDefault(in doDumpInternalInput) {
s := fmt.Sprintf("%v", in.Value)
s = gstr.Trim(s, `<>`)
if !in.Option.WithType {
2021-11-18 15:20:37 +08:00
in.Buffer.WriteString(s)
} else {
in.Buffer.WriteString(fmt.Sprintf("%s(%s)", in.ReflectTypeName, s))
}
}
2021-11-18 15:20:37 +08:00
func addSlashesForString(s string) string {
return gstr.ReplaceByMap(s, map[string]string{
`"`: `\"`,
"\r": `\r`,
"\t": `\t`,
"\n": `\n`,
})
}