1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
// Copyright (c) 2021-2022, Maxime Soulé
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
package types
import (
"reflect"
"github.com/maxatome/go-testdeep/internal/dark"
)
// NewOrder returns a function able to compare 2 non-nil values of type t.
// It returns nil if the type t is not comparable.
func NewOrder(t reflect.Type) func(a, b reflect.Value) int {
// Compare(T) int
if m, ok := cmpMethod("Compare", t, Int); ok {
return func(va, vb reflect.Value) int {
// use dark.MustGetInterface() to bypass possible private fields
ret := m.Call([]reflect.Value{
reflect.ValueOf(dark.MustGetInterface(va)),
reflect.ValueOf(dark.MustGetInterface(vb)),
})
return int(ret[0].Int())
}
}
// Less(T) bool
if m, ok := cmpMethod("Less", t, Bool); ok {
return func(va, vb reflect.Value) int {
// use dark.MustGetInterface() to bypass possible private fields
va = reflect.ValueOf(dark.MustGetInterface(va))
vb = reflect.ValueOf(dark.MustGetInterface(vb))
ret := m.Call([]reflect.Value{va, vb})
if ret[0].Bool() { // a < b
return -1
}
ret = m.Call([]reflect.Value{vb, va})
if ret[0].Bool() { // b < a
return 1
}
return 0
}
}
return nil
}
func cmpMethod(name string, in, out reflect.Type) (reflect.Value, bool) {
if equal, ok := in.MethodByName(name); ok {
ft := equal.Type
if !ft.IsVariadic() &&
ft.NumIn() == 2 &&
ft.NumOut() == 1 &&
ft.In(0) == in &&
ft.In(1) == in &&
ft.Out(0) == out {
return equal.Func, true
}
}
return reflect.Value{}, false
}
|