github.com/ecadlabs/pretty@v0.0.0-20230412123216-0f3d25fb750b/formatter.go (about) 1 package pretty 2 3 import ( 4 "encoding" 5 "fmt" 6 "io" 7 "reflect" 8 "strconv" 9 "text/tabwriter" 10 11 "github.com/kr/text" 12 "github.com/rogpeppe/go-internal/fmtsort" 13 ) 14 15 type options struct { 16 goStringer bool 17 stringer bool 18 textMarshaler bool 19 maxDepth int 20 } 21 22 var defaultOptions = options{goStringer: true, maxDepth: 10} 23 24 type Option func(opt *options) 25 26 func OptGoStringer(x bool) Option { return func(opt *options) { opt.goStringer = x } } 27 func OptStringer(x bool) Option { return func(opt *options) { opt.stringer = x } } 28 func OptTextMarshaler(x bool) Option { return func(opt *options) { opt.textMarshaler = x } } 29 func OptMaxDepth(x int) Option { return func(opt *options) { opt.maxDepth = x } } 30 31 func makeOpt(opts []Option) options { 32 out := defaultOptions 33 for _, fn := range opts { 34 fn(&out) 35 } 36 return out 37 } 38 39 type formatter struct { 40 v reflect.Value 41 force bool 42 quote bool 43 opt options 44 } 45 46 // Formatter makes a wrapper, f, that will format x as go source with line 47 // breaks and tabs. Object f responds to the "%v" formatting verb when both the 48 // "#" and " " (space) flags are set, for example: 49 // 50 // fmt.Sprintf("%# v", Formatter(x)) 51 // 52 // If one of these two flags is not set, or any other verb is used, f will 53 // format x according to the usual rules of package fmt. 54 // In particular, if x satisfies fmt.Formatter, then x.Format will be called. 55 func Formatter(x interface{}, opt ...Option) (f fmt.Formatter) { 56 return formatter{v: reflect.ValueOf(x), quote: true, opt: makeOpt(opt)} 57 } 58 59 func (fo formatter) String() string { 60 return fmt.Sprint(fo.v.Interface()) // unwrap it 61 } 62 63 func (fo formatter) passThrough(f fmt.State, c rune) { 64 s := "%" 65 for i := 0; i < 128; i++ { 66 if f.Flag(i) { 67 s += string(rune(i)) 68 } 69 } 70 if w, ok := f.Width(); ok { 71 s += fmt.Sprintf("%d", w) 72 } 73 if p, ok := f.Precision(); ok { 74 s += fmt.Sprintf(".%d", p) 75 } 76 s += string(c) 77 fmt.Fprintf(f, s, fo.v.Interface()) 78 } 79 80 func (fo formatter) Format(f fmt.State, c rune) { 81 if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { 82 w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) 83 p := &printer{tw: w, Writer: w, visited: make(map[visit]int), opt: &fo.opt} 84 p.printValue(fo.v, true, fo.quote) 85 w.Flush() 86 return 87 } 88 fo.passThrough(f, c) 89 } 90 91 type printer struct { 92 io.Writer 93 tw *tabwriter.Writer 94 visited map[visit]int 95 depth int 96 opt *options 97 } 98 99 func (p *printer) indent() *printer { 100 q := *p 101 q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) 102 q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) 103 return &q 104 } 105 106 func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { 107 if showType { 108 io.WriteString(p, v.Type().String()) 109 fmt.Fprintf(p, "(%#v)", x) 110 } else { 111 fmt.Fprintf(p, "%#v", x) 112 } 113 } 114 115 // printValue must keep track of already-printed pointer values to avoid 116 // infinite recursion. 117 type visit struct { 118 v uintptr 119 typ reflect.Type 120 } 121 122 func (p *printer) catchPanic(v reflect.Value, method string) { 123 if r := recover(); r != nil { 124 if v.Kind() == reflect.Ptr && v.IsNil() { 125 writeByte(p, '(') 126 io.WriteString(p, v.Type().String()) 127 io.WriteString(p, ")(nil)") 128 return 129 } 130 writeByte(p, '(') 131 io.WriteString(p, v.Type().String()) 132 io.WriteString(p, ")(PANIC=calling method ") 133 io.WriteString(p, strconv.Quote(method)) 134 io.WriteString(p, ": ") 135 fmt.Fprint(p, r) 136 writeByte(p, ')') 137 } 138 } 139 140 var ( 141 goStringerType = reflect.TypeOf((*fmt.GoStringer)(nil)).Elem() 142 stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() 143 textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() 144 ) 145 146 func (p *printer) printValue(v reflect.Value, showType, quote bool) { 147 if p.depth > p.opt.maxDepth { 148 io.WriteString(p, "!%v(DEPTH EXCEEDED)") 149 return 150 } 151 152 if v.IsValid() && v.CanInterface() { 153 t := v.Type() 154 if p.opt.goStringer { 155 var goStringer fmt.GoStringer 156 if t.Implements(goStringerType) { 157 goStringer = v.Interface().(fmt.GoStringer) 158 } else if reflect.PtrTo(t).Implements(goStringerType) && v.CanAddr() { 159 goStringer = v.Addr().Interface().(fmt.GoStringer) 160 } 161 if goStringer != nil { 162 defer p.catchPanic(v, "GoString") 163 io.WriteString(p, goStringer.GoString()) 164 return 165 } 166 } 167 168 if p.opt.stringer { 169 var stringer fmt.Stringer 170 if t.Implements(stringerType) { 171 stringer = v.Interface().(fmt.Stringer) 172 } else if reflect.PtrTo(t).Implements(stringerType) && v.CanAddr() { 173 stringer = v.Addr().Interface().(fmt.Stringer) 174 } 175 if stringer != nil { 176 defer p.catchPanic(v, "String") 177 p.fmtString(stringer.String(), quote) 178 return 179 } 180 } 181 182 if p.opt.textMarshaler { 183 var textMarshaler encoding.TextMarshaler 184 if t.Implements(textMarshalerType) { 185 textMarshaler = v.Interface().(encoding.TextMarshaler) 186 } else if reflect.PtrTo(t).Implements(textMarshalerType) && v.CanAddr() { 187 textMarshaler = v.Addr().Interface().(encoding.TextMarshaler) 188 } 189 if textMarshaler != nil { 190 defer p.catchPanic(v, "MarshalText") 191 text, err := textMarshaler.MarshalText() 192 if err != nil { 193 panic(err) 194 } 195 p.fmtString(string(text), quote) 196 return 197 } 198 } 199 } 200 201 switch v.Kind() { 202 case reflect.Bool: 203 p.printInline(v, v.Bool(), showType) 204 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 205 p.printInline(v, v.Int(), showType) 206 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 207 p.printInline(v, v.Uint(), showType) 208 case reflect.Float32, reflect.Float64: 209 p.printInline(v, v.Float(), showType) 210 case reflect.Complex64, reflect.Complex128: 211 fmt.Fprintf(p, "%#v", v.Complex()) 212 case reflect.String: 213 p.fmtString(v.String(), quote) 214 case reflect.Map: 215 t := v.Type() 216 if showType { 217 io.WriteString(p, t.String()) 218 } 219 writeByte(p, '{') 220 if nonzero(v) { 221 expand := !canInline(v.Type()) 222 pp := p 223 if expand { 224 writeByte(p, '\n') 225 pp = p.indent() 226 } 227 sm := fmtsort.Sort(v) 228 for i := 0; i < v.Len(); i++ { 229 k := sm.Key[i] 230 mv := sm.Value[i] 231 pp.printValue(k, false, true) 232 writeByte(pp, ':') 233 if expand { 234 writeByte(pp, '\t') 235 } 236 showTypeInStruct := t.Elem().Kind() == reflect.Interface 237 pp.printValue(mv, showTypeInStruct, true) 238 if expand { 239 io.WriteString(pp, ",\n") 240 } else if i < v.Len()-1 { 241 io.WriteString(pp, ", ") 242 } 243 } 244 if expand { 245 pp.tw.Flush() 246 } 247 } 248 writeByte(p, '}') 249 case reflect.Struct: 250 t := v.Type() 251 if v.CanAddr() { 252 addr := v.UnsafeAddr() 253 vis := visit{addr, t} 254 if vd, ok := p.visited[vis]; ok && vd < p.depth { 255 p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) 256 break // don't print v again 257 } 258 p.visited[vis] = p.depth 259 } 260 261 if showType { 262 io.WriteString(p, t.String()) 263 } 264 writeByte(p, '{') 265 if nonzero(v) { 266 expand := !canInline(v.Type()) 267 pp := p 268 if expand { 269 writeByte(p, '\n') 270 pp = p.indent() 271 } 272 for i := 0; i < v.NumField(); i++ { 273 showTypeInStruct := true 274 if f := t.Field(i); f.Name != "" { 275 io.WriteString(pp, f.Name) 276 writeByte(pp, ':') 277 if expand { 278 writeByte(pp, '\t') 279 } 280 showTypeInStruct = labelType(f.Type) 281 } 282 pp.printValue(getField(v, i), showTypeInStruct, true) 283 if expand { 284 io.WriteString(pp, ",\n") 285 } else if i < v.NumField()-1 { 286 io.WriteString(pp, ", ") 287 } 288 } 289 if expand { 290 pp.tw.Flush() 291 } 292 } 293 writeByte(p, '}') 294 case reflect.Interface: 295 switch e := v.Elem(); { 296 case e.Kind() == reflect.Invalid: 297 io.WriteString(p, "nil") 298 case e.IsValid(): 299 pp := *p 300 pp.depth++ 301 pp.printValue(e, showType, true) 302 default: 303 io.WriteString(p, v.Type().String()) 304 io.WriteString(p, "(nil)") 305 } 306 case reflect.Array, reflect.Slice: 307 t := v.Type() 308 if showType { 309 io.WriteString(p, t.String()) 310 } 311 if v.Kind() == reflect.Slice && v.IsNil() && showType { 312 io.WriteString(p, "(nil)") 313 break 314 } 315 if v.Kind() == reflect.Slice && v.IsNil() { 316 io.WriteString(p, "nil") 317 break 318 } 319 writeByte(p, '{') 320 expand := !canInline(v.Type()) 321 pp := p 322 if expand { 323 writeByte(p, '\n') 324 pp = p.indent() 325 } 326 for i := 0; i < v.Len(); i++ { 327 showTypeInSlice := t.Elem().Kind() == reflect.Interface 328 pp.printValue(v.Index(i), showTypeInSlice, true) 329 if expand { 330 io.WriteString(pp, ",\n") 331 } else if i < v.Len()-1 { 332 io.WriteString(pp, ", ") 333 } 334 } 335 if expand { 336 pp.tw.Flush() 337 } 338 writeByte(p, '}') 339 case reflect.Ptr: 340 e := v.Elem() 341 if !e.IsValid() { 342 writeByte(p, '(') 343 io.WriteString(p, v.Type().String()) 344 io.WriteString(p, ")(nil)") 345 } else { 346 pp := *p 347 pp.depth++ 348 writeByte(pp, '&') 349 pp.printValue(e, true, true) 350 } 351 case reflect.Chan: 352 x := v.Pointer() 353 if showType { 354 writeByte(p, '(') 355 io.WriteString(p, v.Type().String()) 356 fmt.Fprintf(p, ")(%#v)", x) 357 } else { 358 fmt.Fprintf(p, "%#v", x) 359 } 360 case reflect.Func: 361 io.WriteString(p, v.Type().String()) 362 io.WriteString(p, " {...}") 363 case reflect.UnsafePointer: 364 p.printInline(v, v.Pointer(), showType) 365 case reflect.Invalid: 366 io.WriteString(p, "nil") 367 } 368 } 369 370 func canInline(t reflect.Type) bool { 371 switch t.Kind() { 372 case reflect.Map: 373 return !canExpand(t.Elem()) 374 case reflect.Struct: 375 for i := 0; i < t.NumField(); i++ { 376 if canExpand(t.Field(i).Type) { 377 return false 378 } 379 } 380 return true 381 case reflect.Interface: 382 return false 383 case reflect.Array, reflect.Slice: 384 return !canExpand(t.Elem()) 385 case reflect.Ptr: 386 return false 387 case reflect.Chan, reflect.Func, reflect.UnsafePointer: 388 return false 389 } 390 return true 391 } 392 393 func canExpand(t reflect.Type) bool { 394 switch t.Kind() { 395 case reflect.Map, reflect.Struct, 396 reflect.Interface, reflect.Array, reflect.Slice, 397 reflect.Ptr: 398 return true 399 } 400 return false 401 } 402 403 func labelType(t reflect.Type) bool { 404 switch t.Kind() { 405 case reflect.Interface, reflect.Struct: 406 return true 407 } 408 return false 409 } 410 411 func (p *printer) fmtString(s string, quote bool) { 412 if quote { 413 s = strconv.Quote(s) 414 } 415 io.WriteString(p, s) 416 } 417 418 func writeByte(w io.Writer, b byte) { 419 w.Write([]byte{b}) 420 } 421 422 func getField(v reflect.Value, i int) reflect.Value { 423 val := v.Field(i) 424 if val.Kind() == reflect.Interface && !val.IsNil() { 425 val = val.Elem() 426 } 427 return val 428 }