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