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 }