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 k := keys[i] 129 mv := v.MapIndex(k) 130 pp.printValue(k, false, true) 131 writeByte(pp, ':') 132 if expand { 133 writeByte(pp, '\t') 134 } 135 showTypeInStruct := t.Elem().Kind() == reflect.Interface 136 pp.printValue(mv, showTypeInStruct, true) 137 if expand { 138 io.WriteString(pp, ",\n") 139 } else if i < v.Len()-1 { 140 io.WriteString(pp, ", ") 141 } 142 } 143 if expand { 144 pp.tw.Flush() 145 } 146 } 147 writeByte(p, '}') 148 case reflect.Struct: 149 t := v.Type() 150 if v.CanAddr() { 151 addr := v.UnsafeAddr() 152 vis := visit{addr, t} 153 if vd, ok := p.visited[vis]; ok && vd < p.depth { 154 p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) 155 break // don't print v again 156 } 157 p.visited[vis] = p.depth 158 } 159 160 if showType { 161 io.WriteString(p, t.String()) 162 } 163 writeByte(p, '{') 164 if nonzero(v) { 165 expand := !canInline(v.Type()) 166 pp := p 167 if expand { 168 writeByte(p, '\n') 169 pp = p.indent() 170 } 171 for i := 0; i < v.NumField(); i++ { 172 showTypeInStruct := true 173 if f := t.Field(i); f.Name != "" { 174 io.WriteString(pp, f.Name) 175 writeByte(pp, ':') 176 if expand { 177 writeByte(pp, '\t') 178 } 179 showTypeInStruct = labelType(f.Type) 180 } 181 pp.printValue(getField(v, i), showTypeInStruct, true) 182 if expand { 183 io.WriteString(pp, ",\n") 184 } else if i < v.NumField()-1 { 185 io.WriteString(pp, ", ") 186 } 187 } 188 if expand { 189 pp.tw.Flush() 190 } 191 } 192 writeByte(p, '}') 193 case reflect.Interface: 194 switch e := v.Elem(); { 195 case e.Kind() == reflect.Invalid: 196 io.WriteString(p, "nil") 197 case e.IsValid(): 198 pp := *p 199 pp.depth++ 200 pp.printValue(e, showType, true) 201 default: 202 io.WriteString(p, v.Type().String()) 203 io.WriteString(p, "(nil)") 204 } 205 case reflect.Array, reflect.Slice: 206 t := v.Type() 207 if showType { 208 io.WriteString(p, t.String()) 209 } 210 if v.Kind() == reflect.Slice && v.IsNil() && showType { 211 io.WriteString(p, "(nil)") 212 break 213 } 214 if v.Kind() == reflect.Slice && v.IsNil() { 215 io.WriteString(p, "nil") 216 break 217 } 218 writeByte(p, '{') 219 expand := !canInline(v.Type()) 220 pp := p 221 if expand { 222 writeByte(p, '\n') 223 pp = p.indent() 224 } 225 for i := 0; i < v.Len(); i++ { 226 showTypeInSlice := t.Elem().Kind() == reflect.Interface 227 pp.printValue(v.Index(i), showTypeInSlice, true) 228 if expand { 229 io.WriteString(pp, ",\n") 230 } else if i < v.Len()-1 { 231 io.WriteString(pp, ", ") 232 } 233 } 234 if expand { 235 pp.tw.Flush() 236 } 237 writeByte(p, '}') 238 case reflect.Ptr: 239 e := v.Elem() 240 if !e.IsValid() { 241 writeByte(p, '(') 242 io.WriteString(p, v.Type().String()) 243 io.WriteString(p, ")(nil)") 244 } else { 245 pp := *p 246 pp.depth++ 247 writeByte(pp, '&') 248 pp.printValue(e, true, true) 249 } 250 case reflect.Chan: 251 x := v.Pointer() 252 if showType { 253 writeByte(p, '(') 254 io.WriteString(p, v.Type().String()) 255 fmt.Fprintf(p, ")(%#v)", x) 256 } else { 257 fmt.Fprintf(p, "%#v", x) 258 } 259 case reflect.Func: 260 io.WriteString(p, v.Type().String()) 261 io.WriteString(p, " {...}") 262 case reflect.UnsafePointer: 263 p.printInline(v, v.Pointer(), showType) 264 case reflect.Invalid: 265 io.WriteString(p, "nil") 266 } 267} 268 269func canInline(t reflect.Type) bool { 270 switch t.Kind() { 271 case reflect.Map: 272 return !canExpand(t.Elem()) 273 case reflect.Struct: 274 for i := 0; i < t.NumField(); i++ { 275 if canExpand(t.Field(i).Type) { 276 return false 277 } 278 } 279 return true 280 case reflect.Interface: 281 return false 282 case reflect.Array, reflect.Slice: 283 return !canExpand(t.Elem()) 284 case reflect.Ptr: 285 return false 286 case reflect.Chan, reflect.Func, reflect.UnsafePointer: 287 return false 288 } 289 return true 290} 291 292func canExpand(t reflect.Type) bool { 293 switch t.Kind() { 294 case reflect.Map, reflect.Struct, 295 reflect.Interface, reflect.Array, reflect.Slice, 296 reflect.Ptr: 297 return true 298 } 299 return false 300} 301 302func labelType(t reflect.Type) bool { 303 switch t.Kind() { 304 case reflect.Interface, reflect.Struct: 305 return true 306 } 307 return false 308} 309 310func (p *printer) fmtString(s string, quote bool) { 311 if quote { 312 s = strconv.Quote(s) 313 } 314 io.WriteString(p, s) 315} 316 317func writeByte(w io.Writer, b byte) { 318 w.Write([]byte{b}) 319} 320 321func getField(v reflect.Value, i int) reflect.Value { 322 val := v.Field(i) 323 if val.Kind() == reflect.Interface && !val.IsNil() { 324 val = val.Elem() 325 } 326 return val 327} 328