1// Copyright 2016 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package pretty implements a simple pretty-printer. It is intended for
16// debugging the output of tests.
17//
18// It follows pointers and produces multi-line output for complex values like
19// slices, maps and structs.
20package pretty
21
22import (
23	"fmt"
24	"io"
25	"reflect"
26	"sort"
27	"strings"
28	"time"
29)
30
31// Indent is the string output at each level of indentation.
32var Indent = "    "
33
34// Value returns a value that will print prettily when used as an
35// argument for the %v or %s format specifiers.
36// With no flags, struct fields and map keys with default values are omitted.
37// With the '+' or '#' flags, all values are displayed.
38//
39// This package does not detect cycles. Attempting to print a Value that
40// contains cycles will result in unbounded recursion.
41func Value(v interface{}) val { return val{v: v} }
42
43// val is a value.
44type val struct{ v interface{} }
45
46// Format implements the fmt.Formatter interface.
47func (v val) Format(s fmt.State, c rune) {
48	if c == 'v' || c == 's' {
49		fprint(s, reflect.ValueOf(v.v), state{
50			defaults: s.Flag('+') || s.Flag('#'),
51		})
52	} else {
53		fmt.Fprintf(s, "%%!%c(pretty.val)", c)
54	}
55}
56
57type state struct {
58	level          int
59	prefix, suffix string
60	defaults       bool
61}
62
63const maxLevel = 100
64
65var typeOfTime = reflect.TypeOf(time.Time{})
66
67func fprint(w io.Writer, v reflect.Value, s state) {
68	if s.level > maxLevel {
69		fmt.Fprintln(w, "pretty: max nested depth exceeded")
70		return
71	}
72	indent := strings.Repeat(Indent, s.level)
73	fmt.Fprintf(w, "%s%s", indent, s.prefix)
74	if isNil(v) {
75		fmt.Fprintf(w, "nil%s", s.suffix)
76		return
77	}
78	if v.Type().Kind() == reflect.Interface {
79		v = v.Elem()
80	}
81	if v.Type() == typeOfTime {
82		fmt.Fprintf(w, "%s%s", v.Interface(), s.suffix)
83		return
84	}
85	for v.Type().Kind() == reflect.Ptr {
86		fmt.Fprintf(w, "&")
87		v = v.Elem()
88	}
89	switch v.Type().Kind() {
90	default:
91		fmt.Fprintf(w, "%s%s", short(v), s.suffix)
92
93	case reflect.Array:
94		fmt.Fprintf(w, "%s{\n", v.Type())
95		for i := 0; i < v.Len(); i++ {
96			fprint(w, v.Index(i), state{
97				level:    s.level + 1,
98				prefix:   "",
99				suffix:   ",",
100				defaults: s.defaults,
101			})
102			fmt.Fprintln(w)
103		}
104		fmt.Fprintf(w, "%s}", indent)
105
106	case reflect.Slice:
107		fmt.Fprintf(w, "%s{", v.Type())
108		if v.Len() > 0 {
109			fmt.Fprintln(w)
110			for i := 0; i < v.Len(); i++ {
111				fprint(w, v.Index(i), state{
112					level:    s.level + 1,
113					prefix:   "",
114					suffix:   ",",
115					defaults: s.defaults,
116				})
117				fmt.Fprintln(w)
118			}
119		}
120		fmt.Fprintf(w, "%s}%s", indent, s.suffix)
121
122	case reflect.Map:
123		fmt.Fprintf(w, "%s{", v.Type())
124		if v.Len() > 0 {
125			fmt.Fprintln(w)
126			keys := v.MapKeys()
127			maybeSort(keys, v.Type().Key())
128			for _, key := range keys {
129				val := v.MapIndex(key)
130				if s.defaults || !isDefault(val) {
131					fprint(w, val, state{
132						level:    s.level + 1,
133						prefix:   short(key) + ": ",
134						suffix:   ",",
135						defaults: s.defaults,
136					})
137					fmt.Fprintln(w)
138				}
139			}
140		}
141		fmt.Fprintf(w, "%s}%s", indent, s.suffix)
142
143	case reflect.Struct:
144		t := v.Type()
145		fmt.Fprintf(w, "%s{\n", t)
146		for i := 0; i < t.NumField(); i++ {
147			f := v.Field(i)
148			if s.defaults || !isDefault(f) {
149				fprint(w, f, state{
150					level:    s.level + 1,
151					prefix:   t.Field(i).Name + ": ",
152					suffix:   ",",
153					defaults: s.defaults,
154				})
155				fmt.Fprintln(w)
156			}
157		}
158		fmt.Fprintf(w, "%s}%s", indent, s.suffix)
159	}
160}
161
162func isNil(v reflect.Value) bool {
163	if !v.IsValid() {
164		return true
165	}
166	switch v.Type().Kind() {
167	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
168		return v.IsNil()
169	default:
170		return false
171	}
172}
173
174func isDefault(v reflect.Value) bool {
175	if !v.IsValid() {
176		return true
177	}
178	t := v.Type()
179	switch t.Kind() {
180	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
181		return v.IsNil()
182	default:
183		if !v.CanInterface() {
184			return false
185		}
186		return t.Comparable() && v.Interface() == reflect.Zero(t).Interface()
187	}
188}
189
190// short returns a short, one-line string for v.
191func short(v reflect.Value) string {
192	if !v.IsValid() {
193		return "nil"
194	}
195	if v.Type().Kind() == reflect.String {
196		return fmt.Sprintf("%q", v)
197	}
198	return fmt.Sprintf("%v", v)
199}
200
201func maybeSort(vs []reflect.Value, t reflect.Type) {
202	if less := lessFunc(t); less != nil {
203		sort.Sort(&sorter{vs, less})
204	}
205}
206
207// lessFunc returns a function that implements the "<" operator
208// for the given type, or nil if the type doesn't support "<" .
209func lessFunc(t reflect.Type) func(v1, v2 interface{}) bool {
210	switch t.Kind() {
211	case reflect.String:
212		return func(v1, v2 interface{}) bool { return v1.(string) < v2.(string) }
213	case reflect.Int:
214		return func(v1, v2 interface{}) bool { return v1.(int) < v2.(int) }
215	case reflect.Int8:
216		return func(v1, v2 interface{}) bool { return v1.(int8) < v2.(int8) }
217	case reflect.Int16:
218		return func(v1, v2 interface{}) bool { return v1.(int16) < v2.(int16) }
219	case reflect.Int32:
220		return func(v1, v2 interface{}) bool { return v1.(int32) < v2.(int32) }
221	case reflect.Int64:
222		return func(v1, v2 interface{}) bool { return v1.(int64) < v2.(int64) }
223	case reflect.Uint:
224		return func(v1, v2 interface{}) bool { return v1.(uint) < v2.(uint) }
225	case reflect.Uint8:
226		return func(v1, v2 interface{}) bool { return v1.(uint8) < v2.(uint8) }
227	case reflect.Uint16:
228		return func(v1, v2 interface{}) bool { return v1.(uint16) < v2.(uint16) }
229	case reflect.Uint32:
230		return func(v1, v2 interface{}) bool { return v1.(uint32) < v2.(uint32) }
231	case reflect.Uint64:
232		return func(v1, v2 interface{}) bool { return v1.(uint64) < v2.(uint64) }
233	case reflect.Float32:
234		return func(v1, v2 interface{}) bool { return v1.(float32) < v2.(float32) }
235	case reflect.Float64:
236		return func(v1, v2 interface{}) bool { return v1.(float64) < v2.(float64) }
237	default:
238		return nil
239	}
240}
241
242type sorter struct {
243	vs   []reflect.Value
244	less func(v1, v2 interface{}) bool
245}
246
247func (s *sorter) Len() int           { return len(s.vs) }
248func (s *sorter) Swap(i, j int)      { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
249func (s *sorter) Less(i, j int) bool { return s.less(s.vs[i].Interface(), s.vs[j].Interface()) }
250