|
|
package matchers
import ( "fmt" "reflect"
"github.com/onsi/gomega/format" )
type SatisfyMatcher struct { Predicate interface{}
// cached type
predicateArgType reflect.Type }
func NewSatisfyMatcher(predicate interface{}) *SatisfyMatcher { if predicate == nil { panic("predicate cannot be nil") } predicateType := reflect.TypeOf(predicate) if predicateType.Kind() != reflect.Func { panic("predicate must be a function") } if predicateType.NumIn() != 1 { panic("predicate must have 1 argument") } if predicateType.NumOut() != 1 || predicateType.Out(0).Kind() != reflect.Bool { panic("predicate must return bool") }
return &SatisfyMatcher{ Predicate: predicate, predicateArgType: predicateType.In(0), } }
func (m *SatisfyMatcher) Match(actual interface{}) (success bool, err error) { // prepare a parameter to pass to the predicate
var param reflect.Value if actual != nil && reflect.TypeOf(actual).AssignableTo(m.predicateArgType) { // The dynamic type of actual is compatible with the predicate argument.
param = reflect.ValueOf(actual)
} else if actual == nil && m.predicateArgType.Kind() == reflect.Interface { // The dynamic type of actual is unknown, so there's no way to make its
// reflect.Value. Create a nil of the predicate argument, which is known.
param = reflect.Zero(m.predicateArgType)
} else { return false, fmt.Errorf("predicate expects '%s' but we have '%T'", m.predicateArgType, actual) }
// call the predicate with `actual`
fn := reflect.ValueOf(m.Predicate) result := fn.Call([]reflect.Value{param}) return result[0].Bool(), nil }
func (m *SatisfyMatcher) FailureMessage(actual interface{}) (message string) { return format.Message(actual, "to satisfy predicate", m.Predicate) }
func (m *SatisfyMatcher) NegatedFailureMessage(actual interface{}) (message string) { return format.Message(actual, "to not satisfy predicate", m.Predicate) }
|