You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
419 lines
11 KiB
419 lines
11 KiB
/* |
|
* Copyright (c) 2013-2016 Dave Collins <[email protected]> |
|
* |
|
* Permission to use, copy, modify, and distribute this software for any |
|
* purpose with or without fee is hereby granted, provided that the above |
|
* copyright notice and this permission notice appear in all copies. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
*/ |
|
|
|
package spew |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
"reflect" |
|
"strconv" |
|
"strings" |
|
) |
|
|
|
// supportedFlags is a list of all the character flags supported by fmt package. |
|
const supportedFlags = "0-+# " |
|
|
|
// formatState implements the fmt.Formatter interface and contains information |
|
// about the state of a formatting operation. The NewFormatter function can |
|
// be used to get a new Formatter which can be used directly as arguments |
|
// in standard fmt package printing calls. |
|
type formatState struct { |
|
value interface{} |
|
fs fmt.State |
|
depth int |
|
pointers map[uintptr]int |
|
ignoreNextType bool |
|
cs *ConfigState |
|
} |
|
|
|
// buildDefaultFormat recreates the original format string without precision |
|
// and width information to pass in to fmt.Sprintf in the case of an |
|
// unrecognized type. Unless new types are added to the language, this |
|
// function won't ever be called. |
|
func (f *formatState) buildDefaultFormat() (format string) { |
|
buf := bytes.NewBuffer(percentBytes) |
|
|
|
for _, flag := range supportedFlags { |
|
if f.fs.Flag(int(flag)) { |
|
buf.WriteRune(flag) |
|
} |
|
} |
|
|
|
buf.WriteRune('v') |
|
|
|
format = buf.String() |
|
return format |
|
} |
|
|
|
// constructOrigFormat recreates the original format string including precision |
|
// and width information to pass along to the standard fmt package. This allows |
|
// automatic deferral of all format strings this package doesn't support. |
|
func (f *formatState) constructOrigFormat(verb rune) (format string) { |
|
buf := bytes.NewBuffer(percentBytes) |
|
|
|
for _, flag := range supportedFlags { |
|
if f.fs.Flag(int(flag)) { |
|
buf.WriteRune(flag) |
|
} |
|
} |
|
|
|
if width, ok := f.fs.Width(); ok { |
|
buf.WriteString(strconv.Itoa(width)) |
|
} |
|
|
|
if precision, ok := f.fs.Precision(); ok { |
|
buf.Write(precisionBytes) |
|
buf.WriteString(strconv.Itoa(precision)) |
|
} |
|
|
|
buf.WriteRune(verb) |
|
|
|
format = buf.String() |
|
return format |
|
} |
|
|
|
// unpackValue returns values inside of non-nil interfaces when possible and |
|
// ensures that types for values which have been unpacked from an interface |
|
// are displayed when the show types flag is also set. |
|
// This is useful for data types like structs, arrays, slices, and maps which |
|
// can contain varying types packed inside an interface. |
|
func (f *formatState) unpackValue(v reflect.Value) reflect.Value { |
|
if v.Kind() == reflect.Interface { |
|
f.ignoreNextType = false |
|
if !v.IsNil() { |
|
v = v.Elem() |
|
} |
|
} |
|
return v |
|
} |
|
|
|
// formatPtr handles formatting of pointers by indirecting them as necessary. |
|
func (f *formatState) formatPtr(v reflect.Value) { |
|
// Display nil if top level pointer is nil. |
|
showTypes := f.fs.Flag('#') |
|
if v.IsNil() && (!showTypes || f.ignoreNextType) { |
|
f.fs.Write(nilAngleBytes) |
|
return |
|
} |
|
|
|
// Remove pointers at or below the current depth from map used to detect |
|
// circular refs. |
|
for k, depth := range f.pointers { |
|
if depth >= f.depth { |
|
delete(f.pointers, k) |
|
} |
|
} |
|
|
|
// Keep list of all dereferenced pointers to possibly show later. |
|
pointerChain := make([]uintptr, 0) |
|
|
|
// Figure out how many levels of indirection there are by derferencing |
|
// pointers and unpacking interfaces down the chain while detecting circular |
|
// references. |
|
nilFound := false |
|
cycleFound := false |
|
indirects := 0 |
|
ve := v |
|
for ve.Kind() == reflect.Ptr { |
|
if ve.IsNil() { |
|
nilFound = true |
|
break |
|
} |
|
indirects++ |
|
addr := ve.Pointer() |
|
pointerChain = append(pointerChain, addr) |
|
if pd, ok := f.pointers[addr]; ok && pd < f.depth { |
|
cycleFound = true |
|
indirects-- |
|
break |
|
} |
|
f.pointers[addr] = f.depth |
|
|
|
ve = ve.Elem() |
|
if ve.Kind() == reflect.Interface { |
|
if ve.IsNil() { |
|
nilFound = true |
|
break |
|
} |
|
ve = ve.Elem() |
|
} |
|
} |
|
|
|
// Display type or indirection level depending on flags. |
|
if showTypes && !f.ignoreNextType { |
|
f.fs.Write(openParenBytes) |
|
f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) |
|
f.fs.Write([]byte(ve.Type().String())) |
|
f.fs.Write(closeParenBytes) |
|
} else { |
|
if nilFound || cycleFound { |
|
indirects += strings.Count(ve.Type().String(), "*") |
|
} |
|
f.fs.Write(openAngleBytes) |
|
f.fs.Write([]byte(strings.Repeat("*", indirects))) |
|
f.fs.Write(closeAngleBytes) |
|
} |
|
|
|
// Display pointer information depending on flags. |
|
if f.fs.Flag('+') && (len(pointerChain) > 0) { |
|
f.fs.Write(openParenBytes) |
|
for i, addr := range pointerChain { |
|
if i > 0 { |
|
f.fs.Write(pointerChainBytes) |
|
} |
|
printHexPtr(f.fs, addr) |
|
} |
|
f.fs.Write(closeParenBytes) |
|
} |
|
|
|
// Display dereferenced value. |
|
switch { |
|
case nilFound: |
|
f.fs.Write(nilAngleBytes) |
|
|
|
case cycleFound: |
|
f.fs.Write(circularShortBytes) |
|
|
|
default: |
|
f.ignoreNextType = true |
|
f.format(ve) |
|
} |
|
} |
|
|
|
// format is the main workhorse for providing the Formatter interface. It |
|
// uses the passed reflect value to figure out what kind of object we are |
|
// dealing with and formats it appropriately. It is a recursive function, |
|
// however circular data structures are detected and handled properly. |
|
func (f *formatState) format(v reflect.Value) { |
|
// Handle invalid reflect values immediately. |
|
kind := v.Kind() |
|
if kind == reflect.Invalid { |
|
f.fs.Write(invalidAngleBytes) |
|
return |
|
} |
|
|
|
// Handle pointers specially. |
|
if kind == reflect.Ptr { |
|
f.formatPtr(v) |
|
return |
|
} |
|
|
|
// Print type information unless already handled elsewhere. |
|
if !f.ignoreNextType && f.fs.Flag('#') { |
|
f.fs.Write(openParenBytes) |
|
f.fs.Write([]byte(v.Type().String())) |
|
f.fs.Write(closeParenBytes) |
|
} |
|
f.ignoreNextType = false |
|
|
|
// Call Stringer/error interfaces if they exist and the handle methods |
|
// flag is enabled. |
|
if !f.cs.DisableMethods { |
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) { |
|
if handled := handleMethods(f.cs, f.fs, v); handled { |
|
return |
|
} |
|
} |
|
} |
|
|
|
switch kind { |
|
case reflect.Invalid: |
|
// Do nothing. We should never get here since invalid has already |
|
// been handled above. |
|
|
|
case reflect.Bool: |
|
printBool(f.fs, v.Bool()) |
|
|
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: |
|
printInt(f.fs, v.Int(), 10) |
|
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: |
|
printUint(f.fs, v.Uint(), 10) |
|
|
|
case reflect.Float32: |
|
printFloat(f.fs, v.Float(), 32) |
|
|
|
case reflect.Float64: |
|
printFloat(f.fs, v.Float(), 64) |
|
|
|
case reflect.Complex64: |
|
printComplex(f.fs, v.Complex(), 32) |
|
|
|
case reflect.Complex128: |
|
printComplex(f.fs, v.Complex(), 64) |
|
|
|
case reflect.Slice: |
|
if v.IsNil() { |
|
f.fs.Write(nilAngleBytes) |
|
break |
|
} |
|
fallthrough |
|
|
|
case reflect.Array: |
|
f.fs.Write(openBracketBytes) |
|
f.depth++ |
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { |
|
f.fs.Write(maxShortBytes) |
|
} else { |
|
numEntries := v.Len() |
|
for i := 0; i < numEntries; i++ { |
|
if i > 0 { |
|
f.fs.Write(spaceBytes) |
|
} |
|
f.ignoreNextType = true |
|
f.format(f.unpackValue(v.Index(i))) |
|
} |
|
} |
|
f.depth-- |
|
f.fs.Write(closeBracketBytes) |
|
|
|
case reflect.String: |
|
f.fs.Write([]byte(v.String())) |
|
|
|
case reflect.Interface: |
|
// The only time we should get here is for nil interfaces due to |
|
// unpackValue calls. |
|
if v.IsNil() { |
|
f.fs.Write(nilAngleBytes) |
|
} |
|
|
|
case reflect.Ptr: |
|
// Do nothing. We should never get here since pointers have already |
|
// been handled above. |
|
|
|
case reflect.Map: |
|
// nil maps should be indicated as different than empty maps |
|
if v.IsNil() { |
|
f.fs.Write(nilAngleBytes) |
|
break |
|
} |
|
|
|
f.fs.Write(openMapBytes) |
|
f.depth++ |
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { |
|
f.fs.Write(maxShortBytes) |
|
} else { |
|
keys := v.MapKeys() |
|
if f.cs.SortKeys { |
|
sortValues(keys, f.cs) |
|
} |
|
for i, key := range keys { |
|
if i > 0 { |
|
f.fs.Write(spaceBytes) |
|
} |
|
f.ignoreNextType = true |
|
f.format(f.unpackValue(key)) |
|
f.fs.Write(colonBytes) |
|
f.ignoreNextType = true |
|
f.format(f.unpackValue(v.MapIndex(key))) |
|
} |
|
} |
|
f.depth-- |
|
f.fs.Write(closeMapBytes) |
|
|
|
case reflect.Struct: |
|
numFields := v.NumField() |
|
f.fs.Write(openBraceBytes) |
|
f.depth++ |
|
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { |
|
f.fs.Write(maxShortBytes) |
|
} else { |
|
vt := v.Type() |
|
for i := 0; i < numFields; i++ { |
|
if i > 0 { |
|
f.fs.Write(spaceBytes) |
|
} |
|
vtf := vt.Field(i) |
|
if f.fs.Flag('+') || f.fs.Flag('#') { |
|
f.fs.Write([]byte(vtf.Name)) |
|
f.fs.Write(colonBytes) |
|
} |
|
f.format(f.unpackValue(v.Field(i))) |
|
} |
|
} |
|
f.depth-- |
|
f.fs.Write(closeBraceBytes) |
|
|
|
case reflect.Uintptr: |
|
printHexPtr(f.fs, uintptr(v.Uint())) |
|
|
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func: |
|
printHexPtr(f.fs, v.Pointer()) |
|
|
|
// There were not any other types at the time this code was written, but |
|
// fall back to letting the default fmt package handle it if any get added. |
|
default: |
|
format := f.buildDefaultFormat() |
|
if v.CanInterface() { |
|
fmt.Fprintf(f.fs, format, v.Interface()) |
|
} else { |
|
fmt.Fprintf(f.fs, format, v.String()) |
|
} |
|
} |
|
} |
|
|
|
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage |
|
// details. |
|
func (f *formatState) Format(fs fmt.State, verb rune) { |
|
f.fs = fs |
|
|
|
// Use standard formatting for verbs that are not v. |
|
if verb != 'v' { |
|
format := f.constructOrigFormat(verb) |
|
fmt.Fprintf(fs, format, f.value) |
|
return |
|
} |
|
|
|
if f.value == nil { |
|
if fs.Flag('#') { |
|
fs.Write(interfaceBytes) |
|
} |
|
fs.Write(nilAngleBytes) |
|
return |
|
} |
|
|
|
f.format(reflect.ValueOf(f.value)) |
|
} |
|
|
|
// newFormatter is a helper function to consolidate the logic from the various |
|
// public methods which take varying config states. |
|
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { |
|
fs := &formatState{value: v, cs: cs} |
|
fs.pointers = make(map[uintptr]int) |
|
return fs |
|
} |
|
|
|
/* |
|
NewFormatter returns a custom formatter that satisfies the fmt.Formatter |
|
interface. As a result, it integrates cleanly with standard fmt package |
|
printing functions. The formatter is useful for inline printing of smaller data |
|
types similar to the standard %v format specifier. |
|
|
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer |
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb |
|
combinations. Any other verbs such as %x and %q will be sent to the the |
|
standard fmt package for formatting. In addition, the custom formatter ignores |
|
the width and precision arguments (however they will still work on the format |
|
specifiers not handled by the custom formatter). |
|
|
|
Typically this function shouldn't be called directly. It is much easier to make |
|
use of the custom formatter by calling one of the convenience functions such as |
|
Printf, Println, or Fprintf. |
|
*/ |
|
func NewFormatter(v interface{}) fmt.Formatter { |
|
return newFormatter(&Config, v) |
|
}
|
|
|