blob: 3b109d19e65d0f6e3cfcc9c47411d37f09c88dc5 [file] [log] [blame]
package pretty
import (
"fmt"
"io"
"reflect"
)
const (
limit = 50
)
var (
commaLFBytes = []byte(",\n")
curlyBytes = []byte("{}")
openCurlyLFBytes = []byte("{\n")
spacePlusLFBytes = []byte(" +\n")
)
type formatter struct {
d int
x interface{}
omit bool
}
func Formatter(x interface{}) fmt.Formatter {
return formatter{x: x}
}
func (fo formatter) String() string {
return fmt.Sprint(fo.x) // unwrap it
}
func (fo formatter) passThrough(f fmt.State, c int) {
s := "%"
for i := 0; i < 128; i++ {
if f.Flag(i) {
s += string(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.x)
}
func (fo formatter) Format(f fmt.State, c int) {
if c == 'v' && f.Flag('#') && f.Flag(' ') {
fo.format(f)
return
}
fo.passThrough(f, c)
}
func (fo formatter) format(w io.Writer) {
value := reflect.NewValue(fo.x)
switch v := value.(type) {
case *reflect.StringValue:
lim := limit - 8*fo.d
s := v.Get()
z := len(s)
n := (z + lim - 1) / lim
sep := append(spacePlusLFBytes, make([]byte, fo.d)...)
for j := 0; j < fo.d; j++ {
sep[3+j] = '\t'
}
for i := 0; i < n; i++ {
if i > 0 {
w.Write(sep)
}
l, h := i*lim, (i+1)*lim
if h > z {
h = z
}
fmt.Fprintf(w, "%#v", s[l:h])
}
return
case *reflect.SliceValue:
s := fmt.Sprintf("%#v", fo.x)
if len(s) < limit {
io.WriteString(w, s)
return
}
t := v.Type().(*reflect.SliceType)
_, keep := t.Elem().(*reflect.InterfaceType)
io.WriteString(w, reflect.Typeof(fo.x).String())
w.Write(openCurlyLFBytes)
for i := 0; i < v.Len(); i++ {
for j := 0; j < fo.d+1; j++ {
writeByte(w, '\t')
}
inner := formatter{d: fo.d + 1, x: v.Elem(i).Interface(), omit: !keep}
fmt.Fprintf(w, "%# v", inner)
w.Write(commaLFBytes)
}
for j := 0; j < fo.d; j++ {
writeByte(w, '\t')
}
writeByte(w, '}')
case *reflect.StructValue:
t := v.Type().(*reflect.StructType)
if reflect.DeepEqual(reflect.MakeZero(t).Interface(), fo.x) {
if !fo.omit {
io.WriteString(w, t.String())
}
w.Write(curlyBytes)
return
}
s := fmt.Sprintf("%#v", fo.x)
if fo.omit {
s = s[len(t.String()):]
}
if len(s) < limit {
io.WriteString(w, s)
return
}
if !fo.omit {
io.WriteString(w, t.String())
}
w.Write(openCurlyLFBytes)
var max int
for i := 0; i < v.NumField(); i++ {
if v := t.Field(i); v.Name != "" {
if len(v.Name) + 2 > max {
max = len(v.Name) + 2
}
}
}
for i := 0; i < v.NumField(); i++ {
if f := t.Field(i); f.Name != "" {
for j := 0; j < fo.d+1; j++ {
writeByte(w, '\t')
}
io.WriteString(w, f.Name)
writeByte(w, ':')
for j := len(f.Name) + 1; j < max; j++ {
writeByte(w, ' ')
}
inner := formatter{d: fo.d + 1, x: v.Field(i).Interface()}
io.WriteString(w, fmt.Sprintf("%# v", inner))
w.Write(commaLFBytes)
}
}
for j := 0; j < fo.d; j++ {
writeByte(w, '\t')
}
writeByte(w, '}')
default:
fmt.Fprintf(w, "%#v", fo.x)
}
}
func writeByte(w io.Writer, b byte) {
w.Write([]byte{b})
}