blob: 8e58d8a0fb78d88f1bcfb05a4219922a63eb27b0 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001package matchers
2
3import (
4 "fmt"
5 "reflect"
6
7 "github.com/onsi/gomega/internal/oraclematcher"
8 "github.com/onsi/gomega/types"
9)
10
11type WithTransformMatcher struct {
12 // input
13 Transform interface{} // must be a function of one parameter that returns one value
14 Matcher types.GomegaMatcher
15
16 // cached value
17 transformArgType reflect.Type
18
19 // state
20 transformedValue interface{}
21}
22
23func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher) *WithTransformMatcher {
24 if transform == nil {
25 panic("transform function cannot be nil")
26 }
27 txType := reflect.TypeOf(transform)
28 if txType.NumIn() != 1 {
29 panic("transform function must have 1 argument")
30 }
31 if txType.NumOut() != 1 {
32 panic("transform function must have 1 return value")
33 }
34
35 return &WithTransformMatcher{
36 Transform: transform,
37 Matcher: matcher,
38 transformArgType: reflect.TypeOf(transform).In(0),
39 }
40}
41
42func (m *WithTransformMatcher) Match(actual interface{}) (bool, error) {
43 // return error if actual's type is incompatible with Transform function's argument type
44 actualType := reflect.TypeOf(actual)
45 if !actualType.AssignableTo(m.transformArgType) {
46 return false, fmt.Errorf("Transform function expects '%s' but we have '%s'", m.transformArgType, actualType)
47 }
48
49 // call the Transform function with `actual`
50 fn := reflect.ValueOf(m.Transform)
51 result := fn.Call([]reflect.Value{reflect.ValueOf(actual)})
52 m.transformedValue = result[0].Interface() // expect exactly one value
53
54 return m.Matcher.Match(m.transformedValue)
55}
56
57func (m *WithTransformMatcher) FailureMessage(_ interface{}) (message string) {
58 return m.Matcher.FailureMessage(m.transformedValue)
59}
60
61func (m *WithTransformMatcher) NegatedFailureMessage(_ interface{}) (message string) {
62 return m.Matcher.NegatedFailureMessage(m.transformedValue)
63}
64
65func (m *WithTransformMatcher) MatchMayChangeInTheFuture(_ interface{}) bool {
66 // TODO: Maybe this should always just return true? (Only an issue for non-deterministic transformers.)
67 //
68 // Querying the next matcher is fine if the transformer always will return the same value.
69 // But if the transformer is non-deterministic and returns a different value each time, then there
70 // is no point in querying the next matcher, since it can only comment on the last transformed value.
71 return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue)
72}