1package pretty 2 3import ( 4 "fmt" 5 "io" 6 "reflect" 7 "strconv" 8 "text/tabwriter" 9 10 "github.com/kr/text" 11) 12 13type formatter struct { 14 v reflect.Value 15 force bool 16 quote bool 17} 18 19// Formatter makes a wrapper, f, that will format x as go source with line 20// breaks and tabs. Object f responds to the "%v" formatting verb when both the 21// "#" and " " (space) flags are set, for example: 22// 23// fmt.Sprintf("%# v", Formatter(x)) 24// 25// If one of these two flags is not set, or any other verb is used, f will 26// format x according to the usual rules of package fmt. 27// In particular, if x satisfies fmt.Formatter, then x.Format will be called. 28func Formatter(x interface{}) (f fmt.Formatter) { 29 return formatter{v: reflect.ValueOf(x), quote: true} 30} 31 32func (fo formatter) String() string { 33 return fmt.Sprint(fo.v.Interface()) // unwrap it 34} 35 36func (fo formatter) passThrough(f fmt.State, c rune) { 37 s := "%" 38 for i := 0; i < 128; i++ { 39 if f.Flag(i) { 40 s += string(i) 41 } 42 } 43 if w, ok := f.Width(); ok { 44 s += fmt.Sprintf("%d", w) 45 } 46 if p, ok := f.Precision(); ok { 47 s += fmt.Sprintf(".%d", p) 48 } 49 s += string(c) 50 fmt.Fprintf(f, s, fo.v.Interface()) 51} 52 53func (fo formatter) Format(f fmt.State, c rune) { 54 if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { 55 w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) 56 p := &printer{tw: w, Writer: w, visited: make(map[visit]int)} 57 p.printValue(fo.v, true, fo.quote) 58 w.Flush() 59 return 60 } 61 fo.passThrough(f, c) 62} 63 64type printer struct { 65 io.Writer 66 tw *tabwriter.Writer 67 visited map[visit]int 68 depth int 69} 70 71func (p *printer) indent() *printer { 72 q := *p 73 q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) 74 q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) 75 return &q 76} 77 78func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { 79 if showType { 80 io.WriteString(p, v.Type().String()) 81 fmt.Fprintf(p, "(%#v)", x) 82 } else { 83 fmt.Fprintf(p, "%#v", x) 84 } 85} 86 87// printValue must keep track of already-printed pointer values to avoid 88// infinite recursion. 89type visit struct { 90 v uintptr 91 typ reflect.Type 92} 93 94func (p *printer) printValue(v reflect.Value, showType, quote bool) { 95 if p.depth > 10 { 96 io.WriteString(p, "!%v(DEPTH EXCEEDED)") 97 return 98 } 99 100 switch v.Kind() { 101 case reflect.Bool: 102 p.printInline(v, v.Bool(), showType) 103 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 104 p.printInline(v, v.Int(), showType) 105 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 106 p.printInline(v, v.Uint(), showType) 107 case reflect.Float32, reflect.Float64: 108 p.printInline(v, v.Float(), showType) 109 case reflect.Complex64, reflect.Complex128: 110 fmt.Fprintf(p, "%#v", v.Complex()) 111 case reflect.String: 112 p.fmtString(v.String(), quote) 113 case reflect.Map: 114 t := v.Type() 115 if showType { 116 io.WriteString(p, t.String()) 117 } 118 writeByte(p, '{') 119 if nonzero(v) { 120 expand := !canInline(v.Type()) 121 pp := p 122 if expand { 123 writeByte(p, '\n') 124 pp = p.indent() 125 } 126 keys := v.MapKeys() 127 for i := 0; i < v.Len(); i++ { 128 showTypeInStruct := true 129 k := keys[i] 130 mv := v.MapIndex(k) 131 pp.printValue(k, false, true) 132 writeByte(pp, ':') 133 if expand { 134 writeByte(pp, '\t') 135 } 136 showTypeInStruct = t.Elem().Kind() == reflect.Interface 137 pp.printValue(mv, showTypeInStruct, true) 138 if expand { 139 io.WriteString(pp, ",\n") 140 } else if i < v.Len()-1 { 141 io.WriteString(pp, ", ") 142 } 143 } 144 if expand { 145 pp.tw.Flush() 146 } 147 } 148 writeByte(p, '}') 149 case reflect.Struct: 150 t := v.Type() 151 if v.CanAddr() { 152 addr := v.UnsafeAddr() 153 vis := visit{addr, t} 154 if vd, ok := p.visited[vis]; ok && vd < p.depth { 155 p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) 156 break // don't print v again 157 } 158 p.visited[vis] = p.depth 159 } 160 161 if showType { 162 io.WriteString(p, t.String()) 163 } 164 writeByte(p, '{') 165 if nonzero(v) { 166 expand := !canInline(v.Type()) 167 pp := p 168 if expand { 169 writeByte(p, '\n') 170 pp = p.indent() 171 } 172 for i := 0; i < v.NumField(); i++ { 173 showTypeInStruct := true 174 if f := t.Field(i); f.Name != "" { 175 io.WriteString(pp, f.Name) 176 writeByte(pp, ':') 177 if expand { 178 writeByte(pp, '\t') 179 } 180 showTypeInStruct = labelType(f.Type) 181 } 182 pp.printValue(getField(v, i), showTypeInStruct, true) 183 if expand { 184 io.WriteString(pp, ",\n") 185 } else if i < v.NumField()-1 { 186 io.WriteString(pp, ", ") 187 } 188 } 189 if expand { 190 pp.tw.Flush() 191 } 192 } 193 writeByte(p, '}') 194 case reflect.Interface: 195 switch e := v.Elem(); { 196 case e.Kind() == reflect.Invalid: 197 io.WriteString(p, "nil") 198 case e.IsValid(): 199 pp := *p 200 pp.depth++ 201 pp.printValue(e, showType, true) 202 default: 203 io.WriteString(p, v.Type().String()) 204 io.WriteString(p, "(nil)") 205 } 206 case reflect.Array, reflect.Slice: 207 t := v.Type() 208 if showType { 209 io.WriteString(p, t.String()) 210 } 211 if v.Kind() == reflect.Slice && v.IsNil() && showType { 212 io.WriteString(p, "(nil)") 213 break 214 } 215 if v.Kind() == reflect.Slice && v.IsNil() { 216 io.WriteString(p, "nil") 217 break 218 } 219 writeByte(p, '{') 220 expand := !canInline(v.Type()) 221 pp := p 222 if expand { 223 writeByte(p, '\n') 224 pp = p.indent() 225 } 226 for i := 0; i < v.Len(); i++ { 227 showTypeInSlice := t.Elem().Kind() == reflect.Interface 228 pp.printValue(v.Index(i), showTypeInSlice, true) 229 if expand { 230 io.WriteString(pp, ",\n") 231 } else if i < v.Len()-1 { 232 io.WriteString(pp, ", ") 233 } 234 } 235 if expand { 236 pp.tw.Flush() 237 } 238 writeByte(p, '}') 239 case reflect.Ptr: 240 e := v.Elem() 241 if !e.IsValid() { 242 writeByte(p, '(') 243 io.WriteString(p, v.Type().String()) 244 io.WriteString(p, ")(nil)") 245 } else { 246 pp := *p 247 pp.depth++ 248 writeByte(pp, '&') 249 pp.printValue(e, true, true) 250 } 251 case reflect.Chan: 252 x := v.Pointer() 253 if showType { 254 writeByte(p, '(') 255 io.WriteString(p, v.Type().String()) 256 fmt.Fprintf(p, ")(%#v)", x) 257 } else { 258 fmt.Fprintf(p, "%#v", x) 259 } 260 case reflect.Func: 261 io.WriteString(p, v.Type().String()) 262 io.WriteString(p, " {...}") 263 case reflect.UnsafePointer: 264 p.printInline(v, v.Pointer(), showType) 265 case reflect.Invalid: 266 io.WriteString(p, "nil") 267 } 268} 269 270func canInline(t reflect.Type) bool { 271 switch t.Kind() { 272 case reflect.Map: 273 return !canExpand(t.Elem()) 274 case reflect.Struct: 275 for i := 0; i < t.NumField(); i++ { 276 if canExpand(t.Field(i).Type) { 277 return false 278 } 279 } 280 return true 281 case reflect.Interface: 282 return false 283 case reflect.Array, reflect.Slice: 284 return !canExpand(t.Elem()) 285 case reflect.Ptr: 286 return false 287 case reflect.Chan, reflect.Func, reflect.UnsafePointer: 288 return false 289 } 290 return true 291} 292 293func canExpand(t reflect.Type) bool { 294 switch t.Kind() { 295 case reflect.Map, reflect.Struct, 296 reflect.Interface, reflect.Array, reflect.Slice, 297 reflect.Ptr: 298 return true 299 } 300 return false 301} 302 303func labelType(t reflect.Type) bool { 304 switch t.Kind() { 305 case reflect.Interface, reflect.Struct: 306 return true 307 } 308 return false 309} 310 311func (p *printer) fmtString(s string, quote bool) { 312 if quote { 313 s = strconv.Quote(s) 314 } 315 io.WriteString(p, s) 316} 317 318func writeByte(w io.Writer, b byte) { 319 w.Write([]byte{b}) 320} 321 322func getField(v reflect.Value, i int) reflect.Value { 323 val := v.Field(i) 324 if val.Kind() == reflect.Interface && !val.IsNil() { 325 val = val.Elem() 326 } 327 return val 328} 329