repr.go 8.08 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Package repr attempts to represent Go values in a form that can be copy-and-pasted into source
// code directly.
//
// Some values (such as pointers to basic types) can not be represented directly in
// Go. These values will be output as `&<value>`. eg. `&23`
package repr

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"reflect"
	"unsafe"
)

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
var (
	// "Real" names of basic kinds, used to differentiate type aliases.
	realKindName = map[reflect.Kind]string{
		reflect.Bool:       "bool",
		reflect.Int:        "int",
		reflect.Int8:       "int8",
		reflect.Int16:      "int16",
		reflect.Int32:      "int32",
		reflect.Int64:      "int64",
		reflect.Uint:       "uint",
		reflect.Uint8:      "uint8",
		reflect.Uint16:     "uint16",
		reflect.Uint32:     "uint32",
		reflect.Uint64:     "uint64",
		reflect.Uintptr:    "uintptr",
		reflect.Float32:    "float32",
		reflect.Float64:    "float64",
		reflect.Complex64:  "complex64",
		reflect.Complex128: "complex128",
		reflect.Array:      "array",
		reflect.Chan:       "chan",
		reflect.Func:       "func",
		reflect.Map:        "map",
		reflect.Slice:      "slice",
		reflect.String:     "string",
	}

	goStringerType = reflect.TypeOf((*fmt.GoStringer)(nil)).Elem()
45

46 47
	byteSliceType = reflect.TypeOf([]byte{})
)
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

// Default prints to os.Stdout with two space indentation.
var Default = New(os.Stdout, Indent("  "))

// An Option modifies the default behaviour of a Printer.
type Option func(o *Printer)

// Indent output by this much.
func Indent(indent string) Option { return func(o *Printer) { o.indent = indent } }

// NoIndent disables indenting.
func NoIndent() Option { return Indent("") }

// OmitEmpty sets whether empty field members should be omitted from output.
func OmitEmpty(omitEmpty bool) Option { return func(o *Printer) { o.omitEmpty = omitEmpty } }

64
// IgnoreGoStringer disables use of the .GoString() method.
65 66
func IgnoreGoStringer() Option { return func(o *Printer) { o.ignoreGoStringer = true } }

67 68 69 70 71 72 73 74 75 76
// Hide excludes the given types from representation, instead just printing the name of the type.
func Hide(ts ...interface{}) Option {
	return func(o *Printer) {
		for _, t := range ts {
			rt := reflect.Indirect(reflect.ValueOf(t)).Type()
			o.exclude[rt] = true
		}
	}
}

77 78 79
// AlwaysIncludeType always includes explicit type information for each item.
func AlwaysIncludeType() Option { return func(o *Printer) { o.alwaysIncludeType = true } }

80 81
// Printer represents structs in a printable manner.
type Printer struct {
82 83 84 85 86 87
	indent            string
	omitEmpty         bool
	ignoreGoStringer  bool
	alwaysIncludeType bool
	exclude           map[reflect.Type]bool
	w                 io.Writer
88 89 90 91 92 93 94 95
}

// New creates a new Printer on w with the given Options.
func New(w io.Writer, options ...Option) *Printer {
	p := &Printer{
		w:         w,
		indent:    "  ",
		omitEmpty: true,
96
		exclude:   map[reflect.Type]bool{},
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
	}
	for _, option := range options {
		option(p)
	}
	return p
}

func (p *Printer) nextIndent(indent string) string {
	if p.indent != "" {
		return indent + p.indent
	}
	return ""
}

func (p *Printer) thisIndent(indent string) string {
	if p.indent != "" {
		return indent
	}
	return ""
}

// Print the values.
func (p *Printer) Print(vs ...interface{}) {
	for i, v := range vs {
		if i > 0 {
			fmt.Fprint(p.w, " ")
		}
124
		p.reprValue(map[reflect.Value]bool{}, reflect.ValueOf(v), "")
125 126 127 128 129 130 131 132 133
	}
}

// Println prints each value on a new line.
func (p *Printer) Println(vs ...interface{}) {
	for i, v := range vs {
		if i > 0 {
			fmt.Fprint(p.w, " ")
		}
134
		p.reprValue(map[reflect.Value]bool{}, reflect.ValueOf(v), "")
135 136 137 138
	}
	fmt.Fprintln(p.w)
}

