-
Notifications
You must be signed in to change notification settings - Fork 1
/
context.go
148 lines (125 loc) · 3.55 KB
/
context.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
141
142
143
144
145
146
147
148
package litmus
import (
"errors"
"fmt"
"reflect"
"time"
"github.com/RoaringBitmap/roaring"
)
type Context struct {
options []Option
externalCheckers []CheckerFunction
externalMeta MetaInterface
internalMeta internalMeta
internalCheckers map[string]checkerFunc
}
func NewContext(opts []Option) (*Context, error) {
ctx := &Context{
options: opts,
externalCheckers: make([]CheckerFunction, 0),
externalMeta: nil,
internalMeta: internalMeta{
DateMap: make(map[string]time.Time),
RangeBitMap: make(map[string]*roaring.Bitmap),
ArrayMap: make([]map[string]map[interface{}]uint8, 0),
},
internalCheckers: nil,
}
if err := ctx.init(); err != nil {
return nil, err
}
return ctx, nil
}
// AddChecker adds external checker function to check request with selectors
func (c *Context) AddChecker(checkers ...CheckerFunction) {
c.externalCheckers = checkers
}
// AddMeta adds external meta information to use in external checker method
func (c *Context) AddMeta(meta MetaInterface) {
c.externalMeta = meta
}
func (c *Context) init() error {
if len(c.options) == 0 {
return errors.New("must send at least one Option")
}
c.initInternalChecker()
for _, option := range c.options {
arrayMap := make(map[string]map[interface{}]uint8)
selector := option.Selector
valueOf := reflect.Indirect(reflect.ValueOf(selector))
var typeOf reflect.Type
switch k := reflect.TypeOf(selector); {
case k.Kind() == reflect.Ptr:
typeOf = k.Elem()
default:
typeOf = k
}
for i := 0; i < valueOf.NumField(); i++ {
field := typeOf.Field(i)
tagMeta, f := field.Tag.Lookup("meta")
if !f {
continue
}
switch tagMeta {
case "DateMap":
raw := valueOf.Field(i).String()
v, err := ParseDate(raw)
if err != nil {
return fmt.Errorf("failed to parse Date from %v -> %v as %v", option.Key, field.Name, raw)
}
c.internalMeta.DateMap[raw] = v
case "RangeBitMap":
raw := valueOf.Field(i).String()
v, err := ToRoarBitMap(raw)
if err != nil {
return fmt.Errorf("failed to convert to BitMap from %v -> %v as %v", option.Key, field.Name, raw)
}
c.internalMeta.RangeBitMap[raw] = v
case "ArrayMap":
raw := valueOf.Field(i).Interface()
v, err := ToArrayMap(raw)
if err != nil {
return fmt.Errorf("failed to convert to ArrayMap from %v -> %v as %v", option.Key, field.Name, raw)
}
arrayMap[field.Name] = v
default:
return fmt.Errorf("invalid meta found: %v", tagMeta)
}
}
c.internalMeta.ArrayMap = append(c.internalMeta.ArrayMap, arrayMap)
}
return nil
}
// GetResolver will first check externally provided Checker functions
// then will check internal Checker method to match with selector
func (c *Context) GetResolver(request RequestInterface, logger LoggerInterface) (ResolverInterface, bool) {
for optionIndex, option := range c.options {
optionSuccess := true
for _, checker := range c.externalCheckers {
if !checker(request, option.Selector, c.externalMeta, logger) {
optionSuccess = false
break
}
}
valueOf := reflect.Indirect(reflect.ValueOf(request))
typeOf := reflect.TypeOf(request)
for i := 0; i < valueOf.NumField(); i++ {
field := typeOf.Elem().Field(i)
tagChecker, f := field.Tag.Lookup("checker")
if !f {
continue
}
value := valueOf.Field(i)
if fn, f := c.internalCheckers[tagChecker]; f {
if !fn(field.Tag.Get("selector"), value, optionIndex) {
optionSuccess = false
break
}
}
}
if optionSuccess {
return option.Resolver, true
}
}
return nil, false
}