format.go

  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.
  6package value
  7
  8import (
  9	"fmt"
 10	"reflect"
 11	"strconv"
 12	"strings"
 13	"unicode"
 14)
 15
 16var 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
 25func 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
 32type 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
 40func 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
171func 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
190func 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
197func 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
209func 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.
233func 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.
243func 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}