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