summaryrefslogtreecommitdiff
path: root/spew/common.go
diff options
context:
space:
mode:
authorTim Hockin <[email protected]>2015-03-31 13:55:04 -0700
committerTim Hockin <[email protected]>2015-04-10 08:46:42 -0700
commit3e6e67c4dcea3ac2f25fd4731abc0e1deaf36216 (patch)
tree42e0d210e3ed2efe1cc4e931135d0956398de246 /spew/common.go
parentfc32781af5e85e548d3f1abaf0fa3dbe8a72495c (diff)
Enable methods to sort map keys and spew itself as last resort
If enabled by flags, try to use methods to stringify map keys and sort on that. If we can't use primitive sorting and we can't use methods, we can still fall back on spew itself. If SpewKeys is enabled, use Sprintf("%#v") to generate a string and sort by that.
Diffstat (limited to 'spew/common.go')
-rw-r--r--spew/common.go75
1 files changed, 68 insertions, 7 deletions
diff --git a/spew/common.go b/spew/common.go
index 81fac0b..8252cd3 100644
--- a/spew/common.go
+++ b/spew/common.go
@@ -17,6 +17,7 @@
package spew
import (
+ "bytes"
"fmt"
"io"
"reflect"
@@ -325,7 +326,61 @@ func printHexPtr(w io.Writer, p uintptr) {
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
// elements to be sorted.
type valuesSorter struct {
- values []reflect.Value
+ values []reflect.Value
+ strings []string // either nil or same len and values
+ cs *ConfigState
+}
+
+// newValuesSorter initializes a valuesSorter instance, which holds a set of
+// surrogate keys on which the data should be sorted. It uses flags in
+// ConfigState to decide if and how to populate those surrogate keys.
+func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
+ vs := &valuesSorter{values: values, cs: cs}
+ if canSortSimply(vs.values[0].Kind()) {
+ return vs
+ }
+ if !cs.DisableMethods {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ b := bytes.Buffer{}
+ if !handleMethods(cs, &b, vs.values[i]) {
+ vs.strings = nil
+ break
+ }
+ vs.strings[i] = b.String()
+ }
+ }
+ if vs.strings == nil && cs.SpewKeys {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
+ }
+ }
+ return vs
+}
+
+// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
+// directly, or whether it should be considered for sorting by surrogate keys
+// (if the ConfigState allows it).
+func canSortSimply(kind reflect.Kind) bool {
+ // This switch parallels valueSortLess, except for the default case.
+ switch kind {
+ case reflect.Bool:
+ return true
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return true
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return true
+ case reflect.Float32, reflect.Float64:
+ return true
+ case reflect.String:
+ return true
+ case reflect.Uintptr:
+ return true
+ case reflect.Array:
+ return true
+ }
+ return false
}
// Len returns the number of values in the slice. It is part of the
@@ -338,6 +393,9 @@ func (s *valuesSorter) Len() int {
// sort.Interface implementation.
func (s *valuesSorter) Swap(i, j int) {
s.values[i], s.values[j] = s.values[j], s.values[i]
+ if s.strings != nil {
+ s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
+ }
}
// valueSortLess returns whether the first value should sort before the second
@@ -375,15 +433,18 @@ func valueSortLess(a, b reflect.Value) bool {
// Less returns whether the value at index i should sort before the
// value at index j. It is part of the sort.Interface implementation.
func (s *valuesSorter) Less(i, j int) bool {
- return valueSortLess(s.values[i], s.values[j])
+ if s.strings == nil {
+ return valueSortLess(s.values[i], s.values[j])
+ }
+ return s.strings[i] < s.strings[j]
}
-// sortValues is a generic sort function for native types: int, uint, bool,
-// string and uintptr. Other inputs are sorted according to their
-// Value.String() value to ensure display stability.
-func sortValues(values []reflect.Value) {
+// sortValues is a sort function that handles both native types and any type that
+// can be converted to error or Stringer. Other inputs are sorted according to
+// their Value.String() value to ensure display stability.
+func sortValues(values []reflect.Value, cs *ConfigState) {
if len(values) == 0 {
return
}
- sort.Sort(&valuesSorter{values})
+ sort.Sort(newValuesSorter(values, cs))
}