...
1
2
3
4
5 package cmpopts
6
7 import (
8 "fmt"
9 "reflect"
10 "unicode"
11 "unicode/utf8"
12
13 "github.com/google/go-cmp/cmp"
14 "github.com/google/go-cmp/cmp/internal/function"
15 )
16
17
18
19
20
21
22
23
24 func IgnoreFields(typ interface{}, names ...string) cmp.Option {
25 sf := newStructFilter(typ, names...)
26 return cmp.FilterPath(sf.filter, cmp.Ignore())
27 }
28
29
30
31 func IgnoreTypes(typs ...interface{}) cmp.Option {
32 tf := newTypeFilter(typs...)
33 return cmp.FilterPath(tf.filter, cmp.Ignore())
34 }
35
36 type typeFilter []reflect.Type
37
38 func newTypeFilter(typs ...interface{}) (tf typeFilter) {
39 for _, typ := range typs {
40 t := reflect.TypeOf(typ)
41 if t == nil {
42
43 panic("cannot determine type; consider using IgnoreInterfaces")
44 }
45 tf = append(tf, t)
46 }
47 return tf
48 }
49 func (tf typeFilter) filter(p cmp.Path) bool {
50 if len(p) < 1 {
51 return false
52 }
53 t := p.Last().Type()
54 for _, ti := range tf {
55 if t.AssignableTo(ti) {
56 return true
57 }
58 }
59 return false
60 }
61
62
63
64
65
66 func IgnoreInterfaces(ifaces interface{}) cmp.Option {
67 tf := newIfaceFilter(ifaces)
68 return cmp.FilterPath(tf.filter, cmp.Ignore())
69 }
70
71 type ifaceFilter []reflect.Type
72
73 func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) {
74 t := reflect.TypeOf(ifaces)
75 if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct {
76 panic("input must be an anonymous struct")
77 }
78 for i := 0; i < t.NumField(); i++ {
79 fi := t.Field(i)
80 switch {
81 case !fi.Anonymous:
82 panic("struct cannot have named fields")
83 case fi.Type.Kind() != reflect.Interface:
84 panic("embedded field must be an interface type")
85 case fi.Type.NumMethod() == 0:
86
87 panic("cannot ignore empty interface")
88 default:
89 tf = append(tf, fi.Type)
90 }
91 }
92 return tf
93 }
94 func (tf ifaceFilter) filter(p cmp.Path) bool {
95 if len(p) < 1 {
96 return false
97 }
98 t := p.Last().Type()
99 for _, ti := range tf {
100 if t.AssignableTo(ti) {
101 return true
102 }
103 if t.Kind() != reflect.Ptr && reflect.PtrTo(t).AssignableTo(ti) {
104 return true
105 }
106 }
107 return false
108 }
109
110
111
112
113
114
115
116
117
118
119 func IgnoreUnexported(typs ...interface{}) cmp.Option {
120 ux := newUnexportedFilter(typs...)
121 return cmp.FilterPath(ux.filter, cmp.Ignore())
122 }
123
124 type unexportedFilter struct{ m map[reflect.Type]bool }
125
126 func newUnexportedFilter(typs ...interface{}) unexportedFilter {
127 ux := unexportedFilter{m: make(map[reflect.Type]bool)}
128 for _, typ := range typs {
129 t := reflect.TypeOf(typ)
130 if t == nil || t.Kind() != reflect.Struct {
131 panic(fmt.Sprintf("%T must be a non-pointer struct", typ))
132 }
133 ux.m[t] = true
134 }
135 return ux
136 }
137 func (xf unexportedFilter) filter(p cmp.Path) bool {
138 sf, ok := p.Index(-1).(cmp.StructField)
139 if !ok {
140 return false
141 }
142 return xf.m[p.Index(-2).Type()] && !isExported(sf.Name())
143 }
144
145
146 func isExported(id string) bool {
147 r, _ := utf8.DecodeRuneInString(id)
148 return unicode.IsUpper(r)
149 }
150
151
152
153
154
155 func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
156 vf := reflect.ValueOf(discardFunc)
157 if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() {
158 panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
159 }
160 return cmp.FilterPath(func(p cmp.Path) bool {
161 si, ok := p.Index(-1).(cmp.SliceIndex)
162 if !ok {
163 return false
164 }
165 if !si.Type().AssignableTo(vf.Type().In(0)) {
166 return false
167 }
168 vx, vy := si.Values()
169 if vx.IsValid() && vf.Call([]reflect.Value{vx})[0].Bool() {
170 return true
171 }
172 if vy.IsValid() && vf.Call([]reflect.Value{vy})[0].Bool() {
173 return true
174 }
175 return false
176 }, cmp.Ignore())
177 }
178
179
180
181
182
183 func IgnoreMapEntries(discardFunc interface{}) cmp.Option {
184 vf := reflect.ValueOf(discardFunc)
185 if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() {
186 panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
187 }
188 return cmp.FilterPath(func(p cmp.Path) bool {
189 mi, ok := p.Index(-1).(cmp.MapIndex)
190 if !ok {
191 return false
192 }
193 if !mi.Key().Type().AssignableTo(vf.Type().In(0)) || !mi.Type().AssignableTo(vf.Type().In(1)) {
194 return false
195 }
196 k := mi.Key()
197 vx, vy := mi.Values()
198 if vx.IsValid() && vf.Call([]reflect.Value{k, vx})[0].Bool() {
199 return true
200 }
201 if vy.IsValid() && vf.Call([]reflect.Value{k, vy})[0].Bool() {
202 return true
203 }
204 return false
205 }, cmp.Ignore())
206 }
207
View as plain text