aboutsummaryrefslogtreecommitdiff
path: root/cmp
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2017-08-02 16:32:40 -0700
committerGitHub <noreply@github.com>2017-08-02 16:32:40 -0700
commit3fe02156777c9eff14a88826cf3aa495b5db3544 (patch)
tree5ce399040f003cb90006678f98f9201d211ec56e /cmp
parent48a041be5648cc13e0c53082193ed105a0aa99e6 (diff)
downloadgo-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.go15
-rw-r--r--cmp/compare.go34
-rw-r--r--cmp/internal/function/func.go49
-rw-r--r--cmp/options.go8
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}}