123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /*
- * Copyright (c) 2013 Dave Collins <dave@davec.name>
- *
- * 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 == true:
- f.fs.Write(nilAngleBytes)
- case cycleFound == true:
- 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)
- }
|