forked from xmidt-org/talaria
-
Notifications
You must be signed in to change notification settings - Fork 0
/
binOp.go
162 lines (130 loc) · 3.36 KB
/
binOp.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// SPDX-FileCopyrightText: 2017 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0
package main
import (
"errors"
"reflect"
"github.com/spf13/cast"
)
// Errors
var (
errIterableTypeOnly = errors.New("Only slices and arrays are currently supported as iterable")
errNumericalTypeOnly = errors.New("Only numerical values are supported")
errOpNotSupported = errors.New("Operation not supported")
)
// Supported operations
const (
IntersectsOp = "intersects"
ContainsOp = "contains"
EqualsOp = "eq"
GreaterThanOp = "gt"
)
// binOp encapsulates the execution of a generic binary operator.
type binOp interface {
// evaluate applies the operation from left to right
evaluate(left, right interface{}) (bool, error)
// name is the name of the operation.
name() string
}
func newBinOp(operation string) (binOp, error) {
switch operation {
case IntersectsOp:
return new(intersects), nil
case ContainsOp:
return new(contains), nil
case EqualsOp:
return new(equals), nil
case GreaterThanOp:
return new(greaterThan), nil
default:
return nil, errOpNotSupported
}
}
// intersects returns true if left and right contain
// some shared member. False otherwise.
// Note:
// - only elements which can be casted to slices are currently supported.
type intersects struct{}
func (i intersects) evaluate(left, right interface{}) (bool, error) {
if left == nil || right == nil {
return false, nil
}
a, ok := iterable(left)
if !ok {
return false, errIterableTypeOnly
}
b, ok := iterable(right)
if !ok {
return false, errIterableTypeOnly
}
m := make(map[interface{}]bool)
for _, e := range a {
m[e] = true
}
for _, e := range b {
if m[e] {
return true, nil
}
}
return false, nil
}
func (i intersects) name() string {
return IntersectsOp
}
// contains returns true if right is a member of left.
// Note: only slices are supported.
type contains struct{}
func (c contains) evaluate(left interface{}, right interface{}) (bool, error) {
if left == nil {
return false, nil
}
l, ok := iterable(left)
if !ok {
return false, errIterableTypeOnly
}
for _, e := range l {
if reflect.DeepEqual(e, right) {
return true, nil
}
}
return false, nil
}
func (c contains) name() string {
return ContainsOp
}
// equals returns true if left and right are equal as defined by reflect.DeepEqual()
type equals struct{}
func (e equals) evaluate(left interface{}, right interface{}) (bool, error) {
return reflect.DeepEqual(left, right), nil
}
func (e equals) name() string {
return EqualsOp
}
type greaterThan struct{}
func (g greaterThan) evaluate(left interface{}, right interface{}) (bool, error) {
leftNumber, leftErr := cast.ToInt64E(left)
rightNumber, rightErr := cast.ToInt64E(right)
if leftErr != nil || rightErr != nil {
return false, errNumericalTypeOnly
}
return leftNumber > rightNumber, nil
}
func (g greaterThan) name() string {
return GreaterThanOp
}
// iterable checks that the given interface is of a
// supported iterable reflect.Kind and if so,
// returns a slice of its elements
func iterable(e interface{}) ([]interface{}, bool) {
switch reflect.TypeOf(e).Kind() {
case reflect.Slice, reflect.Array:
v := reflect.ValueOf(e)
n := v.Len()
elements := make([]interface{}, n)
for i := 0; i < n; i++ {
elements[i] = v.Index(i).Interface()
}
return elements, true
}
return nil, false
}