1
2
3
4
5 package cmpopts
6
7 import (
8 "fmt"
9 "reflect"
10 "sort"
11
12 "github.com/google/go-cmp/cmp"
13 "github.com/google/go-cmp/cmp/internal/function"
14 )
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 func SortSlices(lessFunc interface{}) cmp.Option {
30 vf := reflect.ValueOf(lessFunc)
31 if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
32 panic(fmt.Sprintf("invalid less function: %T", lessFunc))
33 }
34 ss := sliceSorter{vf.Type().In(0), vf}
35 return cmp.FilterValues(ss.filter, cmp.Transformer("cmpopts.SortSlices", ss.sort))
36 }
37
38 type sliceSorter struct {
39 in reflect.Type
40 fnc reflect.Value
41 }
42
43 func (ss sliceSorter) filter(x, y interface{}) bool {
44 vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
45 if !(x != nil && y != nil && vx.Type() == vy.Type()) ||
46 !(vx.Kind() == reflect.Slice && vx.Type().Elem().AssignableTo(ss.in)) ||
47 (vx.Len() <= 1 && vy.Len() <= 1) {
48 return false
49 }
50
51
52 ok1 := sort.SliceIsSorted(x, func(i, j int) bool { return ss.less(vx, i, j) })
53 ok2 := sort.SliceIsSorted(y, func(i, j int) bool { return ss.less(vy, i, j) })
54 return !ok1 || !ok2
55 }
56 func (ss sliceSorter) sort(x interface{}) interface{} {
57 src := reflect.ValueOf(x)
58 dst := reflect.MakeSlice(src.Type(), src.Len(), src.Len())
59 for i := 0; i < src.Len(); i++ {
60 dst.Index(i).Set(src.Index(i))
61 }
62 sort.SliceStable(dst.Interface(), func(i, j int) bool { return ss.less(dst, i, j) })
63 ss.checkSort(dst)
64 return dst.Interface()
65 }
66 func (ss sliceSorter) checkSort(v reflect.Value) {
67 start := -1
68 for i := 1; i < v.Len(); i++ {
69 if ss.less(v, i-1, i) {
70
71 if start >= 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) {
72 panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i)))
73 }
74 start = -1
75 } else if start == -1 {
76 start = i
77 }
78 }
79 }
80 func (ss sliceSorter) less(v reflect.Value, i, j int) bool {
81 vx, vy := v.Index(i), v.Index(j)
82 return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool()
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 func SortMaps(lessFunc interface{}) cmp.Option {
101 vf := reflect.ValueOf(lessFunc)
102 if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
103 panic(fmt.Sprintf("invalid less function: %T", lessFunc))
104 }
105 ms := mapSorter{vf.Type().In(0), vf}
106 return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort))
107 }
108
109 type mapSorter struct {
110 in reflect.Type
111 fnc reflect.Value
112 }
113
114 func (ms mapSorter) filter(x, y interface{}) bool {
115 vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
116 return (x != nil && y != nil && vx.Type() == vy.Type()) &&
117 (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) &&
118 (vx.Len() != 0 || vy.Len() != 0)
119 }
120 func (ms mapSorter) sort(x interface{}) interface{} {
121 src := reflect.ValueOf(x)
122 outType := reflect.StructOf([]reflect.StructField{
123 {Name: "K", Type: src.Type().Key()},
124 {Name: "V", Type: src.Type().Elem()},
125 })
126 dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len())
127 for i, k := range src.MapKeys() {
128 v := reflect.New(outType).Elem()
129 v.Field(0).Set(k)
130 v.Field(1).Set(src.MapIndex(k))
131 dst.Index(i).Set(v)
132 }
133 sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) })
134 ms.checkSort(dst)
135 return dst.Interface()
136 }
137 func (ms mapSorter) checkSort(v reflect.Value) {
138 for i := 1; i < v.Len(); i++ {
139 if !ms.less(v, i-1, i) {
140 panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i)))
141 }
142 }
143 }
144 func (ms mapSorter) less(v reflect.Value, i, j int) bool {
145 vx, vy := v.Index(i).Field(0), v.Index(j).Field(0)
146 return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool()
147 }
148
View as plain text