|
- // Copyright 2016 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- // Package pretty implements a simple pretty-printer. It is intended for
- // debugging the output of tests.
- //
- // It follows pointers and produces multi-line output for complex values like
- // slices, maps and structs.
- package pretty
-
- import (
- "fmt"
- "io"
- "reflect"
- "sort"
- "strings"
- "time"
- )
-
- // Indent is the string output at each level of indentation.
- var Indent = " "
-
- // Value returns a value that will print prettily when used as an
- // argument for the %v or %s format specifiers.
- // With no flags, struct fields and map keys with default values are omitted.
- // With the '+' or '#' flags, all values are displayed.
- //
- // This package does not detect cycles. Attempting to print a Value that
- // contains cycles will result in unbounded recursion.
- func Value(v interface{}) val { return val{v: v} }
-
- type val struct{ v interface{} }
-
- // Format implements the fmt.Formatter interface.
- func (v val) Format(s fmt.State, c rune) {
- if c == 'v' || c == 's' {
- fprint(s, reflect.ValueOf(v.v), state{
- defaults: s.Flag('+') || s.Flag('#'),
- })
- } else {
- fmt.Fprintf(s, "%%!%c(pretty.Val)", c)
- }
- }
-
- type state struct {
- level int
- prefix, suffix string
- defaults bool
- }
-
- const maxLevel = 100
-
- var typeOfTime = reflect.TypeOf(time.Time{})
-
- func fprint(w io.Writer, v reflect.Value, s state) {
- if s.level > maxLevel {
- fmt.Fprintln(w, "pretty: max nested depth exceeded")
- return
- }
- indent := strings.Repeat(Indent, s.level)
- fmt.Fprintf(w, "%s%s", indent, s.prefix)
- if isNil(v) {
- fmt.Fprintf(w, "nil%s", s.suffix)
- return
- }
- if v.Type().Kind() == reflect.Interface {
- v = v.Elem()
- }
- if v.Type() == typeOfTime {
- fmt.Fprintf(w, "%s%s", v.Interface(), s.suffix)
- return
- }
- for v.Type().Kind() == reflect.Ptr {
- fmt.Fprintf(w, "&")
- v = v.Elem()
- }
- switch v.Type().Kind() {
- default:
- fmt.Fprintf(w, "%s%s", short(v), s.suffix)
-
- case reflect.Array:
- fmt.Fprintf(w, "%s{\n", v.Type())
- for i := 0; i < v.Len(); i++ {
- fprint(w, v.Index(i), state{
- level: s.level + 1,
- prefix: "",
- suffix: ",",
- defaults: s.defaults,
- })
- fmt.Fprintln(w)
- }
- fmt.Fprintf(w, "%s}", indent)
-
- case reflect.Slice:
- fmt.Fprintf(w, "%s{", v.Type())
- if v.Len() > 0 {
- fmt.Fprintln(w)
- for i := 0; i < v.Len(); i++ {
- fprint(w, v.Index(i), state{
- level: s.level + 1,
- prefix: "",
- suffix: ",",
- defaults: s.defaults,
- })
- fmt.Fprintln(w)
- }
- }
- fmt.Fprintf(w, "%s}%s", indent, s.suffix)
-
- case reflect.Map:
- fmt.Fprintf(w, "%s{", v.Type())
- if v.Len() > 0 {
- fmt.Fprintln(w)
- keys := v.MapKeys()
- maybeSort(keys, v.Type().Key())
- for _, key := range keys {
- val := v.MapIndex(key)
- if s.defaults || !isDefault(val) {
- fprint(w, val, state{
- level: s.level + 1,
- prefix: short(key) + ": ",
- suffix: ",",
- defaults: s.defaults,
- })
- fmt.Fprintln(w)
- }
- }
- }
- fmt.Fprintf(w, "%s}%s", indent, s.suffix)
-
- case reflect.Struct:
- t := v.Type()
- fmt.Fprintf(w, "%s{\n", t)
- for i := 0; i < t.NumField(); i++ {
- f := v.Field(i)
- if s.defaults || !isDefault(f) {
- fprint(w, f, state{
- level: s.level + 1,
- prefix: t.Field(i).Name + ": ",
- suffix: ",",
- defaults: s.defaults,
- })
- fmt.Fprintln(w)
- }
- }
- fmt.Fprintf(w, "%s}%s", indent, s.suffix)
- }
- }
-
- func isNil(v reflect.Value) bool {
- if !v.IsValid() {
- return true
- }
- switch v.Type().Kind() {
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
- return v.IsNil()
- default:
- return false
- }
- }
-
- func isDefault(v reflect.Value) bool {
- if !v.IsValid() {
- return true
- }
- t := v.Type()
- switch t.Kind() {
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
- return v.IsNil()
- default:
- if !v.CanInterface() {
- return false
- }
- return t.Comparable() && v.Interface() == reflect.Zero(t).Interface()
- }
- }
-
- // short returns a short, one-line string for v.
- func short(v reflect.Value) string {
- if !v.IsValid() {
- return "nil"
- }
- if v.Type().Kind() == reflect.String {
- return fmt.Sprintf("%q", v)
- }
- return fmt.Sprintf("%v", v)
- }
-
- func indent(w io.Writer, level int) {
- for i := 0; i < level; i++ {
- io.WriteString(w, Indent) // ignore errors
- }
- }
-
- func maybeSort(vs []reflect.Value, t reflect.Type) {
- if less := lessFunc(t); less != nil {
- sort.Sort(&sorter{vs, less})
- }
- }
-
- // lessFunc returns a function that implements the "<" operator
- // for the given type, or nil if the type doesn't support "<" .
- func lessFunc(t reflect.Type) func(v1, v2 interface{}) bool {
- switch t.Kind() {
- case reflect.String:
- return func(v1, v2 interface{}) bool { return v1.(string) < v2.(string) }
- case reflect.Int:
- return func(v1, v2 interface{}) bool { return v1.(int) < v2.(int) }
- case reflect.Int8:
- return func(v1, v2 interface{}) bool { return v1.(int8) < v2.(int8) }
- case reflect.Int16:
- return func(v1, v2 interface{}) bool { return v1.(int16) < v2.(int16) }
- case reflect.Int32:
- return func(v1, v2 interface{}) bool { return v1.(int32) < v2.(int32) }
- case reflect.Int64:
- return func(v1, v2 interface{}) bool { return v1.(int64) < v2.(int64) }
- case reflect.Uint:
- return func(v1, v2 interface{}) bool { return v1.(uint) < v2.(uint) }
- case reflect.Uint8:
- return func(v1, v2 interface{}) bool { return v1.(uint8) < v2.(uint8) }
- case reflect.Uint16:
- return func(v1, v2 interface{}) bool { return v1.(uint16) < v2.(uint16) }
- case reflect.Uint32:
- return func(v1, v2 interface{}) bool { return v1.(uint32) < v2.(uint32) }
- case reflect.Uint64:
- return func(v1, v2 interface{}) bool { return v1.(uint64) < v2.(uint64) }
- case reflect.Float32:
- return func(v1, v2 interface{}) bool { return v1.(float32) < v2.(float32) }
- case reflect.Float64:
- return func(v1, v2 interface{}) bool { return v1.(float64) < v2.(float64) }
- default:
- return nil
- }
- }
-
- type sorter struct {
- vs []reflect.Value
- less func(v1, v2 interface{}) bool
- }
-
- func (s *sorter) Len() int { return len(s.vs) }
- func (s *sorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
- func (s *sorter) Less(i, j int) bool { return s.less(s.vs[i].Interface(), s.vs[j].Interface()) }
|