-
Notifications
You must be signed in to change notification settings - Fork 175
/
display.go
113 lines (108 loc) · 2.97 KB
/
display.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
// ex12.2 displays arbitrary values to a certain depth.
package display
import (
"bytes"
"fmt"
"reflect"
"strconv"
)
func Display(name string, x interface{}) {
fmt.Printf("Display %s (%T):\n", name, x)
display(name, reflect.ValueOf(x), 0)
}
// formatAtom formats a value without inspecting its internal structure.
// It is a copy of the the function in gopl.io/ch11/format.
func formatAtom(v reflect.Value) string {
switch v.Kind() {
case reflect.Invalid:
return "invalid"
case reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64:
return strconv.FormatInt(v.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(v.Uint(), 10)
// ...floating-point and complex cases omitted for brevity...
case reflect.Bool:
if v.Bool() {
return "true"
}
return "false"
case reflect.String:
return strconv.Quote(v.String())
case reflect.Chan, reflect.Func, reflect.Ptr,
reflect.Slice, reflect.Map:
return v.Type().String() + " 0x" +
strconv.FormatUint(uint64(v.Pointer()), 16)
default: // reflect.Array, reflect.Struct, reflect.Interface
return v.Type().String() + " value"
}
}
// formatMapKey includes special behaviour for struct and array values to
// format one level down, but defers to formatAtom for other types.
func formatMapKey(v reflect.Value) string {
switch v.Kind() {
case reflect.Struct:
b := &bytes.Buffer{}
b.WriteByte('{')
for i := 0; i < v.NumField(); i++ {
if i != 0 {
b.WriteString(", ")
}
fmt.Fprintf(b, "%s: %s", v.Type().Field(i).Name, formatAtom(v.Field(i)))
}
b.WriteByte('}')
return b.String()
case reflect.Array:
b := &bytes.Buffer{}
b.WriteByte('{')
for i := 0; i < v.Len(); i++ {
if i != 0 {
b.WriteString(", ")
}
b.WriteString(formatAtom(v.Index(i)))
}
b.WriteByte('}')
return b.String()
default:
return formatAtom(v)
}
}
func display(path string, v reflect.Value, level int) {
if level > 5 {
fmt.Printf("%s = %s\n", path, formatAtom(v))
return
}
switch v.Kind() {
case reflect.Invalid:
fmt.Printf("%s = invalid\n", path)
case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ {
display(fmt.Sprintf("%s[%d]", path, i), v.Index(i), level+1)
}
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
display(fieldPath, v.Field(i), level+1)
}
case reflect.Map:
for _, key := range v.MapKeys() {
display(fmt.Sprintf("%s[%s]", path, formatMapKey(key)), v.MapIndex(key), level+1)
}
case reflect.Ptr:
if v.IsNil() {
fmt.Printf("%s = nil\n", path)
} else {
display(fmt.Sprintf("(*%s)", path), v.Elem(), level+1)
}
case reflect.Interface:
if v.IsNil() {
fmt.Printf("%s = nil\n", path)
} else {
fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
display(path+".value", v.Elem(), level+1)
}
default: // basic types, channels, funcs
fmt.Printf("%s = %s\n", path, formatAtom(v))
}
}