summaryrefslogtreecommitdiff
path: root/spew/format.go
diff options
context:
space:
mode:
Diffstat (limited to 'spew/format.go')
-rw-r--r--spew/format.go338
1 files changed, 338 insertions, 0 deletions
diff --git a/spew/format.go b/spew/format.go
new file mode 100644
index 0000000..cc27180
--- /dev/null
+++ b/spew/format.go
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2013 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{}
+ buffer bytes.Buffer
+ depth int
+ pointers map[uintptr]int // Holds map of points and depth they were seen at
+ fs fmt.State
+}
+
+// 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
+}
+
+// formatPtr handles formatting of pointers by indirecting them as necessary.
+func (f *formatState) formatPtr(v reflect.Value) {
+ // Display nil if top level poiner is nil.
+ if v.IsNil() {
+ f.buffer.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)
+ }
+ }
+
+ plusSyntax := f.fs.Flag('+')
+
+ // 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 {
+ indirects++
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := f.pointers[addr]; ok && pd < f.depth {
+ cycleFound = true
+ break
+ }
+ f.pointers[addr] = f.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display indirection level.
+ f.buffer.Write(openAngleBytes)
+ f.buffer.WriteString(strings.Repeat("*", indirects))
+ f.buffer.Write(closeAngleBytes)
+
+ // Display pointer information depending on flags.
+ if plusSyntax && (len(pointerChain) > 0) {
+ f.buffer.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ f.buffer.Write(pointerChainBytes)
+ }
+ printHexPtr(&f.buffer, addr)
+ }
+ f.buffer.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ switch {
+ case nilFound == true:
+ f.buffer.Write(nilAngleBytes)
+
+ case cycleFound == true:
+ f.buffer.Write(circularShortBytes)
+
+ default:
+ 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) {
+ // Call error/Stringer interfaces if they exist and the handle methods
+ // flag is enabled.
+ kind := v.Kind()
+ if !Config.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(&f.buffer, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ f.buffer.Write(invalidAngleBytes)
+
+ case reflect.Bool:
+ printBool(&f.buffer, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(&f.buffer, v.Int())
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(&f.buffer, v.Uint())
+
+ case reflect.Float32:
+ printFloat(&f.buffer, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(&f.buffer, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(&f.buffer, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(&f.buffer, v.Complex(), 64)
+
+ case reflect.Array, reflect.Slice:
+ f.buffer.WriteRune('[')
+ f.depth++
+ if (Config.MaxDepth != 0) && (f.depth > Config.MaxDepth) {
+ f.buffer.Write(maxShortBytes)
+ } else {
+ numEntries := v.Len()
+ for i := 0; i < numEntries; i++ {
+ if i > 0 {
+ f.buffer.WriteRune(' ')
+ }
+ f.format(unpackValue(v.Index(i)))
+ }
+ }
+ f.depth--
+ f.buffer.WriteRune(']')
+
+ case reflect.String:
+ f.buffer.WriteString(v.String())
+
+ case reflect.Interface:
+ // Do nothing. We should never get here due to unpackValue calls
+
+ case reflect.Map:
+ f.buffer.Write(openMapBytes)
+ f.depth++
+ if (Config.MaxDepth != 0) && (f.depth > Config.MaxDepth) {
+ f.buffer.Write(maxShortBytes)
+ } else {
+ keys := v.MapKeys()
+ for i, key := range keys {
+ if i > 0 {
+ f.buffer.WriteRune(' ')
+ }
+ f.format(unpackValue(key))
+ f.buffer.WriteRune(':')
+ f.format(unpackValue(v.MapIndex(key)))
+ }
+ }
+ f.depth--
+ f.buffer.Write(closeMapBytes)
+
+ case reflect.Ptr:
+ f.formatPtr(v)
+
+ case reflect.Struct:
+ numFields := v.NumField()
+ f.buffer.WriteRune('{')
+ f.depth++
+ if (Config.MaxDepth != 0) && (f.depth > Config.MaxDepth) {
+ f.buffer.Write(maxShortBytes)
+ } else {
+ vt := v.Type()
+ for i := 0; i < numFields; i++ {
+ if i > 0 {
+ f.buffer.WriteRune(' ')
+ }
+ vtf := vt.Field(i)
+ if f.fs.Flag('+') {
+ f.buffer.WriteString(vtf.Name)
+ f.buffer.WriteRune(':')
+ }
+ f.format(unpackValue(v.Field(i)))
+ }
+ }
+ f.depth--
+ f.buffer.WriteRune('}')
+
+ case reflect.Uintptr:
+ printHexPtr(&f.buffer, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(&f.buffer, 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() {
+ f.buffer.WriteString(fmt.Sprintf(format, v.Interface()))
+ } else {
+ f.buffer.WriteString(fmt.Sprintf(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 or #v.
+ if (verb != 'v') || (verb == 'v' && fs.Flag('#')) {
+ format := f.constructOrigFormat(verb)
+ fmt.Fprintf(fs, format, f.value)
+ return
+ }
+
+ if f.value == nil {
+ fmt.Fprint(fs, string(nilAngleBytes))
+ return
+ }
+
+ f.format(reflect.ValueOf(f.value))
+ f.buffer.WriteTo(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 and %+v verb combinations. Any
+other variations such as %x, %q, and %#v 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 is to call one of the convenience functions such as
+Printf, Println, or Printf.
+*/
+func NewFormatter(v interface{}) (f fmt.Formatter) {
+ fs := &formatState{value: v}
+ fs.pointers = make(map[uintptr]int)
+ return fs
+}