123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- // 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"
- "encoding/json"
- "fmt"
- "io"
- "reflect"
- "strings"
- "github.com/gogf/gf/v2/internal/reflection"
- "github.com/gogf/gf/v2/os/gstructs"
- "github.com/gogf/gf/v2/text/gstr"
- )
- // 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
- }
- // iMarshalJSON is the interface for custom Json marshaling.
- type iMarshalJSON interface {
- MarshalJSON() ([]byte, error)
- }
- // DumpOption specifies the behavior of function Export.
- type DumpOption struct {
- WithType bool // WithType specifies dumping content with type information.
- ExportedOnly bool // Only dump Exported fields for structs.
- }
- // Dump prints variables `values` to stdout with more manually readable.
- func Dump(values ...interface{}) {
- for _, value := range values {
- DumpWithOption(value, DumpOption{
- WithType: false,
- ExportedOnly: false,
- })
- }
- }
- // DumpWithType acts like Dump, but with type information.
- // Also see Dump.
- func DumpWithType(values ...interface{}) {
- for _, value := range values {
- DumpWithOption(value, DumpOption{
- WithType: true,
- ExportedOnly: false,
- })
- }
- }
- // DumpWithOption returns variables `values` as a string with more manually readable.
- func DumpWithOption(value interface{}, option DumpOption) {
- buffer := bytes.NewBuffer(nil)
- DumpTo(buffer, value, DumpOption{
- WithType: option.WithType,
- ExportedOnly: option.ExportedOnly,
- })
- fmt.Println(buffer.String())
- }
- // DumpTo writes variables `values` as a string in to `writer` with more manually readable
- func DumpTo(writer io.Writer, value interface{}, option DumpOption) {
- buffer := bytes.NewBuffer(nil)
- doDump(value, "", buffer, doDumpOption{
- WithType: option.WithType,
- ExportedOnly: option.ExportedOnly,
- })
- _, _ = writer.Write(buffer.Bytes())
- }
- type doDumpOption struct {
- WithType bool
- ExportedOnly bool
- DumpedPointerSet map[string]struct{}
- }
- func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDumpOption) {
- if option.DumpedPointerSet == nil {
- option.DumpedPointerSet = map[string]struct{}{}
- }
- if value == nil {
- buffer.WriteString(`<nil>`)
- return
- }
- var reflectValue reflect.Value
- if v, ok := value.(reflect.Value); ok {
- reflectValue = v
- if v.IsValid() && v.CanInterface() {
- value = v.Interface()
- } else {
- if convertedValue, ok := reflection.ValueToInterface(v); ok {
- value = convertedValue
- }
- }
- } else {
- reflectValue = reflect.ValueOf(value)
- }
- var reflectKind = reflectValue.Kind()
- // Double check nil value.
- if value == nil || reflectKind == reflect.Invalid {
- buffer.WriteString(`<nil>`)
- return
- }
- var (
- reflectTypeName = reflectValue.Type().String()
- ptrAddress string
- newIndent = indent + dumpIndent
- )
- reflectTypeName = strings.ReplaceAll(reflectTypeName, `[]uint8`, `[]byte`)
- for reflectKind == reflect.Ptr {
- if ptrAddress == "" {
- ptrAddress = fmt.Sprintf(`0x%x`, reflectValue.Pointer())
- }
- reflectValue = reflectValue.Elem()
- reflectKind = reflectValue.Kind()
- }
- var (
- exportInternalInput = doDumpInternalInput{
- Value: value,
- Indent: indent,
- NewIndent: newIndent,
- Buffer: buffer,
- Option: option,
- PtrAddress: ptrAddress,
- ReflectValue: reflectValue,
- ReflectTypeName: reflectTypeName,
- ExportedOnly: option.ExportedOnly,
- DumpedPointerSet: option.DumpedPointerSet,
- }
- )
- switch reflectKind {
- case reflect.Slice, reflect.Array:
- doDumpSlice(exportInternalInput)
- case reflect.Map:
- doDumpMap(exportInternalInput)
- case reflect.Struct:
- doDumpStruct(exportInternalInput)
- case reflect.String:
- doDumpString(exportInternalInput)
- case reflect.Bool:
- doDumpBool(exportInternalInput)
- 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>`, reflectValue.Type().String()))
- case reflect.Func:
- if reflectValue.IsNil() || !reflectValue.IsValid() {
- buffer.WriteString(`<nil>`)
- } else {
- buffer.WriteString(fmt.Sprintf(`<%s>`, reflectValue.Type().String()))
- }
- case reflect.Interface:
- doDump(exportInternalInput.ReflectValue.Elem(), indent, buffer, option)
- default:
- doDumpDefault(exportInternalInput)
- }
- }
- type doDumpInternalInput struct {
- Value interface{}
- Indent string
- NewIndent string
- Buffer *bytes.Buffer
- Option doDumpOption
- ReflectValue reflect.Value
- ReflectTypeName string
- PtrAddress string
- ExportedOnly bool
- DumpedPointerSet map[string]struct{}
- }
- func doDumpSlice(in doDumpInternalInput) {
- if b, ok := in.Value.([]byte); ok {
- if !in.Option.WithType {
- in.Buffer.WriteString(fmt.Sprintf(`"%s"`, addSlashesForString(string(b))))
- } else {
- in.Buffer.WriteString(fmt.Sprintf(
- `%s(%d) "%s"`,
- in.ReflectTypeName,
- len(string(b)),
- string(b),
- ))
- }
- return
- }
- if in.ReflectValue.Len() == 0 {
- if !in.Option.WithType {
- in.Buffer.WriteString("[]")
- } else {
- in.Buffer.WriteString(fmt.Sprintf("%s(0) []", in.ReflectTypeName))
- }
- return
- }
- if !in.Option.WithType {
- 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), in.NewIndent, in.Buffer, in.Option)
- in.Buffer.WriteString(",\n")
- }
- in.Buffer.WriteString(fmt.Sprintf("%s]", in.Indent))
- }
- func doDumpMap(in doDumpInternalInput) {
- var mapKeys = make([]reflect.Value, 0)
- for _, key := range in.ReflectValue.MapKeys() {
- if !key.CanInterface() {
- continue
- }
- mapKey := key
- mapKeys = append(mapKeys, mapKey)
- }
- if len(mapKeys) == 0 {
- if !in.Option.WithType {
- in.Buffer.WriteString("{}")
- } else {
- in.Buffer.WriteString(fmt.Sprintf("%s(0) {}", in.ReflectTypeName))
- }
- return
- }
- var (
- maxSpaceNum = 0
- tmpSpaceNum = 0
- mapKeyStr = ""
- )
- for _, key := range mapKeys {
- tmpSpaceNum = len(fmt.Sprintf(`%v`, key.Interface()))
- if tmpSpaceNum > maxSpaceNum {
- maxSpaceNum = tmpSpaceNum
- }
- }
- if !in.Option.WithType {
- 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())
- } else {
- mapKeyStr = fmt.Sprintf(`%v`, mapKey.Interface())
- }
- // Map key and indent string dump.
- if !in.Option.WithType {
- 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),
- ))
- }
- // Map value dump.
- doDump(in.ReflectValue.MapIndex(mapKey), in.NewIndent, in.Buffer, in.Option)
- in.Buffer.WriteString(",\n")
- }
- in.Buffer.WriteString(fmt.Sprintf("%s}", in.Indent))
- }
- func doDumpStruct(in doDumpInternalInput) {
- if in.PtrAddress != "" {
- if _, ok := in.DumpedPointerSet[in.PtrAddress]; ok {
- in.Buffer.WriteString(fmt.Sprintf(`<cycle dump %s>`, in.PtrAddress))
- return
- }
- }
- in.DumpedPointerSet[in.PtrAddress] = struct{}{}
- structFields, _ := gstructs.Fields(gstructs.FieldsInput{
- Pointer: in.Value,
- RecursiveOption: gstructs.RecursiveOptionEmbedded,
- })
- var (
- hasNoExportedFields = true
- _, isReflectValue = in.Value.(reflect.Value)
- )
- for _, field := range structFields {
- if field.IsExported() {
- hasNoExportedFields = false
- break
- }
- }
- if !isReflectValue && (len(structFields) == 0 || hasNoExportedFields) {
- var (
- structContentStr = ""
- attributeCountStr = "0"
- )
- if v, ok := in.Value.(iString); ok {
- structContentStr = v.String()
- } else if v, ok := in.Value.(iError); ok {
- structContentStr = v.Error()
- } else if v, ok := in.Value.(iMarshalJSON); ok {
- b, _ := v.MarshalJSON()
- structContentStr = string(b)
- } else {
- // Has no common interface implements.
- if len(structFields) != 0 {
- goto dumpStructFields
- }
- }
- if structContentStr == "" {
- structContentStr = "{}"
- } else {
- structContentStr = fmt.Sprintf(`"%s"`, addSlashesForString(structContentStr))
- attributeCountStr = fmt.Sprintf(`%d`, len(structContentStr)-2)
- }
- if !in.Option.WithType {
- in.Buffer.WriteString(structContentStr)
- } else {
- in.Buffer.WriteString(fmt.Sprintf(
- "%s(%s) %s",
- in.ReflectTypeName,
- attributeCountStr,
- structContentStr,
- ))
- }
- return
- }
- dumpStructFields:
- var (
- maxSpaceNum = 0
- tmpSpaceNum = 0
- )
- for _, field := range structFields {
- if in.ExportedOnly && !field.IsExported() {
- continue
- }
- tmpSpaceNum = len(field.Name())
- if tmpSpaceNum > maxSpaceNum {
- maxSpaceNum = tmpSpaceNum
- }
- }
- if !in.Option.WithType {
- in.Buffer.WriteString("{\n")
- } else {
- in.Buffer.WriteString(fmt.Sprintf("%s(%d) {\n", in.ReflectTypeName, len(structFields)))
- }
- for _, field := range structFields {
- if in.ExportedOnly && !field.IsExported() {
- continue
- }
- 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, in.NewIndent, in.Buffer, in.Option)
- in.Buffer.WriteString(",\n")
- }
- in.Buffer.WriteString(fmt.Sprintf("%s}", in.Indent))
- }
- func doDumpNumber(in doDumpInternalInput) {
- if v, ok := in.Value.(iString); ok {
- s := v.String()
- if !in.Option.WithType {
- in.Buffer.WriteString(fmt.Sprintf(`"%v"`, addSlashesForString(s)))
- } else {
- in.Buffer.WriteString(fmt.Sprintf(
- `%s(%d) "%v"`,
- in.ReflectTypeName,
- len(s),
- addSlashesForString(s),
- ))
- }
- } else {
- doDumpDefault(in)
- }
- }
- func doDumpString(in doDumpInternalInput) {
- s := in.ReflectValue.String()
- if !in.Option.WithType {
- in.Buffer.WriteString(fmt.Sprintf(`"%v"`, addSlashesForString(s)))
- } else {
- in.Buffer.WriteString(fmt.Sprintf(
- `%s(%d) "%v"`,
- in.ReflectTypeName,
- len(s),
- addSlashesForString(s),
- ))
- }
- }
- 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)
- }
- func doDumpDefault(in doDumpInternalInput) {
- var s string
- if in.ReflectValue.IsValid() && in.ReflectValue.CanInterface() {
- s = fmt.Sprintf("%v", in.ReflectValue.Interface())
- }
- if s == "" {
- s = fmt.Sprintf("%v", in.Value)
- }
- s = gstr.Trim(s, `<>`)
- if !in.Option.WithType {
- in.Buffer.WriteString(s)
- } else {
- in.Buffer.WriteString(fmt.Sprintf("%s(%s)", in.ReflectTypeName, s))
- }
- }
- func addSlashesForString(s string) string {
- return gstr.ReplaceByMap(s, map[string]string{
- `"`: `\"`,
- "\r": `\r`,
- "\t": `\t`,
- "\n": `\n`,
- })
- }
- // DumpJson pretty dumps json content to stdout.
- func DumpJson(jsonContent string) {
- var (
- buffer = bytes.NewBuffer(nil)
- jsonBytes = []byte(jsonContent)
- )
- if err := json.Indent(buffer, jsonBytes, "", "\t"); err != nil {
- fmt.Println(err.Error())
- }
- fmt.Println(buffer.String())
- }
|