139 140 141 142 143 144 145 146
func (p *Printer) reprValue(seen map[reflect.Value]bool, v reflect.Value, indent string) { // nolint: gocyclo
	if seen[v] {
		fmt.Fprint(p.w, "...")
		return
	}
	seen[v] = true
	defer delete(seen, v)

147
	if v.Kind() == reflect.Invalid || (v.Kind() == reflect.Ptr || v.Kind() == reflect.Map || v.Kind() == reflect.Chan || v.Kind() == reflect.Slice || v.Kind() == reflect.Func || v.Kind() == reflect.Interface) && v.IsNil() {
148 149 150 151
		fmt.Fprint(p.w, "nil")
		return
	}
	if p.exclude[v.Type()] {
152
		fmt.Fprintf(p.w, "%s...", v.Type().Name())
153 154
		return
	}
155
	t := v.Type()
156 157 158 159 160 161

	if t == byteSliceType {
		fmt.Fprintf(p.w, "[]byte(%q)", v.Interface())
		return
	}

162 163 164 165 166 167 168 169
	// If we can't access a private field directly with reflection, try and do so via unsafe.
	if !v.CanInterface() && v.CanAddr() {
		uv := reflect.NewAt(t, unsafe.Pointer(v.UnsafeAddr())).Elem()
		if uv.CanInterface() {
			v = uv
		}
	}
	// Attempt to use fmt.GoStringer interface.
170
	if !p.ignoreGoStringer && t.Implements(goStringerType) {
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
		fmt.Fprint(p.w, v.Interface().(fmt.GoStringer).GoString())
		return
	}
	in := p.thisIndent(indent)
	ni := p.nextIndent(indent)
	switch v.Kind() {
	case reflect.Slice, reflect.Array:
		if p.omitEmpty && v.Len() == 0 {
			return
		}
		fmt.Fprintf(p.w, "%s{", v.Type())
		if v.Len() == 0 {
			fmt.Fprint(p.w, "}")
		} else {
			if p.indent != "" {
				fmt.Fprintf(p.w, "\n")
			}
			for i := 0; i < v.Len(); i++ {
				e := v.Index(i)
				fmt.Fprintf(p.w, "%s", ni)
191
				p.reprValue(seen, e, ni)
192 193 194 195 196 197 198 199
				if p.indent != "" {
					fmt.Fprintf(p.w, ",\n")
				} else if i < v.Len()-1 {
					fmt.Fprintf(p.w, ", ")
				}
			}
			fmt.Fprintf(p.w, "%s}", in)
		}
200

201 202 203 204
	case reflect.Chan:
		fmt.Fprintf(p.w, "make(")
		fmt.Fprintf(p.w, "%s", v.Type())
		fmt.Fprintf(p.w, ", %d)", v.Cap())
205

206 207 208 209 210 211 212 213
	case reflect.Map:
		fmt.Fprintf(p.w, "%s{", v.Type())
		if p.indent != "" && v.Len() != 0 {
			fmt.Fprintf(p.w, "\n")
		}
		for i, k := range v.MapKeys() {
			kv := v.MapIndex(k)
			fmt.Fprintf(p.w, "%s", ni)
214
			p.reprValue(seen, k, ni)
215
			fmt.Fprintf(p.w, ": ")
216
			p.reprValue(seen, kv, ni)
217 218 219 220 221 222 223
			if p.indent != "" {
				fmt.Fprintf(p.w, ",\n")
			} else if i < v.Len()-1 {
				fmt.Fprintf(p.w, ", ")
			}
		}
		fmt.Fprintf(p.w, "%s}", in)
224

225 226 227 228 229 230 231 232 233 234 235 236
	case reflect.Struct:
		fmt.Fprintf(p.w, "%s{", v.Type())
		if p.indent != "" && v.NumField() != 0 {
			fmt.Fprintf(p.w, "\n")
		}
		for i := 0; i < v.NumField(); i++ {
			t := v.Type().Field(i)
			f := v.Field(i)
			if p.omitEmpty && isZero(f) {
				continue
			}
			fmt.Fprintf(p.w, "%s%s: ", ni, t.Name)
237
			p.reprValue(seen, f, ni)
238 239 240 241 242 243 244
			if p.indent != "" {
				fmt.Fprintf(p.w, ",\n")
			} else if i < v.NumField()-1 {
				fmt.Fprintf(p.w, ", ")
			}
		}
		fmt.Fprintf(p.w, "%s}", indent)
245

246 247 248 249 250 251
	case reflect.Ptr:
		if v.IsNil() {
			fmt.Fprintf(p.w, "nil")
			return
		}
		fmt.Fprintf(p.w, "&")
252
		p.reprValue(seen, v.Elem(), indent)
253

254
	case reflect.String:
255
		if t.Name() != "string" || p.alwaysIncludeType {
256 257 258 259
			fmt.Fprintf(p.w, "%s(%q)", t, v.String())
		} else {
			fmt.Fprintf(p.w, "%q", v.String())
		}
260

261 262 263 264
	case reflect.Interface:
		if v.IsNil() {
			fmt.Fprintf(p.w, "interface {}(nil)")
		} else {
265
			p.reprValue(seen, v.Elem(), indent)
266
		}
267

268
	default:
269
		if t.Name() != realKindName[t.Kind()] || p.alwaysIncludeType {
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
			fmt.Fprintf(p.w, "%s(%v)", t, v)
		} else {
			fmt.Fprintf(p.w, "%v", v)
		}
	}
}

// String returns a string representing v.
func String(v interface{}, options ...Option) string {
	w := bytes.NewBuffer(nil)
	options = append([]Option{NoIndent()}, options...)
	p := New(w, options...)
	p.Print(v)
	return w.String()
}

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
func extractOptions(vs ...interface{}) (args []interface{}, options []Option) {
	for _, v := range vs {
		if o, ok := v.(Option); ok {
			options = append(options, o)
		} else {
			args = append(args, v)
		}
	}
	return
}

// Println prints v to os.Stdout, one per line.
func Println(vs ...interface{}) {
	args, options := extractOptions(vs...)
	New(os.Stdout, options...).Println(args...)
301 302 303
}

// Print writes a representation of v to os.Stdout, separated by spaces.
304 305 306
func Print(vs ...interface{}) {
	args, options := extractOptions(vs...)
	New(os.Stdout, options...).Print(args...)
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
}

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
		return v.IsNil()
	}
	return false
}