1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsenpackage protocmp 6*1c12ee1eSDan Willemsen 7*1c12ee1eSDan Willemsenimport ( 8*1c12ee1eSDan Willemsen "bytes" 9*1c12ee1eSDan Willemsen "fmt" 10*1c12ee1eSDan Willemsen "math" 11*1c12ee1eSDan Willemsen "reflect" 12*1c12ee1eSDan Willemsen "strings" 13*1c12ee1eSDan Willemsen 14*1c12ee1eSDan Willemsen "github.com/google/go-cmp/cmp" 15*1c12ee1eSDan Willemsen "github.com/google/go-cmp/cmp/cmpopts" 16*1c12ee1eSDan Willemsen 17*1c12ee1eSDan Willemsen "google.golang.org/protobuf/proto" 18*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoreflect" 19*1c12ee1eSDan Willemsen) 20*1c12ee1eSDan Willemsen 21*1c12ee1eSDan Willemsenvar ( 22*1c12ee1eSDan Willemsen enumReflectType = reflect.TypeOf(Enum{}) 23*1c12ee1eSDan Willemsen messageReflectType = reflect.TypeOf(Message{}) 24*1c12ee1eSDan Willemsen) 25*1c12ee1eSDan Willemsen 26*1c12ee1eSDan Willemsen// FilterEnum filters opt to only be applicable on standalone Enums, 27*1c12ee1eSDan Willemsen// singular fields of enums, list fields of enums, or map fields of enum values, 28*1c12ee1eSDan Willemsen// where the enum is the same type as the specified enum. 29*1c12ee1eSDan Willemsen// 30*1c12ee1eSDan Willemsen// The Go type of the last path step may be an: 31*1c12ee1eSDan Willemsen// - Enum for singular fields, elements of a repeated field, 32*1c12ee1eSDan Willemsen// values of a map field, or standalone Enums 33*1c12ee1eSDan Willemsen// - []Enum for list fields 34*1c12ee1eSDan Willemsen// - map[K]Enum for map fields 35*1c12ee1eSDan Willemsen// - interface{} for a Message map entry value 36*1c12ee1eSDan Willemsen// 37*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 38*1c12ee1eSDan Willemsenfunc FilterEnum(enum protoreflect.Enum, opt cmp.Option) cmp.Option { 39*1c12ee1eSDan Willemsen return FilterDescriptor(enum.Descriptor(), opt) 40*1c12ee1eSDan Willemsen} 41*1c12ee1eSDan Willemsen 42*1c12ee1eSDan Willemsen// FilterMessage filters opt to only be applicable on standalone Messages, 43*1c12ee1eSDan Willemsen// singular fields of messages, list fields of messages, or map fields of 44*1c12ee1eSDan Willemsen// message values, where the message is the same type as the specified message. 45*1c12ee1eSDan Willemsen// 46*1c12ee1eSDan Willemsen// The Go type of the last path step may be an: 47*1c12ee1eSDan Willemsen// - Message for singular fields, elements of a repeated field, 48*1c12ee1eSDan Willemsen// values of a map field, or standalone Messages 49*1c12ee1eSDan Willemsen// - []Message for list fields 50*1c12ee1eSDan Willemsen// - map[K]Message for map fields 51*1c12ee1eSDan Willemsen// - interface{} for a Message map entry value 52*1c12ee1eSDan Willemsen// 53*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 54*1c12ee1eSDan Willemsenfunc FilterMessage(message proto.Message, opt cmp.Option) cmp.Option { 55*1c12ee1eSDan Willemsen return FilterDescriptor(message.ProtoReflect().Descriptor(), opt) 56*1c12ee1eSDan Willemsen} 57*1c12ee1eSDan Willemsen 58*1c12ee1eSDan Willemsen// FilterField filters opt to only be applicable on the specified field 59*1c12ee1eSDan Willemsen// in the message. It panics if a field of the given name does not exist. 60*1c12ee1eSDan Willemsen// 61*1c12ee1eSDan Willemsen// The Go type of the last path step may be an: 62*1c12ee1eSDan Willemsen// - T for singular fields 63*1c12ee1eSDan Willemsen// - []T for list fields 64*1c12ee1eSDan Willemsen// - map[K]T for map fields 65*1c12ee1eSDan Willemsen// - interface{} for a Message map entry value 66*1c12ee1eSDan Willemsen// 67*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 68*1c12ee1eSDan Willemsenfunc FilterField(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { 69*1c12ee1eSDan Willemsen md := message.ProtoReflect().Descriptor() 70*1c12ee1eSDan Willemsen return FilterDescriptor(mustFindFieldDescriptor(md, name), opt) 71*1c12ee1eSDan Willemsen} 72*1c12ee1eSDan Willemsen 73*1c12ee1eSDan Willemsen// FilterOneof filters opt to only be applicable on all fields within the 74*1c12ee1eSDan Willemsen// specified oneof in the message. It panics if a oneof of the given name 75*1c12ee1eSDan Willemsen// does not exist. 76*1c12ee1eSDan Willemsen// 77*1c12ee1eSDan Willemsen// The Go type of the last path step may be an: 78*1c12ee1eSDan Willemsen// - T for singular fields 79*1c12ee1eSDan Willemsen// - []T for list fields 80*1c12ee1eSDan Willemsen// - map[K]T for map fields 81*1c12ee1eSDan Willemsen// - interface{} for a Message map entry value 82*1c12ee1eSDan Willemsen// 83*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 84*1c12ee1eSDan Willemsenfunc FilterOneof(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { 85*1c12ee1eSDan Willemsen md := message.ProtoReflect().Descriptor() 86*1c12ee1eSDan Willemsen return FilterDescriptor(mustFindOneofDescriptor(md, name), opt) 87*1c12ee1eSDan Willemsen} 88*1c12ee1eSDan Willemsen 89*1c12ee1eSDan Willemsen// FilterDescriptor ignores the specified descriptor. 90*1c12ee1eSDan Willemsen// 91*1c12ee1eSDan Willemsen// The following descriptor types may be specified: 92*1c12ee1eSDan Willemsen// - protoreflect.EnumDescriptor 93*1c12ee1eSDan Willemsen// - protoreflect.MessageDescriptor 94*1c12ee1eSDan Willemsen// - protoreflect.FieldDescriptor 95*1c12ee1eSDan Willemsen// - protoreflect.OneofDescriptor 96*1c12ee1eSDan Willemsen// 97*1c12ee1eSDan Willemsen// For the behavior of each, see the corresponding filter function. 98*1c12ee1eSDan Willemsen// Since this filter accepts a protoreflect.FieldDescriptor, it can be used 99*1c12ee1eSDan Willemsen// to also filter for extension fields as a protoreflect.ExtensionDescriptor 100*1c12ee1eSDan Willemsen// is just an alias to protoreflect.FieldDescriptor. 101*1c12ee1eSDan Willemsen// 102*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 103*1c12ee1eSDan Willemsenfunc FilterDescriptor(desc protoreflect.Descriptor, opt cmp.Option) cmp.Option { 104*1c12ee1eSDan Willemsen f := newNameFilters(desc) 105*1c12ee1eSDan Willemsen return cmp.FilterPath(f.Filter, opt) 106*1c12ee1eSDan Willemsen} 107*1c12ee1eSDan Willemsen 108*1c12ee1eSDan Willemsen// IgnoreEnums ignores all enums of the specified types. 109*1c12ee1eSDan Willemsen// It is equivalent to FilterEnum(enum, cmp.Ignore()) for each enum. 110*1c12ee1eSDan Willemsen// 111*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 112*1c12ee1eSDan Willemsenfunc IgnoreEnums(enums ...protoreflect.Enum) cmp.Option { 113*1c12ee1eSDan Willemsen var ds []protoreflect.Descriptor 114*1c12ee1eSDan Willemsen for _, e := range enums { 115*1c12ee1eSDan Willemsen ds = append(ds, e.Descriptor()) 116*1c12ee1eSDan Willemsen } 117*1c12ee1eSDan Willemsen return IgnoreDescriptors(ds...) 118*1c12ee1eSDan Willemsen} 119*1c12ee1eSDan Willemsen 120*1c12ee1eSDan Willemsen// IgnoreMessages ignores all messages of the specified types. 121*1c12ee1eSDan Willemsen// It is equivalent to FilterMessage(message, cmp.Ignore()) for each message. 122*1c12ee1eSDan Willemsen// 123*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 124*1c12ee1eSDan Willemsenfunc IgnoreMessages(messages ...proto.Message) cmp.Option { 125*1c12ee1eSDan Willemsen var ds []protoreflect.Descriptor 126*1c12ee1eSDan Willemsen for _, m := range messages { 127*1c12ee1eSDan Willemsen ds = append(ds, m.ProtoReflect().Descriptor()) 128*1c12ee1eSDan Willemsen } 129*1c12ee1eSDan Willemsen return IgnoreDescriptors(ds...) 130*1c12ee1eSDan Willemsen} 131*1c12ee1eSDan Willemsen 132*1c12ee1eSDan Willemsen// IgnoreFields ignores the specified fields in the specified message. 133*1c12ee1eSDan Willemsen// It is equivalent to FilterField(message, name, cmp.Ignore()) for each field 134*1c12ee1eSDan Willemsen// in the message. 135*1c12ee1eSDan Willemsen// 136*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 137*1c12ee1eSDan Willemsenfunc IgnoreFields(message proto.Message, names ...protoreflect.Name) cmp.Option { 138*1c12ee1eSDan Willemsen var ds []protoreflect.Descriptor 139*1c12ee1eSDan Willemsen md := message.ProtoReflect().Descriptor() 140*1c12ee1eSDan Willemsen for _, s := range names { 141*1c12ee1eSDan Willemsen ds = append(ds, mustFindFieldDescriptor(md, s)) 142*1c12ee1eSDan Willemsen } 143*1c12ee1eSDan Willemsen return IgnoreDescriptors(ds...) 144*1c12ee1eSDan Willemsen} 145*1c12ee1eSDan Willemsen 146*1c12ee1eSDan Willemsen// IgnoreOneofs ignores fields of the specified oneofs in the specified message. 147*1c12ee1eSDan Willemsen// It is equivalent to FilterOneof(message, name, cmp.Ignore()) for each oneof 148*1c12ee1eSDan Willemsen// in the message. 149*1c12ee1eSDan Willemsen// 150*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 151*1c12ee1eSDan Willemsenfunc IgnoreOneofs(message proto.Message, names ...protoreflect.Name) cmp.Option { 152*1c12ee1eSDan Willemsen var ds []protoreflect.Descriptor 153*1c12ee1eSDan Willemsen md := message.ProtoReflect().Descriptor() 154*1c12ee1eSDan Willemsen for _, s := range names { 155*1c12ee1eSDan Willemsen ds = append(ds, mustFindOneofDescriptor(md, s)) 156*1c12ee1eSDan Willemsen } 157*1c12ee1eSDan Willemsen return IgnoreDescriptors(ds...) 158*1c12ee1eSDan Willemsen} 159*1c12ee1eSDan Willemsen 160*1c12ee1eSDan Willemsen// IgnoreDescriptors ignores the specified set of descriptors. 161*1c12ee1eSDan Willemsen// It is equivalent to FilterDescriptor(desc, cmp.Ignore()) for each descriptor. 162*1c12ee1eSDan Willemsen// 163*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 164*1c12ee1eSDan Willemsenfunc IgnoreDescriptors(descs ...protoreflect.Descriptor) cmp.Option { 165*1c12ee1eSDan Willemsen return cmp.FilterPath(newNameFilters(descs...).Filter, cmp.Ignore()) 166*1c12ee1eSDan Willemsen} 167*1c12ee1eSDan Willemsen 168*1c12ee1eSDan Willemsenfunc mustFindFieldDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.FieldDescriptor { 169*1c12ee1eSDan Willemsen d := findDescriptor(md, s) 170*1c12ee1eSDan Willemsen if fd, ok := d.(protoreflect.FieldDescriptor); ok && fd.TextName() == string(s) { 171*1c12ee1eSDan Willemsen return fd 172*1c12ee1eSDan Willemsen } 173*1c12ee1eSDan Willemsen 174*1c12ee1eSDan Willemsen var suggestion string 175*1c12ee1eSDan Willemsen switch d := d.(type) { 176*1c12ee1eSDan Willemsen case protoreflect.FieldDescriptor: 177*1c12ee1eSDan Willemsen suggestion = fmt.Sprintf("; consider specifying field %q instead", d.TextName()) 178*1c12ee1eSDan Willemsen case protoreflect.OneofDescriptor: 179*1c12ee1eSDan Willemsen suggestion = fmt.Sprintf("; consider specifying oneof %q with IgnoreOneofs instead", d.Name()) 180*1c12ee1eSDan Willemsen } 181*1c12ee1eSDan Willemsen panic(fmt.Sprintf("message %q has no field %q%s", md.FullName(), s, suggestion)) 182*1c12ee1eSDan Willemsen} 183*1c12ee1eSDan Willemsen 184*1c12ee1eSDan Willemsenfunc mustFindOneofDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.OneofDescriptor { 185*1c12ee1eSDan Willemsen d := findDescriptor(md, s) 186*1c12ee1eSDan Willemsen if od, ok := d.(protoreflect.OneofDescriptor); ok && d.Name() == s { 187*1c12ee1eSDan Willemsen return od 188*1c12ee1eSDan Willemsen } 189*1c12ee1eSDan Willemsen 190*1c12ee1eSDan Willemsen var suggestion string 191*1c12ee1eSDan Willemsen switch d := d.(type) { 192*1c12ee1eSDan Willemsen case protoreflect.OneofDescriptor: 193*1c12ee1eSDan Willemsen suggestion = fmt.Sprintf("; consider specifying oneof %q instead", d.Name()) 194*1c12ee1eSDan Willemsen case protoreflect.FieldDescriptor: 195*1c12ee1eSDan Willemsen suggestion = fmt.Sprintf("; consider specifying field %q with IgnoreFields instead", d.TextName()) 196*1c12ee1eSDan Willemsen } 197*1c12ee1eSDan Willemsen panic(fmt.Sprintf("message %q has no oneof %q%s", md.FullName(), s, suggestion)) 198*1c12ee1eSDan Willemsen} 199*1c12ee1eSDan Willemsen 200*1c12ee1eSDan Willemsenfunc findDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.Descriptor { 201*1c12ee1eSDan Willemsen // Exact match. 202*1c12ee1eSDan Willemsen if fd := md.Fields().ByTextName(string(s)); fd != nil { 203*1c12ee1eSDan Willemsen return fd 204*1c12ee1eSDan Willemsen } 205*1c12ee1eSDan Willemsen if od := md.Oneofs().ByName(s); od != nil && !od.IsSynthetic() { 206*1c12ee1eSDan Willemsen return od 207*1c12ee1eSDan Willemsen } 208*1c12ee1eSDan Willemsen 209*1c12ee1eSDan Willemsen // Best-effort match. 210*1c12ee1eSDan Willemsen // 211*1c12ee1eSDan Willemsen // It's a common user mistake to use the CamelCased field name as it appears 212*1c12ee1eSDan Willemsen // in the generated Go struct. Instead of complaining that it doesn't exist, 213*1c12ee1eSDan Willemsen // suggest the real protobuf name that the user may have desired. 214*1c12ee1eSDan Willemsen normalize := func(s protoreflect.Name) string { 215*1c12ee1eSDan Willemsen return strings.Replace(strings.ToLower(string(s)), "_", "", -1) 216*1c12ee1eSDan Willemsen } 217*1c12ee1eSDan Willemsen for i := 0; i < md.Fields().Len(); i++ { 218*1c12ee1eSDan Willemsen if fd := md.Fields().Get(i); normalize(fd.Name()) == normalize(s) { 219*1c12ee1eSDan Willemsen return fd 220*1c12ee1eSDan Willemsen } 221*1c12ee1eSDan Willemsen } 222*1c12ee1eSDan Willemsen for i := 0; i < md.Oneofs().Len(); i++ { 223*1c12ee1eSDan Willemsen if od := md.Oneofs().Get(i); normalize(od.Name()) == normalize(s) { 224*1c12ee1eSDan Willemsen return od 225*1c12ee1eSDan Willemsen } 226*1c12ee1eSDan Willemsen } 227*1c12ee1eSDan Willemsen return nil 228*1c12ee1eSDan Willemsen} 229*1c12ee1eSDan Willemsen 230*1c12ee1eSDan Willemsentype nameFilters struct { 231*1c12ee1eSDan Willemsen names map[protoreflect.FullName]bool 232*1c12ee1eSDan Willemsen} 233*1c12ee1eSDan Willemsen 234*1c12ee1eSDan Willemsenfunc newNameFilters(descs ...protoreflect.Descriptor) *nameFilters { 235*1c12ee1eSDan Willemsen f := &nameFilters{names: make(map[protoreflect.FullName]bool)} 236*1c12ee1eSDan Willemsen for _, d := range descs { 237*1c12ee1eSDan Willemsen switch d := d.(type) { 238*1c12ee1eSDan Willemsen case protoreflect.EnumDescriptor: 239*1c12ee1eSDan Willemsen f.names[d.FullName()] = true 240*1c12ee1eSDan Willemsen case protoreflect.MessageDescriptor: 241*1c12ee1eSDan Willemsen f.names[d.FullName()] = true 242*1c12ee1eSDan Willemsen case protoreflect.FieldDescriptor: 243*1c12ee1eSDan Willemsen f.names[d.FullName()] = true 244*1c12ee1eSDan Willemsen case protoreflect.OneofDescriptor: 245*1c12ee1eSDan Willemsen for i := 0; i < d.Fields().Len(); i++ { 246*1c12ee1eSDan Willemsen f.names[d.Fields().Get(i).FullName()] = true 247*1c12ee1eSDan Willemsen } 248*1c12ee1eSDan Willemsen default: 249*1c12ee1eSDan Willemsen panic("invalid descriptor type") 250*1c12ee1eSDan Willemsen } 251*1c12ee1eSDan Willemsen } 252*1c12ee1eSDan Willemsen return f 253*1c12ee1eSDan Willemsen} 254*1c12ee1eSDan Willemsen 255*1c12ee1eSDan Willemsenfunc (f *nameFilters) Filter(p cmp.Path) bool { 256*1c12ee1eSDan Willemsen vx, vy := p.Last().Values() 257*1c12ee1eSDan Willemsen return (f.filterValue(vx) && f.filterValue(vy)) || f.filterFields(p) 258*1c12ee1eSDan Willemsen} 259*1c12ee1eSDan Willemsen 260*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterFields(p cmp.Path) bool { 261*1c12ee1eSDan Willemsen // Trim off trailing type-assertions so that the filter can match on the 262*1c12ee1eSDan Willemsen // concrete value held within an interface value. 263*1c12ee1eSDan Willemsen if _, ok := p.Last().(cmp.TypeAssertion); ok { 264*1c12ee1eSDan Willemsen p = p[:len(p)-1] 265*1c12ee1eSDan Willemsen } 266*1c12ee1eSDan Willemsen 267*1c12ee1eSDan Willemsen // Filter for Message maps. 268*1c12ee1eSDan Willemsen mi, ok := p.Index(-1).(cmp.MapIndex) 269*1c12ee1eSDan Willemsen if !ok { 270*1c12ee1eSDan Willemsen return false 271*1c12ee1eSDan Willemsen } 272*1c12ee1eSDan Willemsen ps := p.Index(-2) 273*1c12ee1eSDan Willemsen if ps.Type() != messageReflectType { 274*1c12ee1eSDan Willemsen return false 275*1c12ee1eSDan Willemsen } 276*1c12ee1eSDan Willemsen 277*1c12ee1eSDan Willemsen // Check field name. 278*1c12ee1eSDan Willemsen vx, vy := ps.Values() 279*1c12ee1eSDan Willemsen mx := vx.Interface().(Message) 280*1c12ee1eSDan Willemsen my := vy.Interface().(Message) 281*1c12ee1eSDan Willemsen k := mi.Key().String() 282*1c12ee1eSDan Willemsen if f.filterFieldName(mx, k) && f.filterFieldName(my, k) { 283*1c12ee1eSDan Willemsen return true 284*1c12ee1eSDan Willemsen } 285*1c12ee1eSDan Willemsen 286*1c12ee1eSDan Willemsen // Check field value. 287*1c12ee1eSDan Willemsen vx, vy = mi.Values() 288*1c12ee1eSDan Willemsen if f.filterFieldValue(vx) && f.filterFieldValue(vy) { 289*1c12ee1eSDan Willemsen return true 290*1c12ee1eSDan Willemsen } 291*1c12ee1eSDan Willemsen 292*1c12ee1eSDan Willemsen return false 293*1c12ee1eSDan Willemsen} 294*1c12ee1eSDan Willemsen 295*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterFieldName(m Message, k string) bool { 296*1c12ee1eSDan Willemsen if _, ok := m[k]; !ok { 297*1c12ee1eSDan Willemsen return true // treat missing fields as already filtered 298*1c12ee1eSDan Willemsen } 299*1c12ee1eSDan Willemsen var fd protoreflect.FieldDescriptor 300*1c12ee1eSDan Willemsen switch mm := m[messageTypeKey].(messageMeta); { 301*1c12ee1eSDan Willemsen case protoreflect.Name(k).IsValid(): 302*1c12ee1eSDan Willemsen fd = mm.md.Fields().ByTextName(k) 303*1c12ee1eSDan Willemsen default: 304*1c12ee1eSDan Willemsen fd = mm.xds[k] 305*1c12ee1eSDan Willemsen } 306*1c12ee1eSDan Willemsen if fd != nil { 307*1c12ee1eSDan Willemsen return f.names[fd.FullName()] 308*1c12ee1eSDan Willemsen } 309*1c12ee1eSDan Willemsen return false 310*1c12ee1eSDan Willemsen} 311*1c12ee1eSDan Willemsen 312*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterFieldValue(v reflect.Value) bool { 313*1c12ee1eSDan Willemsen if !v.IsValid() { 314*1c12ee1eSDan Willemsen return true // implies missing slice element or map entry 315*1c12ee1eSDan Willemsen } 316*1c12ee1eSDan Willemsen v = v.Elem() // map entries are always populated values 317*1c12ee1eSDan Willemsen switch t := v.Type(); { 318*1c12ee1eSDan Willemsen case t == enumReflectType || t == messageReflectType: 319*1c12ee1eSDan Willemsen // Check for singular message or enum field. 320*1c12ee1eSDan Willemsen return f.filterValue(v) 321*1c12ee1eSDan Willemsen case t.Kind() == reflect.Slice && (t.Elem() == enumReflectType || t.Elem() == messageReflectType): 322*1c12ee1eSDan Willemsen // Check for list field of enum or message type. 323*1c12ee1eSDan Willemsen return f.filterValue(v.Index(0)) 324*1c12ee1eSDan Willemsen case t.Kind() == reflect.Map && (t.Elem() == enumReflectType || t.Elem() == messageReflectType): 325*1c12ee1eSDan Willemsen // Check for map field of enum or message type. 326*1c12ee1eSDan Willemsen return f.filterValue(v.MapIndex(v.MapKeys()[0])) 327*1c12ee1eSDan Willemsen } 328*1c12ee1eSDan Willemsen return false 329*1c12ee1eSDan Willemsen} 330*1c12ee1eSDan Willemsen 331*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterValue(v reflect.Value) bool { 332*1c12ee1eSDan Willemsen if !v.IsValid() { 333*1c12ee1eSDan Willemsen return true // implies missing slice element or map entry 334*1c12ee1eSDan Willemsen } 335*1c12ee1eSDan Willemsen if !v.CanInterface() { 336*1c12ee1eSDan Willemsen return false // implies unexported struct field 337*1c12ee1eSDan Willemsen } 338*1c12ee1eSDan Willemsen switch v := v.Interface().(type) { 339*1c12ee1eSDan Willemsen case Enum: 340*1c12ee1eSDan Willemsen return v.Descriptor() != nil && f.names[v.Descriptor().FullName()] 341*1c12ee1eSDan Willemsen case Message: 342*1c12ee1eSDan Willemsen return v.Descriptor() != nil && f.names[v.Descriptor().FullName()] 343*1c12ee1eSDan Willemsen } 344*1c12ee1eSDan Willemsen return false 345*1c12ee1eSDan Willemsen} 346*1c12ee1eSDan Willemsen 347*1c12ee1eSDan Willemsen// IgnoreDefaultScalars ignores singular scalars that are unpopulated or 348*1c12ee1eSDan Willemsen// explicitly set to the default value. 349*1c12ee1eSDan Willemsen// This option does not effect elements in a list or entries in a map. 350*1c12ee1eSDan Willemsen// 351*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 352*1c12ee1eSDan Willemsenfunc IgnoreDefaultScalars() cmp.Option { 353*1c12ee1eSDan Willemsen return cmp.FilterPath(func(p cmp.Path) bool { 354*1c12ee1eSDan Willemsen // Filter for Message maps. 355*1c12ee1eSDan Willemsen mi, ok := p.Index(-1).(cmp.MapIndex) 356*1c12ee1eSDan Willemsen if !ok { 357*1c12ee1eSDan Willemsen return false 358*1c12ee1eSDan Willemsen } 359*1c12ee1eSDan Willemsen ps := p.Index(-2) 360*1c12ee1eSDan Willemsen if ps.Type() != messageReflectType { 361*1c12ee1eSDan Willemsen return false 362*1c12ee1eSDan Willemsen } 363*1c12ee1eSDan Willemsen 364*1c12ee1eSDan Willemsen // Check whether both fields are default or unpopulated scalars. 365*1c12ee1eSDan Willemsen vx, vy := ps.Values() 366*1c12ee1eSDan Willemsen mx := vx.Interface().(Message) 367*1c12ee1eSDan Willemsen my := vy.Interface().(Message) 368*1c12ee1eSDan Willemsen k := mi.Key().String() 369*1c12ee1eSDan Willemsen return isDefaultScalar(mx, k) && isDefaultScalar(my, k) 370*1c12ee1eSDan Willemsen }, cmp.Ignore()) 371*1c12ee1eSDan Willemsen} 372*1c12ee1eSDan Willemsen 373*1c12ee1eSDan Willemsenfunc isDefaultScalar(m Message, k string) bool { 374*1c12ee1eSDan Willemsen if _, ok := m[k]; !ok { 375*1c12ee1eSDan Willemsen return true 376*1c12ee1eSDan Willemsen } 377*1c12ee1eSDan Willemsen 378*1c12ee1eSDan Willemsen var fd protoreflect.FieldDescriptor 379*1c12ee1eSDan Willemsen switch mm := m[messageTypeKey].(messageMeta); { 380*1c12ee1eSDan Willemsen case protoreflect.Name(k).IsValid(): 381*1c12ee1eSDan Willemsen fd = mm.md.Fields().ByTextName(k) 382*1c12ee1eSDan Willemsen default: 383*1c12ee1eSDan Willemsen fd = mm.xds[k] 384*1c12ee1eSDan Willemsen } 385*1c12ee1eSDan Willemsen if fd == nil || !fd.Default().IsValid() { 386*1c12ee1eSDan Willemsen return false 387*1c12ee1eSDan Willemsen } 388*1c12ee1eSDan Willemsen switch fd.Kind() { 389*1c12ee1eSDan Willemsen case protoreflect.BytesKind: 390*1c12ee1eSDan Willemsen v, ok := m[k].([]byte) 391*1c12ee1eSDan Willemsen return ok && bytes.Equal(fd.Default().Bytes(), v) 392*1c12ee1eSDan Willemsen case protoreflect.FloatKind: 393*1c12ee1eSDan Willemsen v, ok := m[k].(float32) 394*1c12ee1eSDan Willemsen return ok && equalFloat64(fd.Default().Float(), float64(v)) 395*1c12ee1eSDan Willemsen case protoreflect.DoubleKind: 396*1c12ee1eSDan Willemsen v, ok := m[k].(float64) 397*1c12ee1eSDan Willemsen return ok && equalFloat64(fd.Default().Float(), float64(v)) 398*1c12ee1eSDan Willemsen case protoreflect.EnumKind: 399*1c12ee1eSDan Willemsen v, ok := m[k].(Enum) 400*1c12ee1eSDan Willemsen return ok && fd.Default().Enum() == v.Number() 401*1c12ee1eSDan Willemsen default: 402*1c12ee1eSDan Willemsen return reflect.DeepEqual(fd.Default().Interface(), m[k]) 403*1c12ee1eSDan Willemsen } 404*1c12ee1eSDan Willemsen} 405*1c12ee1eSDan Willemsen 406*1c12ee1eSDan Willemsenfunc equalFloat64(x, y float64) bool { 407*1c12ee1eSDan Willemsen return x == y || (math.IsNaN(x) && math.IsNaN(y)) 408*1c12ee1eSDan Willemsen} 409*1c12ee1eSDan Willemsen 410*1c12ee1eSDan Willemsen// IgnoreEmptyMessages ignores messages that are empty or unpopulated. 411*1c12ee1eSDan Willemsen// It applies to standalone Messages, singular message fields, 412*1c12ee1eSDan Willemsen// list fields of messages, and map fields of message values. 413*1c12ee1eSDan Willemsen// 414*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 415*1c12ee1eSDan Willemsenfunc IgnoreEmptyMessages() cmp.Option { 416*1c12ee1eSDan Willemsen return cmp.FilterPath(func(p cmp.Path) bool { 417*1c12ee1eSDan Willemsen vx, vy := p.Last().Values() 418*1c12ee1eSDan Willemsen return (isEmptyMessage(vx) && isEmptyMessage(vy)) || isEmptyMessageFields(p) 419*1c12ee1eSDan Willemsen }, cmp.Ignore()) 420*1c12ee1eSDan Willemsen} 421*1c12ee1eSDan Willemsen 422*1c12ee1eSDan Willemsenfunc isEmptyMessageFields(p cmp.Path) bool { 423*1c12ee1eSDan Willemsen // Filter for Message maps. 424*1c12ee1eSDan Willemsen mi, ok := p.Index(-1).(cmp.MapIndex) 425*1c12ee1eSDan Willemsen if !ok { 426*1c12ee1eSDan Willemsen return false 427*1c12ee1eSDan Willemsen } 428*1c12ee1eSDan Willemsen ps := p.Index(-2) 429*1c12ee1eSDan Willemsen if ps.Type() != messageReflectType { 430*1c12ee1eSDan Willemsen return false 431*1c12ee1eSDan Willemsen } 432*1c12ee1eSDan Willemsen 433*1c12ee1eSDan Willemsen // Check field value. 434*1c12ee1eSDan Willemsen vx, vy := mi.Values() 435*1c12ee1eSDan Willemsen if isEmptyMessageFieldValue(vx) && isEmptyMessageFieldValue(vy) { 436*1c12ee1eSDan Willemsen return true 437*1c12ee1eSDan Willemsen } 438*1c12ee1eSDan Willemsen 439*1c12ee1eSDan Willemsen return false 440*1c12ee1eSDan Willemsen} 441*1c12ee1eSDan Willemsen 442*1c12ee1eSDan Willemsenfunc isEmptyMessageFieldValue(v reflect.Value) bool { 443*1c12ee1eSDan Willemsen if !v.IsValid() { 444*1c12ee1eSDan Willemsen return true // implies missing slice element or map entry 445*1c12ee1eSDan Willemsen } 446*1c12ee1eSDan Willemsen v = v.Elem() // map entries are always populated values 447*1c12ee1eSDan Willemsen switch t := v.Type(); { 448*1c12ee1eSDan Willemsen case t == messageReflectType: 449*1c12ee1eSDan Willemsen // Check singular field for empty message. 450*1c12ee1eSDan Willemsen if !isEmptyMessage(v) { 451*1c12ee1eSDan Willemsen return false 452*1c12ee1eSDan Willemsen } 453*1c12ee1eSDan Willemsen case t.Kind() == reflect.Slice && t.Elem() == messageReflectType: 454*1c12ee1eSDan Willemsen // Check list field for all empty message elements. 455*1c12ee1eSDan Willemsen for i := 0; i < v.Len(); i++ { 456*1c12ee1eSDan Willemsen if !isEmptyMessage(v.Index(i)) { 457*1c12ee1eSDan Willemsen return false 458*1c12ee1eSDan Willemsen } 459*1c12ee1eSDan Willemsen } 460*1c12ee1eSDan Willemsen case t.Kind() == reflect.Map && t.Elem() == messageReflectType: 461*1c12ee1eSDan Willemsen // Check map field for all empty message values. 462*1c12ee1eSDan Willemsen for _, k := range v.MapKeys() { 463*1c12ee1eSDan Willemsen if !isEmptyMessage(v.MapIndex(k)) { 464*1c12ee1eSDan Willemsen return false 465*1c12ee1eSDan Willemsen } 466*1c12ee1eSDan Willemsen } 467*1c12ee1eSDan Willemsen default: 468*1c12ee1eSDan Willemsen return false 469*1c12ee1eSDan Willemsen } 470*1c12ee1eSDan Willemsen return true 471*1c12ee1eSDan Willemsen} 472*1c12ee1eSDan Willemsen 473*1c12ee1eSDan Willemsenfunc isEmptyMessage(v reflect.Value) bool { 474*1c12ee1eSDan Willemsen if !v.IsValid() { 475*1c12ee1eSDan Willemsen return true // implies missing slice element or map entry 476*1c12ee1eSDan Willemsen } 477*1c12ee1eSDan Willemsen if !v.CanInterface() { 478*1c12ee1eSDan Willemsen return false // implies unexported struct field 479*1c12ee1eSDan Willemsen } 480*1c12ee1eSDan Willemsen if m, ok := v.Interface().(Message); ok { 481*1c12ee1eSDan Willemsen for k := range m { 482*1c12ee1eSDan Willemsen if k != messageTypeKey && k != messageInvalidKey { 483*1c12ee1eSDan Willemsen return false 484*1c12ee1eSDan Willemsen } 485*1c12ee1eSDan Willemsen } 486*1c12ee1eSDan Willemsen return true 487*1c12ee1eSDan Willemsen } 488*1c12ee1eSDan Willemsen return false 489*1c12ee1eSDan Willemsen} 490*1c12ee1eSDan Willemsen 491*1c12ee1eSDan Willemsen// IgnoreUnknown ignores unknown fields in all messages. 492*1c12ee1eSDan Willemsen// 493*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 494*1c12ee1eSDan Willemsenfunc IgnoreUnknown() cmp.Option { 495*1c12ee1eSDan Willemsen return cmp.FilterPath(func(p cmp.Path) bool { 496*1c12ee1eSDan Willemsen // Filter for Message maps. 497*1c12ee1eSDan Willemsen mi, ok := p.Index(-1).(cmp.MapIndex) 498*1c12ee1eSDan Willemsen if !ok { 499*1c12ee1eSDan Willemsen return false 500*1c12ee1eSDan Willemsen } 501*1c12ee1eSDan Willemsen ps := p.Index(-2) 502*1c12ee1eSDan Willemsen if ps.Type() != messageReflectType { 503*1c12ee1eSDan Willemsen return false 504*1c12ee1eSDan Willemsen } 505*1c12ee1eSDan Willemsen 506*1c12ee1eSDan Willemsen // Filter for unknown fields (which always have a numeric map key). 507*1c12ee1eSDan Willemsen return strings.Trim(mi.Key().String(), "0123456789") == "" 508*1c12ee1eSDan Willemsen }, cmp.Ignore()) 509*1c12ee1eSDan Willemsen} 510*1c12ee1eSDan Willemsen 511*1c12ee1eSDan Willemsen// SortRepeated sorts repeated fields of the specified element type. 512*1c12ee1eSDan Willemsen// The less function must be of the form "func(T, T) bool" where T is the 513*1c12ee1eSDan Willemsen// Go element type for the repeated field kind. 514*1c12ee1eSDan Willemsen// 515*1c12ee1eSDan Willemsen// The element type T can be one of the following: 516*1c12ee1eSDan Willemsen// - Go type for a protobuf scalar kind except for an enum 517*1c12ee1eSDan Willemsen// (i.e., bool, int32, int64, uint32, uint64, float32, float64, string, and []byte) 518*1c12ee1eSDan Willemsen// - E where E is a concrete enum type that implements protoreflect.Enum 519*1c12ee1eSDan Willemsen// - M where M is a concrete message type that implement proto.Message 520*1c12ee1eSDan Willemsen// 521*1c12ee1eSDan Willemsen// This option only applies to repeated fields within a protobuf message. 522*1c12ee1eSDan Willemsen// It does not operate on higher-order Go types that seem like a repeated field. 523*1c12ee1eSDan Willemsen// For example, a []T outside the context of a protobuf message will not be 524*1c12ee1eSDan Willemsen// handled by this option. To sort Go slices that are not repeated fields, 525*1c12ee1eSDan Willemsen// consider using "github.com/google/go-cmp/cmp/cmpopts".SortSlices instead. 526*1c12ee1eSDan Willemsen// 527*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 528*1c12ee1eSDan Willemsenfunc SortRepeated(lessFunc interface{}) cmp.Option { 529*1c12ee1eSDan Willemsen t, ok := checkTTBFunc(lessFunc) 530*1c12ee1eSDan Willemsen if !ok { 531*1c12ee1eSDan Willemsen panic(fmt.Sprintf("invalid less function: %T", lessFunc)) 532*1c12ee1eSDan Willemsen } 533*1c12ee1eSDan Willemsen 534*1c12ee1eSDan Willemsen var opt cmp.Option 535*1c12ee1eSDan Willemsen var sliceType reflect.Type 536*1c12ee1eSDan Willemsen switch vf := reflect.ValueOf(lessFunc); { 537*1c12ee1eSDan Willemsen case t.Implements(enumV2Type): 538*1c12ee1eSDan Willemsen et := reflect.Zero(t).Interface().(protoreflect.Enum).Type() 539*1c12ee1eSDan Willemsen lessFunc = func(x, y Enum) bool { 540*1c12ee1eSDan Willemsen vx := reflect.ValueOf(et.New(x.Number())) 541*1c12ee1eSDan Willemsen vy := reflect.ValueOf(et.New(y.Number())) 542*1c12ee1eSDan Willemsen return vf.Call([]reflect.Value{vx, vy})[0].Bool() 543*1c12ee1eSDan Willemsen } 544*1c12ee1eSDan Willemsen opt = FilterDescriptor(et.Descriptor(), cmpopts.SortSlices(lessFunc)) 545*1c12ee1eSDan Willemsen sliceType = reflect.SliceOf(enumReflectType) 546*1c12ee1eSDan Willemsen case t.Implements(messageV2Type): 547*1c12ee1eSDan Willemsen mt := reflect.Zero(t).Interface().(protoreflect.ProtoMessage).ProtoReflect().Type() 548*1c12ee1eSDan Willemsen lessFunc = func(x, y Message) bool { 549*1c12ee1eSDan Willemsen mx := mt.New().Interface() 550*1c12ee1eSDan Willemsen my := mt.New().Interface() 551*1c12ee1eSDan Willemsen proto.Merge(mx, x) 552*1c12ee1eSDan Willemsen proto.Merge(my, y) 553*1c12ee1eSDan Willemsen vx := reflect.ValueOf(mx) 554*1c12ee1eSDan Willemsen vy := reflect.ValueOf(my) 555*1c12ee1eSDan Willemsen return vf.Call([]reflect.Value{vx, vy})[0].Bool() 556*1c12ee1eSDan Willemsen } 557*1c12ee1eSDan Willemsen opt = FilterDescriptor(mt.Descriptor(), cmpopts.SortSlices(lessFunc)) 558*1c12ee1eSDan Willemsen sliceType = reflect.SliceOf(messageReflectType) 559*1c12ee1eSDan Willemsen default: 560*1c12ee1eSDan Willemsen switch t { 561*1c12ee1eSDan Willemsen case reflect.TypeOf(bool(false)): 562*1c12ee1eSDan Willemsen case reflect.TypeOf(int32(0)): 563*1c12ee1eSDan Willemsen case reflect.TypeOf(int64(0)): 564*1c12ee1eSDan Willemsen case reflect.TypeOf(uint32(0)): 565*1c12ee1eSDan Willemsen case reflect.TypeOf(uint64(0)): 566*1c12ee1eSDan Willemsen case reflect.TypeOf(float32(0)): 567*1c12ee1eSDan Willemsen case reflect.TypeOf(float64(0)): 568*1c12ee1eSDan Willemsen case reflect.TypeOf(string("")): 569*1c12ee1eSDan Willemsen case reflect.TypeOf([]byte(nil)): 570*1c12ee1eSDan Willemsen default: 571*1c12ee1eSDan Willemsen panic(fmt.Sprintf("invalid element type: %v", t)) 572*1c12ee1eSDan Willemsen } 573*1c12ee1eSDan Willemsen opt = cmpopts.SortSlices(lessFunc) 574*1c12ee1eSDan Willemsen sliceType = reflect.SliceOf(t) 575*1c12ee1eSDan Willemsen } 576*1c12ee1eSDan Willemsen 577*1c12ee1eSDan Willemsen return cmp.FilterPath(func(p cmp.Path) bool { 578*1c12ee1eSDan Willemsen // Filter to only apply to repeated fields within a message. 579*1c12ee1eSDan Willemsen if t := p.Index(-1).Type(); t == nil || t != sliceType { 580*1c12ee1eSDan Willemsen return false 581*1c12ee1eSDan Willemsen } 582*1c12ee1eSDan Willemsen if t := p.Index(-2).Type(); t == nil || t.Kind() != reflect.Interface { 583*1c12ee1eSDan Willemsen return false 584*1c12ee1eSDan Willemsen } 585*1c12ee1eSDan Willemsen if t := p.Index(-3).Type(); t == nil || t != messageReflectType { 586*1c12ee1eSDan Willemsen return false 587*1c12ee1eSDan Willemsen } 588*1c12ee1eSDan Willemsen return true 589*1c12ee1eSDan Willemsen }, opt) 590*1c12ee1eSDan Willemsen} 591*1c12ee1eSDan Willemsen 592*1c12ee1eSDan Willemsenfunc checkTTBFunc(lessFunc interface{}) (reflect.Type, bool) { 593*1c12ee1eSDan Willemsen switch t := reflect.TypeOf(lessFunc); { 594*1c12ee1eSDan Willemsen case t == nil: 595*1c12ee1eSDan Willemsen return nil, false 596*1c12ee1eSDan Willemsen case t.NumIn() != 2 || t.In(0) != t.In(1) || t.IsVariadic(): 597*1c12ee1eSDan Willemsen return nil, false 598*1c12ee1eSDan Willemsen case t.NumOut() != 1 || t.Out(0) != reflect.TypeOf(false): 599*1c12ee1eSDan Willemsen return nil, false 600*1c12ee1eSDan Willemsen default: 601*1c12ee1eSDan Willemsen return t.In(0), true 602*1c12ee1eSDan Willemsen } 603*1c12ee1eSDan Willemsen} 604*1c12ee1eSDan Willemsen 605*1c12ee1eSDan Willemsen// SortRepeatedFields sorts the specified repeated fields. 606*1c12ee1eSDan Willemsen// Sorting a repeated field is useful for treating the list as a multiset 607*1c12ee1eSDan Willemsen// (i.e., a set where each value can appear multiple times). 608*1c12ee1eSDan Willemsen// It panics if the field does not exist or is not a repeated field. 609*1c12ee1eSDan Willemsen// 610*1c12ee1eSDan Willemsen// The sort ordering is as follows: 611*1c12ee1eSDan Willemsen// - Booleans are sorted where false is sorted before true. 612*1c12ee1eSDan Willemsen// - Integers are sorted in ascending order. 613*1c12ee1eSDan Willemsen// - Floating-point numbers are sorted in ascending order according to 614*1c12ee1eSDan Willemsen// the total ordering defined by IEEE-754 (section 5.10). 615*1c12ee1eSDan Willemsen// - Strings and bytes are sorted lexicographically in ascending order. 616*1c12ee1eSDan Willemsen// - Enums are sorted in ascending order based on its numeric value. 617*1c12ee1eSDan Willemsen// - Messages are sorted according to some arbitrary ordering 618*1c12ee1eSDan Willemsen// which is undefined and may change in future implementations. 619*1c12ee1eSDan Willemsen// 620*1c12ee1eSDan Willemsen// The ordering chosen for repeated messages is unlikely to be aesthetically 621*1c12ee1eSDan Willemsen// preferred by humans. Consider using a custom sort function: 622*1c12ee1eSDan Willemsen// 623*1c12ee1eSDan Willemsen// FilterField(m, "foo_field", SortRepeated(func(x, y *foopb.MyMessage) bool { 624*1c12ee1eSDan Willemsen// ... // user-provided definition for less 625*1c12ee1eSDan Willemsen// })) 626*1c12ee1eSDan Willemsen// 627*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform. 628*1c12ee1eSDan Willemsenfunc SortRepeatedFields(message proto.Message, names ...protoreflect.Name) cmp.Option { 629*1c12ee1eSDan Willemsen var opts cmp.Options 630*1c12ee1eSDan Willemsen md := message.ProtoReflect().Descriptor() 631*1c12ee1eSDan Willemsen for _, name := range names { 632*1c12ee1eSDan Willemsen fd := mustFindFieldDescriptor(md, name) 633*1c12ee1eSDan Willemsen if !fd.IsList() { 634*1c12ee1eSDan Willemsen panic(fmt.Sprintf("message field %q is not repeated", fd.FullName())) 635*1c12ee1eSDan Willemsen } 636*1c12ee1eSDan Willemsen 637*1c12ee1eSDan Willemsen var lessFunc interface{} 638*1c12ee1eSDan Willemsen switch fd.Kind() { 639*1c12ee1eSDan Willemsen case protoreflect.BoolKind: 640*1c12ee1eSDan Willemsen lessFunc = func(x, y bool) bool { return !x && y } 641*1c12ee1eSDan Willemsen case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: 642*1c12ee1eSDan Willemsen lessFunc = func(x, y int32) bool { return x < y } 643*1c12ee1eSDan Willemsen case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: 644*1c12ee1eSDan Willemsen lessFunc = func(x, y int64) bool { return x < y } 645*1c12ee1eSDan Willemsen case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: 646*1c12ee1eSDan Willemsen lessFunc = func(x, y uint32) bool { return x < y } 647*1c12ee1eSDan Willemsen case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: 648*1c12ee1eSDan Willemsen lessFunc = func(x, y uint64) bool { return x < y } 649*1c12ee1eSDan Willemsen case protoreflect.FloatKind: 650*1c12ee1eSDan Willemsen lessFunc = lessF32 651*1c12ee1eSDan Willemsen case protoreflect.DoubleKind: 652*1c12ee1eSDan Willemsen lessFunc = lessF64 653*1c12ee1eSDan Willemsen case protoreflect.StringKind: 654*1c12ee1eSDan Willemsen lessFunc = func(x, y string) bool { return x < y } 655*1c12ee1eSDan Willemsen case protoreflect.BytesKind: 656*1c12ee1eSDan Willemsen lessFunc = func(x, y []byte) bool { return bytes.Compare(x, y) < 0 } 657*1c12ee1eSDan Willemsen case protoreflect.EnumKind: 658*1c12ee1eSDan Willemsen lessFunc = func(x, y Enum) bool { return x.Number() < y.Number() } 659*1c12ee1eSDan Willemsen case protoreflect.MessageKind, protoreflect.GroupKind: 660*1c12ee1eSDan Willemsen lessFunc = func(x, y Message) bool { return x.String() < y.String() } 661*1c12ee1eSDan Willemsen default: 662*1c12ee1eSDan Willemsen panic(fmt.Sprintf("invalid kind: %v", fd.Kind())) 663*1c12ee1eSDan Willemsen } 664*1c12ee1eSDan Willemsen opts = append(opts, FilterDescriptor(fd, cmpopts.SortSlices(lessFunc))) 665*1c12ee1eSDan Willemsen } 666*1c12ee1eSDan Willemsen return opts 667*1c12ee1eSDan Willemsen} 668*1c12ee1eSDan Willemsen 669*1c12ee1eSDan Willemsenfunc lessF32(x, y float32) bool { 670*1c12ee1eSDan Willemsen // Bit-wise implementation of IEEE-754, section 5.10. 671*1c12ee1eSDan Willemsen xi := int32(math.Float32bits(x)) 672*1c12ee1eSDan Willemsen yi := int32(math.Float32bits(y)) 673*1c12ee1eSDan Willemsen xi ^= int32(uint32(xi>>31) >> 1) 674*1c12ee1eSDan Willemsen yi ^= int32(uint32(yi>>31) >> 1) 675*1c12ee1eSDan Willemsen return xi < yi 676*1c12ee1eSDan Willemsen} 677*1c12ee1eSDan Willemsenfunc lessF64(x, y float64) bool { 678*1c12ee1eSDan Willemsen // Bit-wise implementation of IEEE-754, section 5.10. 679*1c12ee1eSDan Willemsen xi := int64(math.Float64bits(x)) 680*1c12ee1eSDan Willemsen yi := int64(math.Float64bits(y)) 681*1c12ee1eSDan Willemsen xi ^= int64(uint64(xi>>63) >> 1) 682*1c12ee1eSDan Willemsen yi ^= int64(uint64(yi>>63) >> 1) 683*1c12ee1eSDan Willemsen return xi < yi 684*1c12ee1eSDan Willemsen} 685