package pretty import ( "fmt" "io" "reflect" "strconv" "text/tabwriter" "github.com/kr/text" ) type formatter struct { v reflect.Value force bool quote bool } // Formatter makes a wrapper, f, that will format x as go source with line // breaks and tabs. Object f responds to the "%v" formatting verb when both the // "#" and " " (space) flags are set, for example: // // fmt.Sprintf("%# v", Formatter(x)) // // If one of these two flags is not set, or any other verb is used, f will // format x according to the usual rules of package fmt. // In particular, if x satisfies fmt.Formatter, then x.Format will be called. func Formatter(x interface{}) (f fmt.Formatter) { return formatter{v: reflect.ValueOf(x), quote: true} } func (fo formatter) String() string { return fmt.Sprint(fo.v.Interface()) // unwrap it } func (fo formatter) passThrough(f fmt.State, c rune) { s := "%" for i := 0; i < 128; i++ { if f.Flag(i) { s += string(rune(i)) } } if w, ok := f.Width(); ok { s += fmt.Sprintf("%d", w) } if p, ok := f.Precision(); ok { s += fmt.Sprintf(".%d", p) } s += string(c) fmt.Fprintf(f, s, fo.v.Interface()) } func (fo formatter) Format(f fmt.State, c rune) { if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) p := &printer{tw: w, Writer: w, visited: make(map[visit]int)} p.printValue(fo.v, true, fo.quote) w.Flush() return } fo.passThrough(f, c) } type printer struct { io.Writer tw *tabwriter.Writer visited map[visit]int depth int } func (p *printer) indent() *printer { q := *p q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) return &q } func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { if showType { io.WriteString(p, v.Type().String()) fmt.Fprintf(p, "(%#v)", x) } else { fmt.Fprintf(p, "%#v", x) } } // printValue must keep track of already-printed pointer values to avoid // infinite recursion. type visit struct { v uintptr typ reflect.Type } func (p *printer) printValue(v reflect.Value, showType, quote bool) { if p.depth > 10 { io.WriteString(p, "!%v(DEPTH EXCEEDED)") return } switch v.Kind() { case reflect.Bool: p.printInline(v, v.Bool(), showType) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p.printInline(v, v.Int(), showType) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p.printInline(v, v.Uint(), showType) case reflect.Float32, reflect.Float64: p.printInline(v, v.Float(), showType) case reflect.Complex64, reflect.Complex128: fmt.Fprintf(p, "%#v", v.Complex()) case reflect.String: p.fmtString(v.String(), quote) case reflect.Map: t := v.Type() if showType { io.WriteString(p, t.String()) } writeByte(p, '{') if nonzero(v) { expand := !canInline(v.Type()) pp := p if expand { writeByte(p, '\n') pp = p.indent() } keys := v.MapKeys() for i := 0; i < v.Len(); i++ { k := keys[i] mv := v.MapIndex(k) pp.printValue(k, false, true) writeByte(pp, ':') if expand { writeByte(pp, '\t') } showTypeInStruct := t.Elem().Kind() == reflect.Interface pp.printValue(mv, showTypeInStruct, true) if expand { io.WriteString(pp, ",\n") } else if i < v.Len()-1 { io.WriteString(pp, ", ") } } if expand { pp.tw.Flush() } } writeByte(p, '}') case reflect.Struct: t := v.Type() if v.CanAddr() { addr := v.UnsafeAddr() vis := visit{addr, t} if vd, ok := p.visited[vis]; ok && vd < p.depth { p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) break // don't print v again } p.visited[vis] = p.depth } if showType { io.WriteString(p, t.String()) } writeByte(p, '{') if nonzero(v) { expand := !canInline(v.Type()) pp := p if expand { writeByte(p, '\n') pp = p.indent() } for i := 0; i < v.NumField(); i++ { showTypeInStruct := true if f := t.Field(i); f.Name != "" { io.WriteString(pp, f.Name) writeByte(pp, ':') if expand { writeByte(pp, '\t') } showTypeInStruct = labelType(f.Type) } pp.printValue(getField(v, i), showTypeInStruct, true) if expand { io.WriteString(pp, ",\n") } else if i < v.NumField()-1 { io.WriteString(pp, ", ") } } if expand { pp.tw.Flush() } } writeByte(p, '}') case reflect.Interface: switch e := v.Elem(); { case e.Kind() == reflect.Invalid: io.WriteString(p, "nil") case e.IsValid(): pp := *p pp.depth++ pp.printValue(e, showType, true) default: io.WriteString(p, v.Type().String()) io.WriteString(p, "(nil)") } case reflect.Array, reflect.Slice: t := v.Type() if showType { io.WriteString(p, t.String()) } if v.Kind() == reflect.Slice && v.IsNil() && showType { io.WriteString(p, "(nil)") break } if v.Kind() == reflect.Slice && v.IsNil() { io.WriteString(p, "nil") break } writeByte(p, '{') expand := !canInline(v.Type()) pp := p if expand { writeByte(p, '\n') pp = p.indent() } for i := 0; i < v.Len(); i++ { showTypeInSlice := t.Elem().Kind() == reflect.Interface pp.printValue(v.Index(i), showTypeInSlice, true) if expand { io.WriteString(pp, ",\n") } else if i < v.Len()-1 { io.WriteString(pp, ", ") } } if expand { pp.tw.Flush() } writeByte(p, '}') case reflect.Ptr: e := v.Elem() if !e.IsValid() { writeByte(p, '(') io.WriteString(p, v.Type().String()) io.WriteString(p, ")(nil)") } else { pp := *p pp.depth++ writeByte(pp, '&') pp.printValue(e, true, true) } case reflect.Chan: x := v.Pointer() if showType { writeByte(p, '(') io.WriteString(p, v.Type().String()) fmt.Fprintf(p, ")(%#v)", x) } else { fmt.Fprintf(p, "%#v", x) } case reflect.Func: io.WriteString(p, v.Type().String()) io.WriteString(p, " {...}") case reflect.UnsafePointer: p.printInline(v, v.Pointer(), showType) case reflect.Invalid: io.WriteString(p, "nil") } } func canInline(t reflect.Type) bool { switch t.Kind() { case reflect.Map: return !canExpand(t.Elem()) case reflect.Struct: for i := 0; i < t.NumField(); i++ { if canExpand(t.Field(i).Type) { return false } } return true case reflect.Interface: return false case reflect.Array, reflect.Slice: return !canExpand(t.Elem()) case reflect.Ptr: return false case reflect.Chan, reflect.Func, reflect.UnsafePointer: return false } return true } func canExpand(t reflect.Type) bool { switch t.Kind() { case reflect.Map, reflect.Struct, reflect.Interface, reflect.Array, reflect.Slice, reflect.Ptr: return true } return false } func labelType(t reflect.Type) bool { switch t.Kind() { case reflect.Interface, reflect.Struct: return true } return false } func (p *printer) fmtString(s string, quote bool) { if quote { s = strconv.Quote(s) } io.WriteString(p, s) } func writeByte(w io.Writer, b byte) { w.Write([]byte{b}) } func getField(v reflect.Value, i int) reflect.Value { val := v.Field(i) if val.Kind() == reflect.Interface && !val.IsNil() { val = val.Elem() } return val }