github.com/integration-system/go-cmp@v0.0.0-20190131081942-ac5582987a2f/cmp/internal/value/format.go (about)

     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.
     6  package value
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  	"unicode"
    14  )
    15  
    16  var 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
    25  func Format(v reflect.Value, conf FormatConfig) string {
    26  	conf.printType = true
    27  	conf.followPointers = true
    28  	conf.realPointers = true
    29  	return formatAny(v, conf, visited{})
    30  }
    31  
    32  type 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  
    40  func formatAny(v reflect.Value, conf FormatConfig, m visited) 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 m.Visit(v) || !conf.followPointers {
    83  			return formatPointer(v, conf)
    84  		}
    85  		return "&" + formatAny(v.Elem(), conf, m)
    86  	case reflect.Interface:
    87  		if v.IsNil() {
    88  			if conf.printType {
    89  				return fmt.Sprintf("%v(nil)", v.Type())
    90  			}
    91  			return "<nil>"
    92  		}
    93  		return formatAny(v.Elem(), conf, m)
    94  	case reflect.Slice:
    95  		if v.IsNil() {
    96  			if conf.printType {
    97  				return fmt.Sprintf("%v(nil)", v.Type())
    98  			}
    99  			return "<nil>"
   100  		}
   101  		fallthrough
   102  	case reflect.Array:
   103  		var ss []string
   104  		subConf := conf
   105  		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
   106  		for i := 0; i < v.Len(); i++ {
   107  			vi := v.Index(i)
   108  			if vi.CanAddr() { // Check for recursive elements
   109  				p := vi.Addr()
   110  				if m.Visit(p) {
   111  					subConf := conf
   112  					subConf.printType = true
   113  					ss = append(ss, "*"+formatPointer(p, subConf))
   114  					continue
   115  				}
   116  			}
   117  			ss = append(ss, formatAny(vi, subConf, m))
   118  		}
   119  		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
   120  		if conf.printType {
   121  			return v.Type().String() + s
   122  		}
   123  		return s
   124  	case reflect.Map:
   125  		if v.IsNil() {
   126  			if conf.printType {
   127  				return fmt.Sprintf("%v(nil)", v.Type())
   128  			}
   129  			return "<nil>"
   130  		}
   131  		if m.Visit(v) {
   132  			return formatPointer(v, conf)
   133  		}
   134  
   135  		var ss []string
   136  		keyConf, valConf := conf, conf
   137  		keyConf.printType = v.Type().Key().Kind() == reflect.Interface
   138  		keyConf.followPointers = false
   139  		valConf.printType = v.Type().Elem().Kind() == reflect.Interface
   140  		for _, k := range SortKeys(v.MapKeys()) {
   141  			sk := formatAny(k, keyConf, m)
   142  			sv := formatAny(v.MapIndex(k), valConf, m)
   143  			ss = append(ss, fmt.Sprintf("%s: %s", sk, sv))
   144  		}
   145  		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
   146  		if conf.printType {
   147  			return v.Type().String() + s
   148  		}
   149  		return s
   150  	case reflect.Struct:
   151  		var ss []string
   152  		subConf := conf
   153  		subConf.printType = true
   154  		for i := 0; i < v.NumField(); i++ {
   155  			vv := v.Field(i)
   156  			if isZero(vv) {
   157  				continue // Elide zero value fields
   158  			}
   159  			name := v.Type().Field(i).Name
   160  			subConf.UseStringer = conf.UseStringer
   161  			s := formatAny(vv, subConf, m)
   162  			ss = append(ss, fmt.Sprintf("%s: %s", name, s))
   163  		}
   164  		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
   165  		if conf.printType {
   166  			return v.Type().String() + s
   167  		}
   168  		return s
   169  	default:
   170  		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
   171  	}
   172  }
   173  
   174  func formatString(s string) string {
   175  	// Use quoted string if it the same length as a raw string literal.
   176  	// Otherwise, attempt to use the raw string form.
   177  	qs := strconv.Quote(s)
   178  	if len(qs) == 1+len(s)+1 {
   179  		return qs
   180  	}
   181  
   182  	// Disallow newlines to ensure output is a single line.
   183  	// Only allow printable runes for readability purposes.
   184  	rawInvalid := func(r rune) bool {
   185  		return r == '`' || r == '\n' || !unicode.IsPrint(r)
   186  	}
   187  	if strings.IndexFunc(s, rawInvalid) < 0 {
   188  		return "`" + s + "`"
   189  	}
   190  	return qs
   191  }
   192  
   193  func formatPrimitive(t reflect.Type, v interface{}, conf FormatConfig) string {
   194  	if conf.printType && (conf.PrintPrimitiveType || t.PkgPath() != "") {
   195  		return fmt.Sprintf("%v(%v)", t, v)
   196  	}
   197  	return fmt.Sprintf("%v", v)
   198  }
   199  
   200  func formatPointer(v reflect.Value, conf FormatConfig) string {
   201  	p := v.Pointer()
   202  	if !conf.realPointers {
   203  		p = 0 // For deterministic printing purposes
   204  	}
   205  	s := formatHex(uint64(p))
   206  	if conf.printType {
   207  		return fmt.Sprintf("(%v)(%s)", v.Type(), s)
   208  	}
   209  	return s
   210  }
   211  
   212  func formatHex(u uint64) string {
   213  	var f string
   214  	switch {
   215  	case u <= 0xff:
   216  		f = "0x%02x"
   217  	case u <= 0xffff:
   218  		f = "0x%04x"
   219  	case u <= 0xffffff:
   220  		f = "0x%06x"
   221  	case u <= 0xffffffff:
   222  		f = "0x%08x"
   223  	case u <= 0xffffffffff:
   224  		f = "0x%010x"
   225  	case u <= 0xffffffffffff:
   226  		f = "0x%012x"
   227  	case u <= 0xffffffffffffff:
   228  		f = "0x%014x"
   229  	case u <= 0xffffffffffffffff:
   230  		f = "0x%016x"
   231  	}
   232  	return fmt.Sprintf(f, u)
   233  }
   234  
   235  // isZero reports whether v is the zero value.
   236  // This does not rely on Interface and so can be used on unexported fields.
   237  func isZero(v reflect.Value) bool {
   238  	switch v.Kind() {
   239  	case reflect.Bool:
   240  		return v.Bool() == false
   241  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   242  		return v.Int() == 0
   243  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   244  		return v.Uint() == 0
   245  	case reflect.Float32, reflect.Float64:
   246  		return v.Float() == 0
   247  	case reflect.Complex64, reflect.Complex128:
   248  		return v.Complex() == 0
   249  	case reflect.String:
   250  		return v.String() == ""
   251  	case reflect.UnsafePointer:
   252  		return v.Pointer() == 0
   253  	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
   254  		return v.IsNil()
   255  	case reflect.Array:
   256  		for i := 0; i < v.Len(); i++ {
   257  			if !isZero(v.Index(i)) {
   258  				return false
   259  			}
   260  		}
   261  		return true
   262  	case reflect.Struct:
   263  		for i := 0; i < v.NumField(); i++ {
   264  			if !isZero(v.Field(i)) {
   265  				return false
   266  			}
   267  		}
   268  		return true
   269  	}
   270  	return false
   271  }
   272  
   273  type visited map[Pointer]bool
   274  
   275  func (m visited) Visit(v reflect.Value) bool {
   276  	p := PointerOf(v)
   277  	visited := m[p]
   278  	m[p] = true
   279  	return visited
   280  }