| // 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} } |
| |
| // val is a value. |
| 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 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()) } |