]>
Commit | Line | Data |
---|---|---|
1 | // Copyright 2017, The Go Authors. All rights reserved. | |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE.md file. | |
4 | ||
5 | // Package value provides functionality for reflect.Value types. | |
6 | package value | |
7 | ||
8 | import ( | |
9 | "fmt" | |
10 | "reflect" | |
11 | "strconv" | |
12 | "strings" | |
13 | "unicode" | |
14 | ) | |
15 | ||
16 | var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() | |
17 | ||
18 | // Format formats the value v as a string. | |
19 | // | |
20 | // This is similar to fmt.Sprintf("%+v", v) except this: | |
21 | // * Prints the type unless it can be elided | |
22 | // * Avoids printing struct fields that are zero | |
23 | // * Prints a nil-slice as being nil, not empty | |
24 | // * Prints map entries in deterministic order | |
25 | func Format(v reflect.Value, conf FormatConfig) string { | |
26 | conf.printType = true | |
27 | conf.followPointers = true | |
28 | conf.realPointers = true | |
29 | return formatAny(v, conf, nil) | |
30 | } | |
31 | ||
32 | type FormatConfig struct { | |
33 | UseStringer bool // Should the String method be used if available? | |
34 | printType bool // Should we print the type before the value? | |
35 | PrintPrimitiveType bool // Should we print the type of primitives? | |
36 | followPointers bool // Should we recursively follow pointers? | |
37 | realPointers bool // Should we print the real address of pointers? | |
38 | } | |
39 | ||
40 | func formatAny(v reflect.Value, conf FormatConfig, visited map[uintptr]bool) string { | |
41 | // TODO: Should this be a multi-line printout in certain situations? | |
42 | ||
43 | if !v.IsValid() { | |
44 | return "<non-existent>" | |
45 | } | |
46 | if conf.UseStringer && v.Type().Implements(stringerIface) && v.CanInterface() { | |
47 | if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() { | |
48 | return "<nil>" | |
49 | } | |
50 | ||
51 | const stringerPrefix = "s" // Indicates that the String method was used | |
52 | s := v.Interface().(fmt.Stringer).String() | |
53 | return stringerPrefix + formatString(s) | |
54 | } | |
55 | ||
56 | switch v.Kind() { | |
57 | case reflect.Bool: | |
58 | return formatPrimitive(v.Type(), v.Bool(), conf) | |
59 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
60 | return formatPrimitive(v.Type(), v.Int(), conf) | |
61 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |
62 | if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr { | |
63 | // Unnamed uints are usually bytes or words, so use hexadecimal. | |
64 | return formatPrimitive(v.Type(), formatHex(v.Uint()), conf) | |
65 | } | |
66 | return formatPrimitive(v.Type(), v.Uint(), conf) | |
67 | case reflect.Float32, reflect.Float64: | |
68 | return formatPrimitive(v.Type(), v.Float(), conf) | |
69 | case reflect.Complex64, reflect.Complex128: | |
70 | return formatPrimitive(v.Type(), v.Complex(), conf) | |
71 | case reflect.String: | |
72 | return formatPrimitive(v.Type(), formatString(v.String()), conf) | |
73 | case reflect.UnsafePointer, reflect.Chan, reflect.Func: | |
74 | return formatPointer(v, conf) | |
75 | case reflect.Ptr: | |
76 | if v.IsNil() { | |
77 | if conf.printType { | |
78 | return fmt.Sprintf("(%v)(nil)", v.Type()) | |
79 | } | |
80 | return "<nil>" | |
81 | } | |
82 | if visited[v.Pointer()] || !conf.followPointers { | |
83 | return formatPointer(v, conf) | |
84 | } | |
85 | visited = insertPointer(visited, v.Pointer()) | |
86 | return "&" + formatAny(v.Elem(), conf, visited) | |
87 | case reflect.Interface: | |
88 | if v.IsNil() { | |
89 | if conf.printType { | |
90 | return fmt.Sprintf("%v(nil)", v.Type()) | |
91 | } | |
92 | return "<nil>" | |
93 | } | |
94 | return formatAny(v.Elem(), conf, visited) | |
95 | case reflect.Slice: | |
96 | if v.IsNil() { | |
97 | if conf.printType { | |
98 | return fmt.Sprintf("%v(nil)", v.Type()) | |
99 | } | |
100 | return "<nil>" | |
101 | } | |
102 | if visited[v.Pointer()] { | |
103 | return formatPointer(v, conf) | |
104 | } | |
105 | visited = insertPointer(visited, v.Pointer()) | |
106 | fallthrough | |
107 | case reflect.Array: | |
108 | var ss []string | |
109 | subConf := conf | |
110 | subConf.printType = v.Type().Elem().Kind() == reflect.Interface | |
111 | for i := 0; i < v.Len(); i++ { | |
112 | s := formatAny(v.Index(i), subConf, visited) | |
113 | ss = append(ss, s) | |
114 | } | |
115 | s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) | |
116 | if conf.printType { | |
117 | return v.Type().String() + s | |
118 | } | |
119 | return s | |
120 | case reflect.Map: | |
121 | if v.IsNil() { | |
122 | if conf.printType { | |
123 | return fmt.Sprintf("%v(nil)", v.Type()) | |
124 | } | |
125 | return "<nil>" | |
126 | } | |
127 | if visited[v.Pointer()] { | |
128 | return formatPointer(v, conf) | |
129 | } | |
130 | visited = insertPointer(visited, v.Pointer()) | |
131 | ||
132 | var ss []string | |
133 | keyConf, valConf := conf, conf | |
134 | keyConf.printType = v.Type().Key().Kind() == reflect.Interface | |
135 | keyConf.followPointers = false | |
136 | valConf.printType = v.Type().Elem().Kind() == reflect.Interface | |
137 | for _, k := range SortKeys(v.MapKeys()) { | |
138 | sk := formatAny(k, keyConf, visited) | |
139 | sv := formatAny(v.MapIndex(k), valConf, visited) | |
140 | ss = append(ss, fmt.Sprintf("%s: %s", sk, sv)) | |
141 | } | |
142 | s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) | |
143 | if conf.printType { | |
144 | return v.Type().String() + s | |
145 | } | |
146 | return s | |
147 | case reflect.Struct: | |
148 | var ss []string | |
149 | subConf := conf | |
150 | subConf.printType = true | |
151 | for i := 0; i < v.NumField(); i++ { | |
152 | vv := v.Field(i) | |
153 | if isZero(vv) { | |
154 | continue // Elide zero value fields | |
155 | } | |
156 | name := v.Type().Field(i).Name | |
157 | subConf.UseStringer = conf.UseStringer | |
158 | s := formatAny(vv, subConf, visited) | |
159 | ss = append(ss, fmt.Sprintf("%s: %s", name, s)) | |
160 | } | |
161 | s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) | |
162 | if conf.printType { | |
163 | return v.Type().String() + s | |
164 | } | |
165 | return s | |
166 | default: | |
167 | panic(fmt.Sprintf("%v kind not handled", v.Kind())) | |
168 | } | |
169 | } | |
170 | ||
171 | func formatString(s string) string { | |
172 | // Use quoted string if it the same length as a raw string literal. | |
173 | // Otherwise, attempt to use the raw string form. | |
174 | qs := strconv.Quote(s) | |
175 | if len(qs) == 1+len(s)+1 { | |
176 | return qs | |
177 | } | |
178 | ||
179 | // Disallow newlines to ensure output is a single line. | |
180 | // Only allow printable runes for readability purposes. | |
181 | rawInvalid := func(r rune) bool { | |
182 | return r == '`' || r == '\n' || !unicode.IsPrint(r) | |
183 | } | |
184 | if strings.IndexFunc(s, rawInvalid) < 0 { | |
185 | return "`" + s + "`" | |
186 | } | |
187 | return qs | |
188 | } | |
189 | ||
190 | func formatPrimitive(t reflect.Type, v interface{}, conf FormatConfig) string { | |
191 | if conf.printType && (conf.PrintPrimitiveType || t.PkgPath() != "") { | |
192 | return fmt.Sprintf("%v(%v)", t, v) | |
193 | } | |
194 | return fmt.Sprintf("%v", v) | |
195 | } | |
196 | ||
197 | func formatPointer(v reflect.Value, conf FormatConfig) string { | |
198 | p := v.Pointer() | |
199 | if !conf.realPointers { | |
200 | p = 0 // For deterministic printing purposes | |
201 | } | |
202 | s := formatHex(uint64(p)) | |
203 | if conf.printType { | |
204 | return fmt.Sprintf("(%v)(%s)", v.Type(), s) | |
205 | } | |
206 | return s | |
207 | } | |
208 | ||
209 | func formatHex(u uint64) string { | |
210 | var f string | |
211 | switch { | |
212 | case u <= 0xff: | |
213 | f = "0x%02x" | |
214 | case u <= 0xffff: | |
215 | f = "0x%04x" | |
216 | case u <= 0xffffff: | |
217 | f = "0x%06x" | |
218 | case u <= 0xffffffff: | |
219 | f = "0x%08x" | |
220 | case u <= 0xffffffffff: | |
221 | f = "0x%010x" | |
222 | case u <= 0xffffffffffff: | |
223 | f = "0x%012x" | |
224 | case u <= 0xffffffffffffff: | |
225 | f = "0x%014x" | |
226 | case u <= 0xffffffffffffffff: | |
227 | f = "0x%016x" | |
228 | } | |
229 | return fmt.Sprintf(f, u) | |
230 | } | |
231 | ||
232 | // insertPointer insert p into m, allocating m if necessary. | |
233 | func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool { | |
234 | if m == nil { | |
235 | m = make(map[uintptr]bool) | |
236 | } | |
237 | m[p] = true | |
238 | return m | |
239 | } | |
240 | ||
241 | // isZero reports whether v is the zero value. | |
242 | // This does not rely on Interface and so can be used on unexported fields. | |
243 | func isZero(v reflect.Value) bool { | |
244 | switch v.Kind() { | |
245 | case reflect.Bool: | |
246 | return v.Bool() == false | |
247 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |
248 | return v.Int() == 0 | |
249 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |
250 | return v.Uint() == 0 | |
251 | case reflect.Float32, reflect.Float64: | |
252 | return v.Float() == 0 | |
253 | case reflect.Complex64, reflect.Complex128: | |
254 | return v.Complex() == 0 | |
255 | case reflect.String: | |
256 | return v.String() == "" | |
257 | case reflect.UnsafePointer: | |
258 | return v.Pointer() == 0 | |
259 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: | |
260 | return v.IsNil() | |
261 | case reflect.Array: | |
262 | for i := 0; i < v.Len(); i++ { | |
263 | if !isZero(v.Index(i)) { | |
264 | return false | |
265 | } | |
266 | } | |
267 | return true | |
268 | case reflect.Struct: | |
269 | for i := 0; i < v.NumField(); i++ { | |
270 | if !isZero(v.Field(i)) { | |
271 | return false | |
272 | } | |
273 | } | |
274 | return true | |
275 | } | |
276 | return false | |
277 | } |