/
matcher.go
140 lines (114 loc) · 3.21 KB
/
matcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package pattern
import (
"reflect"
)
type Patterner interface {
Match(any) bool
}
func Patteners(patterns ...Patterner) []Patterner {
return patterns
}
type Handler[T any] func() T
// Matcher is a generic struct that matches a value of type V to a response of type T.
// It has three fields: value, isMatched and response.
// value is the input that needs to be matched.
// isMatched is a boolean that indicates whether a match has been found.
// response is the output that is returned when a match is found.
type Matcher[T any, V any] struct {
input V
isMatched bool
response T
}
// NewMatcher is a function that creates a new Matcher instance.
// It takes a value of any type V and returns a pointer to a Matcher instance.
// The returned Matcher instance has its value field set to the input value and isMatched field set to false by default.
func NewMatcher[T any, V any](input V) *Matcher[T, V] {
return &Matcher[T, V]{input: input}
}
// WithPattern check if pattern matches the entire input
func (m *Matcher[T, V]) WithPattern(pattern Patterner, fn Handler[T]) *Matcher[T, V] {
if !m.isMatched && pattern.Match(m.input) {
m.patternMatched(fn)
}
return m
}
// WithPatterns check each of the patterns against the each of the input
func (m *Matcher[T, V]) WithPatterns(patterns []Patterner, fn Handler[T]) *Matcher[T, V] {
if m.isMatched {
return m
}
var allMatched = true
input := reflect.ValueOf(m.input)
if input.Len() != len(patterns) {
return m
}
for i := 0; i < input.Len(); i++ {
inputVal := input.Index(i)
if !patterns[i].Match(inputVal.Interface()) {
allMatched = false
break
}
}
if allMatched {
m.patternMatched(fn)
}
return m
}
// WithValues check for deep equality between each of the value against the each of the input
func (m *Matcher[T, V]) WithValues(value any, fn Handler[T]) *Matcher[T, V] {
if m.isMatched {
return m
}
if reflect.TypeOf(value).Kind() == reflect.Array || reflect.TypeOf(value).Kind() == reflect.Slice {
patternVal := reflect.ValueOf(value)
input := reflect.ValueOf(m.input)
if input.Len() != patternVal.Len() {
return m
}
var allMatched = true
for i := 0; i < patternVal.Len(); i++ {
firstVal := patternVal.Index(i)
inputVal := input.Index(i)
// Check if firstVal is a Patterner
if patterner, ok := firstVal.Interface().(Patterner); ok {
// If it is, run patterner.Match
if !patterner.Match(inputVal.Interface()) {
allMatched = false
break
}
} else {
// If it's not a Patterner, then run reflect.DeepEqual
if !reflect.DeepEqual(firstVal.Interface(), inputVal.Interface()) {
allMatched = false
break
}
}
}
if allMatched {
m.patternMatched(fn)
}
return m
}
return m
}
// WithValue check for deep equality between the value and the input
func (m *Matcher[T, V]) WithValue(pattern V, fn Handler[T]) *Matcher[T, V] {
if m.isMatched {
return m
}
if reflect.DeepEqual(m.input, pattern) {
m.patternMatched(fn)
}
return m
}
// Otherwise is called if no patterns match
func (m *Matcher[T, V]) Otherwise(fn Handler[T]) T {
if !m.isMatched {
m.response = fn()
}
return m.response
}
func (m *Matcher[T, V]) patternMatched(fn Handler[T]) {
m.response = fn()
m.isMatched = true
}