mirror of
https://gitee.com/johng/gf
synced 2026-07-07 22:27:06 +08:00
improve gutil.Dump
This commit is contained in:
@ -184,5 +184,5 @@ func (j *Json) Dump() {
|
||||
func (j *Json) Export() string {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gutil.Export(*j.p)
|
||||
return gutil.Export(*j.p, gutil.ExportOption{})
|
||||
}
|
||||
|
||||
@ -34,13 +34,14 @@ func Listen() {
|
||||
}
|
||||
|
||||
// Dump dumps a variable to stdout with more manually readable.
|
||||
func Dump(i ...interface{}) {
|
||||
gutil.Dump(i...)
|
||||
func Dump(values ...interface{}) {
|
||||
gutil.Dump(values...)
|
||||
}
|
||||
|
||||
// Export exports a variable to string with more manually readable.
|
||||
func Export(i ...interface{}) string {
|
||||
return gutil.Export(i...)
|
||||
// DumpBrief acts like Dump, but with no type information.
|
||||
// Also see Dump.
|
||||
func DumpBrief(values ...interface{}) {
|
||||
gutil.DumpBrief(values...)
|
||||
}
|
||||
|
||||
// Throw throws an exception, which can be caught by TryCatch function.
|
||||
|
||||
@ -26,7 +26,7 @@ import (
|
||||
func (view *View) buildInFuncDump(values ...interface{}) (result string) {
|
||||
result += "<!--\n"
|
||||
for _, v := range values {
|
||||
result += gutil.Export(v) + "\n"
|
||||
result += gutil.Export(v, gutil.ExportOption{WithoutType: true}) + "\n"
|
||||
}
|
||||
result += "-->\n"
|
||||
return result
|
||||
|
||||
@ -16,6 +16,10 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
dumpIndent = ` `
|
||||
)
|
||||
|
||||
// Throw throws out an exception, which can be caught be TryCatch or recover.
|
||||
func Throw(exception interface{}) {
|
||||
panic(exception)
|
||||
|
||||
@ -9,12 +9,16 @@ package gutil
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"os"
|
||||
"github.com/gogf/gf/v2/internal/structs"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ExportOption specifies the behavior of function Export.
|
||||
type ExportOption struct {
|
||||
WithoutType bool // WithoutType specifies exported content has no type information.
|
||||
}
|
||||
|
||||
// iVal is used for type assert api for Val().
|
||||
type iVal interface {
|
||||
Val() interface{}
|
||||
@ -30,66 +34,207 @@ type iMapStrAny interface {
|
||||
MapStrAny() map[string]interface{}
|
||||
}
|
||||
|
||||
// Dump prints variables <i...> to stdout with more manually readable.
|
||||
func Dump(i ...interface{}) {
|
||||
s := Export(i...)
|
||||
if s != "" {
|
||||
fmt.Println(s)
|
||||
// Dump prints variables `values` to stdout with more manually readable.
|
||||
func Dump(values ...interface{}) {
|
||||
for _, value := range values {
|
||||
if s := Export(value, ExportOption{
|
||||
WithoutType: false,
|
||||
}); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export returns variables <i...> as a string with more manually readable.
|
||||
func Export(i ...interface{}) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for _, value := range i {
|
||||
switch r := value.(type) {
|
||||
case []byte:
|
||||
buffer.Write(r)
|
||||
case string:
|
||||
buffer.WriteString(r)
|
||||
default:
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
value = gconv.Interfaces(value)
|
||||
case reflect.Map:
|
||||
value = gconv.Map(value)
|
||||
case reflect.Struct:
|
||||
converted := false
|
||||
if r, ok := value.(iVal); ok {
|
||||
if result := r.Val(); result != nil {
|
||||
value = result
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
if r, ok := value.(iMapStrAny); ok {
|
||||
if result := r.MapStrAny(); result != nil {
|
||||
value = result
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
if r, ok := value.(iString); ok {
|
||||
value = r.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
encoder := json.NewEncoder(buffer)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.SetIndent("", "\t")
|
||||
if err := encoder.Encode(value); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
}
|
||||
// DumpBrief acts like Dump, but with no type information.
|
||||
// Also see Dump.
|
||||
func DumpBrief(values ...interface{}) {
|
||||
for _, value := range values {
|
||||
if s := Export(value, ExportOption{
|
||||
WithoutType: true,
|
||||
}); s != "" {
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export returns variables `values` as a string with more manually readable.
|
||||
func Export(value interface{}, option ExportOption) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
doExport(value, "", buffer, doExportOption{
|
||||
WithoutType: option.WithoutType,
|
||||
})
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
type doExportOption struct {
|
||||
WithoutType bool
|
||||
}
|
||||
|
||||
func doExport(value interface{}, indent string, buffer *bytes.Buffer, option doExportOption) {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
reflectTypeName = reflectValue.Type().String()
|
||||
newIndent = indent + dumpIndent
|
||||
)
|
||||
if option.WithoutType {
|
||||
reflectTypeName = ""
|
||||
}
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
if _, ok := value.([]byte); ok {
|
||||
if option.WithoutType {
|
||||
buffer.WriteString(fmt.Sprintf("\"%v\"\n", value))
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
"%s(%d) \"%v\"\n",
|
||||
reflectTypeName,
|
||||
len(reflectValue.String()),
|
||||
value,
|
||||
))
|
||||
}
|
||||
return
|
||||
}
|
||||
if reflectValue.Len() == 0 {
|
||||
if option.WithoutType {
|
||||
buffer.WriteString("[]")
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%s(0) []", reflectTypeName))
|
||||
}
|
||||
return
|
||||
}
|
||||
if option.WithoutType {
|
||||
buffer.WriteString("[\n")
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%s(%d) [\n", reflectTypeName, reflectValue.Len()))
|
||||
}
|
||||
for i := 0; i < reflectValue.Len(); i++ {
|
||||
buffer.WriteString(newIndent)
|
||||
doExport(reflectValue.Index(i).Interface(), newIndent, buffer, option)
|
||||
buffer.WriteString(",\n")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%s]", indent))
|
||||
|
||||
case reflect.Map:
|
||||
var (
|
||||
mapKeys = reflectValue.MapKeys()
|
||||
)
|
||||
if len(mapKeys) == 0 {
|
||||
if option.WithoutType {
|
||||
buffer.WriteString("{}")
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%s(0) {}", 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 option.WithoutType {
|
||||
buffer.WriteString("{\n")
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%s(%d) {\n", 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())
|
||||
}
|
||||
if option.WithoutType {
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
"%s%v:%s",
|
||||
newIndent,
|
||||
mapKeyStr,
|
||||
gstr.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
|
||||
))
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
"%s%s(%v):%s",
|
||||
newIndent,
|
||||
mapKey.Type().String(),
|
||||
mapKeyStr,
|
||||
gstr.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
|
||||
))
|
||||
}
|
||||
doExport(reflectValue.MapIndex(mapKey).Interface(), newIndent, buffer, option)
|
||||
buffer.WriteString(",\n")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%s}", indent))
|
||||
|
||||
case reflect.Struct:
|
||||
structFields, _ := structs.Fields(structs.FieldsInput{
|
||||
Pointer: value,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
if len(structFields) == 0 {
|
||||
if option.WithoutType {
|
||||
buffer.WriteString("{}")
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%s(0) {}", reflectTypeName))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
maxSpaceNum = 0
|
||||
tmpSpaceNum = 0
|
||||
)
|
||||
for _, field := range structFields {
|
||||
tmpSpaceNum = len(field.Name())
|
||||
if tmpSpaceNum > maxSpaceNum {
|
||||
maxSpaceNum = tmpSpaceNum
|
||||
}
|
||||
}
|
||||
if option.WithoutType {
|
||||
buffer.WriteString("{\n")
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%s(%d) {\n", reflectTypeName, len(structFields)))
|
||||
}
|
||||
for _, field := range structFields {
|
||||
tmpSpaceNum = len(fmt.Sprintf(`%v`, field.Name()))
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
"%s%s:%s",
|
||||
newIndent,
|
||||
field.Name(),
|
||||
gstr.Repeat(" ", maxSpaceNum-tmpSpaceNum+1),
|
||||
))
|
||||
doExport(field.Value.Interface(), newIndent, buffer, option)
|
||||
buffer.WriteString(",\n")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%s}", indent))
|
||||
|
||||
case reflect.String:
|
||||
if option.WithoutType {
|
||||
buffer.WriteString(fmt.Sprintf("\"%v\"", value))
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
"%s(%d) \"%v\"",
|
||||
reflectTypeName,
|
||||
len(reflectValue.String()),
|
||||
value,
|
||||
))
|
||||
}
|
||||
|
||||
default:
|
||||
if option.WithoutType {
|
||||
buffer.WriteString(fmt.Sprintf("%v", value))
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%s(%v)", reflectTypeName, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ package gutil_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
@ -15,10 +17,116 @@ import (
|
||||
)
|
||||
|
||||
func Test_Dump(t *testing.T) {
|
||||
type CommonReq struct {
|
||||
AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"`
|
||||
ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"`
|
||||
}
|
||||
type SetSpecInfo struct {
|
||||
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"`
|
||||
Shards int32 `des:"shards 分片数" sum:"Shards Summary"`
|
||||
Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"`
|
||||
}
|
||||
type CreateResourceReq struct {
|
||||
CommonReq
|
||||
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"`
|
||||
Name string `des:"实例名称"`
|
||||
Product string `des:"业务类型"`
|
||||
Region string `v:"required" des:"区域"`
|
||||
SetMap map[string]*SetSpecInfo `v:"required" des:"配置Map"`
|
||||
SetSlice []SetSpecInfo `v:"required" des:"配置Slice"`
|
||||
Handler ghttp.HandlerFunc
|
||||
internal string
|
||||
}
|
||||
req := &CreateResourceReq{
|
||||
CommonReq: CommonReq{
|
||||
AppId: 12345678,
|
||||
ResourceId: "tdchqy-xxx",
|
||||
},
|
||||
Name: "john",
|
||||
Product: "goframe",
|
||||
Region: "cd",
|
||||
SetMap: map[string]*SetSpecInfo{
|
||||
"test1": {
|
||||
StorageType: "ssd",
|
||||
Shards: 2,
|
||||
Params: []string{"a", "b", "c"},
|
||||
},
|
||||
"test2": {
|
||||
StorageType: "hssd",
|
||||
Shards: 10,
|
||||
Params: []string{},
|
||||
},
|
||||
},
|
||||
SetSlice: []SetSpecInfo{
|
||||
{
|
||||
StorageType: "hssd",
|
||||
Shards: 10,
|
||||
Params: []string{"h"},
|
||||
},
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gutil.Dump(map[int]int{
|
||||
100: 100,
|
||||
})
|
||||
gutil.Dump(req)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DumpBrief(t *testing.T) {
|
||||
type CommonReq struct {
|
||||
AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"`
|
||||
ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"`
|
||||
}
|
||||
type SetSpecInfo struct {
|
||||
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"`
|
||||
Shards int32 `des:"shards 分片数" sum:"Shards Summary"`
|
||||
Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"`
|
||||
}
|
||||
type CreateResourceReq struct {
|
||||
CommonReq
|
||||
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"`
|
||||
Name string `des:"实例名称"`
|
||||
Product string `des:"业务类型"`
|
||||
Region string `v:"required" des:"区域"`
|
||||
SetMap map[string]*SetSpecInfo `v:"required" des:"配置Map"`
|
||||
SetSlice []SetSpecInfo `v:"required" des:"配置Slice"`
|
||||
Handler ghttp.HandlerFunc
|
||||
internal string
|
||||
}
|
||||
req := &CreateResourceReq{
|
||||
CommonReq: CommonReq{
|
||||
AppId: 12345678,
|
||||
ResourceId: "tdchqy-xxx",
|
||||
},
|
||||
Name: "john",
|
||||
Product: "goframe",
|
||||
Region: "cd",
|
||||
SetMap: map[string]*SetSpecInfo{
|
||||
"test1": {
|
||||
StorageType: "ssd",
|
||||
Shards: 2,
|
||||
Params: []string{"a", "b", "c"},
|
||||
},
|
||||
"test2": {
|
||||
StorageType: "hssd",
|
||||
Shards: 10,
|
||||
Params: []string{},
|
||||
},
|
||||
},
|
||||
SetSlice: []SetSpecInfo{
|
||||
{
|
||||
StorageType: "hssd",
|
||||
Shards: 10,
|
||||
Params: []string{"h"},
|
||||
},
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gutil.DumpBrief(map[int]int{
|
||||
100: 100,
|
||||
})
|
||||
gutil.DumpBrief(req)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user