diff options
author | Joe Tsai <joetsai@digital-static.net> | 2017-08-02 16:32:40 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-02 16:32:40 -0700 |
commit | 3fe02156777c9eff14a88826cf3aa495b5db3544 (patch) | |
tree | 5ce399040f003cb90006678f98f9201d211ec56e /cmp | |
parent | 48a041be5648cc13e0c53082193ed105a0aa99e6 (diff) | |
download | go-cmp-3fe02156777c9eff14a88826cf3aa495b5db3544.tar.gz |
Add cmp/internal/function package (#35)
Unify the common logic for identifying the type of function.
This logic is helpful since Go lacks generics, so it is hard
to query whether a function is of the signature: func(T, T) bool
Diffstat (limited to 'cmp')
-rw-r--r-- | cmp/cmpopts/sort.go | 15 | ||||
-rw-r--r-- | cmp/compare.go | 34 | ||||
-rw-r--r-- | cmp/internal/function/func.go | 49 | ||||
-rw-r--r-- | cmp/options.go | 8 |
4 files changed, 59 insertions, 47 deletions
diff --git a/cmp/cmpopts/sort.go b/cmp/cmpopts/sort.go index e155467..a566d24 100644 --- a/cmp/cmpopts/sort.go +++ b/cmp/cmpopts/sort.go @@ -9,6 +9,7 @@ import ( "reflect" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/internal/function" ) // SortSlices returns a Transformer option that sorts all []V. @@ -26,7 +27,7 @@ import ( // SortSlices can be used in conjuction with EquateEmpty. func SortSlices(less interface{}) cmp.Option { vf := reflect.ValueOf(less) - if !isTTBoolFunc(vf.Type()) || vf.IsNil() { + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { panic(fmt.Sprintf("invalid less function: %T", less)) } ss := sliceSorter{vf.Type().In(0), vf} @@ -97,7 +98,7 @@ func (ss sliceSorter) less(v reflect.Value, i, j int) bool { // SortMaps can be used in conjuction with EquateEmpty. func SortMaps(less interface{}) cmp.Option { vf := reflect.ValueOf(less) - if !isTTBoolFunc(vf.Type()) || vf.IsNil() { + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { panic(fmt.Sprintf("invalid less function: %T", less)) } ms := mapSorter{vf.Type().In(0), vf} @@ -143,13 +144,3 @@ func (ms mapSorter) less(v reflect.Value, i, j int) bool { } return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() } - -var boolType = reflect.TypeOf(true) - -// isTTBoolFunc reports whether f is of the form: func(T, T) bool. -func isTTBoolFunc(t reflect.Type) bool { - if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { - return false - } - return t.NumIn() == 2 && t.NumOut() == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType -} diff --git a/cmp/compare.go b/cmp/compare.go index 77b9269..177fc10 100644 --- a/cmp/compare.go +++ b/cmp/compare.go @@ -31,6 +31,7 @@ import ( "reflect" "github.com/google/go-cmp/cmp/internal/diff" + "github.com/google/go-cmp/cmp/internal/function" "github.com/google/go-cmp/cmp/internal/value" ) @@ -372,8 +373,7 @@ func (s *state) applyOption(vx, vy reflect.Value, t reflect.Type, opt option) { func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool { // Check if this type even has an Equal method. m, ok := t.MethodByName("Equal") - ft := functionType(m.Type) - if !ok || (ft != equalFunc && ft != equalIfaceFunc) { + if !ok || !function.IsType(m.Type, function.EqualAssignable) { return false } @@ -588,33 +588,3 @@ func makeAddressable(v reflect.Value) reflect.Value { vc.Set(v) return vc } - -type funcType int - -const ( - invalidFunc funcType = iota - equalFunc // func(T, T) bool - equalIfaceFunc // func(T, I) bool - transformFunc // func(T) R - valueFilterFunc = equalFunc // func(T, T) bool -) - -var boolType = reflect.TypeOf(true) - -// functionType identifies which type of function signature this is. -func functionType(t reflect.Type) funcType { - if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { - return invalidFunc - } - ni, no := t.NumIn(), t.NumOut() - switch { - case ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType: - return equalFunc // or valueFilterFunc - case ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType: - return equalIfaceFunc - case ni == 1 && no == 1: - return transformFunc - default: - return invalidFunc - } -} diff --git a/cmp/internal/function/func.go b/cmp/internal/function/func.go new file mode 100644 index 0000000..4c35ff1 --- /dev/null +++ b/cmp/internal/function/func.go @@ -0,0 +1,49 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package function identifies function types. +package function + +import "reflect" + +type funcType int + +const ( + _ funcType = iota + + ttbFunc // func(T, T) bool + tibFunc // func(T, I) bool + trFunc // func(T) R + + Equal = ttbFunc // func(T, T) bool + EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool + Transformer = trFunc // func(T) R + ValueFilter = ttbFunc // func(T, T) bool + Less = ttbFunc // func(T, T) bool +) + +var boolType = reflect.TypeOf(true) + +// IsType reports whether the reflect.Type is of the specified function type. +func IsType(t reflect.Type, ft funcType) bool { + if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { + return false + } + ni, no := t.NumIn(), t.NumOut() + switch ft { + case ttbFunc: // func(T, T) bool + if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { + return true + } + case tibFunc: // func(T, I) bool + if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { + return true + } + case trFunc: // func(T) R + if ni == 1 && no == 1 { + return true + } + } + return false +} diff --git a/cmp/options.go b/cmp/options.go index 7b65baa..0498a55 100644 --- a/cmp/options.go +++ b/cmp/options.go @@ -9,6 +9,8 @@ import ( "reflect" "runtime" "strings" + + "github.com/google/go-cmp/cmp/internal/function" ) // Option configures for specific behavior of Equal and Diff. In particular, @@ -150,7 +152,7 @@ func FilterPath(f func(Path) bool, opt Option) Option { // a previously filtered Option. func FilterValues(f interface{}, opt Option) Option { v := reflect.ValueOf(f) - if functionType(v.Type()) != valueFilterFunc || v.IsNil() { + if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { panic(fmt.Sprintf("invalid values filter function: %T", f)) } switch opt := opt.(type) { @@ -191,7 +193,7 @@ func Ignore() Option { // transformation PathStep. If empty, an arbitrary name is used. func Transformer(name string, f interface{}) Option { v := reflect.ValueOf(f) - if functionType(v.Type()) != transformFunc || v.IsNil() { + if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { panic(fmt.Sprintf("invalid transformer function: %T", f)) } if name == "" { @@ -226,7 +228,7 @@ type transformer struct { // • Pure: equal(x, y) does not modify x or y func Comparer(f interface{}) Option { v := reflect.ValueOf(f) - if functionType(v.Type()) != equalFunc || v.IsNil() { + if !function.IsType(v.Type(), function.Equal) || v.IsNil() { panic(fmt.Sprintf("invalid comparer function: %T", f)) } opt := option{op: &comparer{v}} |