with_transform.go 2.3 KB

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