diff options
Diffstat (limited to 'spew/format.go')
| -rw-r--r-- | spew/format.go | 338 |
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 +} |
