-
Notifications
You must be signed in to change notification settings - Fork 43
/
debug.go
116 lines (108 loc) · 3.11 KB
/
debug.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package utils
import (
"fmt"
"reflect"
"sort"
"strings"
"unsafe"
)
func SafeReflectValue(refV reflect.Value) reflect.Value {
if !refV.CanAddr() {
newRefV := reflect.New(refV.Type()).Elem()
newRefV.Set(refV)
refV = newRefV
}
return refV
}
func SafeReflectStructField(refV reflect.Value, field reflect.Value) reflect.Value {
var f reflect.Value
if refV.CanAddr() {
f = reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
}
return f
}
func AsDebugString(i interface{}, raws ...bool) string {
raw := false
if len(raws) > 0 && raws[0] {
raw = true
}
return asDebugString(i, raw, make(map[uintptr]struct{}))
}
func asDebugString(i interface{}, raw bool, pointers map[uintptr]struct{}) (ret string) {
refV := reflect.ValueOf(i)
if !refV.IsValid() {
return fmt.Sprintf("%#v", i)
}
typ := refV.Type()
kind := typ.Kind()
// todo: 美化输出,主要使用省略号来省略过长的字符串
_ = raw
if !refV.IsValid() {
return fmt.Sprintf("%#v", i)
}
if kind == reflect.Array || kind == reflect.Slice {
length := refV.Len()
if length > 0 {
elemKind := refV.Index(0).Kind()
if elemKind == reflect.Uint8 || elemKind == reflect.Int32 { // []byte or []rune
return fmt.Sprintf("%q", i)
}
}
content := make([]string, length)
for i := 0; i < length; i++ {
content[i] = asDebugString(refV.Index(i).Interface(), raw, pointers)
}
return fmt.Sprintf("%T{%s}", i, strings.Join(content, ", "))
} else if kind == reflect.String {
return fmt.Sprintf("%q", i)
} else if kind == reflect.Map {
content := make([]string, refV.Len())
keys := refV.MapKeys()
sort.SliceStable(keys, func(i, j int) bool {
return fmt.Sprintf("%v", keys[i]) < fmt.Sprintf("%v", keys[j])
})
for i, key := range keys {
content[i] = fmt.Sprintf("%s: %s", asDebugString(key.Interface(), raw, pointers), asDebugString(refV.MapIndex(key).Interface(), raw, pointers))
}
return fmt.Sprintf("%T{%s}", i, strings.Join(content, ", "))
} else if f, ok := i.(fmt.Stringer); ok {
return fmt.Sprintf("%s", f.String())
} else if kind == reflect.Ptr {
// fix circle call
p := refV.Pointer()
if _, ok := pointers[p]; ok {
return "<Already printed>"
} else {
pointers[p] = struct{}{}
}
elem := refV.Elem()
if elem.IsValid() {
return fmt.Sprintf("&%s", asDebugString(elem.Interface(), raw, pointers))
} else {
return fmt.Sprintf("%#v", i)
}
} else if kind == reflect.Struct {
content := make([]string, refV.NumField())
if !refV.CanAddr() {
newRefV := reflect.New(refV.Type()).Elem()
newRefV.Set(refV)
refV = newRefV
}
for i := 0; i < refV.NumField(); i++ {
field := refV.Field(i)
fieldStr := "<Unavailable>"
newField := SafeReflectStructField(refV, field)
iField := newField.Interface()
if iField != nil {
fieldStr = asDebugString(iField, raw, pointers)
}
content[i] = fmt.Sprintf("%s: %s", typ.Field(i).Name, fieldStr)
}
return fmt.Sprintf("%T{%s}", i, strings.Join(content, ", "))
} else if refV.CanInt() || refV.CanUint() {
return fmt.Sprintf("%d", i)
} else if refV.CanFloat() {
return fmt.Sprintf("%f", i)
}
return fmt.Sprintf("%#v", i)
}