...
1 package ast
2
3 import (
4 "bytes"
5 "fmt"
6 "reflect"
7 "strconv"
8 "strings"
9 )
10
11
12 func Dump(i interface{}) string {
13 v := reflect.ValueOf(i)
14
15 d := dumper{Buffer: &bytes.Buffer{}}
16 d.dump(v)
17
18 return d.String()
19 }
20
21 type dumper struct {
22 *bytes.Buffer
23 indent int
24 }
25
26 type Dumpable interface {
27 Dump() string
28 }
29
30 func (d *dumper) dump(v reflect.Value) {
31 if dumpable, isDumpable := v.Interface().(Dumpable); isDumpable {
32 d.WriteString(dumpable.Dump())
33 return
34 }
35 switch v.Kind() {
36 case reflect.Bool:
37 if v.Bool() {
38 d.WriteString("true")
39 } else {
40 d.WriteString("false")
41 }
42 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
43 d.WriteString(fmt.Sprintf("%d", v.Int()))
44
45 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
46 d.WriteString(fmt.Sprintf("%d", v.Uint()))
47
48 case reflect.Float32, reflect.Float64:
49 d.WriteString(fmt.Sprintf("%.2f", v.Float()))
50
51 case reflect.String:
52 if v.Type().Name() != "string" {
53 d.WriteString(v.Type().Name() + "(" + strconv.Quote(v.String()) + ")")
54 } else {
55 d.WriteString(strconv.Quote(v.String()))
56 }
57
58 case reflect.Array, reflect.Slice:
59 d.dumpArray(v)
60
61 case reflect.Interface, reflect.Ptr:
62 d.dumpPtr(v)
63
64 case reflect.Struct:
65 d.dumpStruct(v)
66
67 default:
68 panic(fmt.Errorf("unsupported kind: %s\n buf: %s", v.Kind().String(), d.String()))
69 }
70 }
71
72 func (d *dumper) writeIndent() {
73 d.Buffer.WriteString(strings.Repeat(" ", d.indent))
74 }
75
76 func (d *dumper) nl() {
77 d.Buffer.WriteByte('\n')
78 d.writeIndent()
79 }
80
81 func typeName(t reflect.Type) string {
82 if t.Kind() == reflect.Ptr {
83 return typeName(t.Elem())
84 }
85 return t.Name()
86 }
87
88 func (d *dumper) dumpArray(v reflect.Value) {
89 d.WriteString("[" + typeName(v.Type().Elem()) + "]")
90
91 for i := 0; i < v.Len(); i++ {
92 d.nl()
93 d.WriteString("- ")
94 d.indent++
95 d.dump(v.Index(i))
96 d.indent--
97 }
98 }
99
100 func (d *dumper) dumpStruct(v reflect.Value) {
101 d.WriteString("<" + v.Type().Name() + ">")
102 d.indent++
103
104 typ := v.Type()
105 for i := 0; i < v.NumField(); i++ {
106 f := v.Field(i)
107 if typ.Field(i).Tag.Get("dump") == "-" {
108 continue
109 }
110
111 if isZero(f) {
112 continue
113 }
114 d.nl()
115 d.WriteString(typ.Field(i).Name)
116 d.WriteString(": ")
117 d.dump(v.Field(i))
118 }
119
120 d.indent--
121 }
122
123 func isZero(v reflect.Value) bool {
124 switch v.Kind() {
125 case reflect.Ptr, reflect.Interface:
126 return v.IsNil()
127 case reflect.Func, reflect.Map:
128 return v.IsNil()
129
130 case reflect.Array, reflect.Slice:
131 if v.IsNil() {
132 return true
133 }
134 z := true
135 for i := 0; i < v.Len(); i++ {
136 z = z && isZero(v.Index(i))
137 }
138 return z
139 case reflect.Struct:
140 z := true
141 for i := 0; i < v.NumField(); i++ {
142 z = z && isZero(v.Field(i))
143 }
144 return z
145 case reflect.String:
146 return v.String() == ""
147 }
148
149
150 return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()))
151 }
152
153 func (d *dumper) dumpPtr(v reflect.Value) {
154 if v.IsNil() {
155 d.WriteString("nil")
156 return
157 }
158 d.dump(v.Elem())
159 }
160
View as plain text