1package exp
2
3import (
4	"reflect"
5	"regexp"
6)
7
8type boolean struct {
9	lhs Expression
10	rhs interface{}
11	op  BooleanOperation
12}
13
14func NewBooleanExpression(op BooleanOperation, lhs Expression, rhs interface{}) BooleanExpression {
15	return boolean{op: op, lhs: lhs, rhs: rhs}
16}
17
18func (b boolean) Clone() Expression {
19	return NewBooleanExpression(b.op, b.lhs.Clone(), b.rhs)
20}
21
22func (b boolean) Expression() Expression {
23	return b
24}
25
26func (b boolean) RHS() interface{} {
27	return b.rhs
28}
29
30func (b boolean) LHS() Expression {
31	return b.lhs
32}
33
34func (b boolean) Op() BooleanOperation {
35	return b.op
36}
37
38// used internally to create an equality BooleanExpression
39func eq(lhs Expression, rhs interface{}) BooleanExpression {
40	return checkBoolExpType(EqOp, lhs, rhs, false)
41}
42
43// used internally to create an in-equality BooleanExpression
44func neq(lhs Expression, rhs interface{}) BooleanExpression {
45	return checkBoolExpType(EqOp, lhs, rhs, true)
46}
47
48// used internally to create an gt comparison BooleanExpression
49func gt(lhs Expression, rhs interface{}) BooleanExpression {
50	return NewBooleanExpression(GtOp, lhs, rhs)
51}
52
53// used internally to create an gte comparison BooleanExpression
54func gte(lhs Expression, rhs interface{}) BooleanExpression {
55	return NewBooleanExpression(GteOp, lhs, rhs)
56}
57
58// used internally to create an lt comparison BooleanExpression
59func lt(lhs Expression, rhs interface{}) BooleanExpression {
60	return NewBooleanExpression(LtOp, lhs, rhs)
61}
62
63// used internally to create an lte comparison BooleanExpression
64func lte(lhs Expression, rhs interface{}) BooleanExpression {
65	return NewBooleanExpression(LteOp, lhs, rhs)
66}
67
68// used internally to create an IN BooleanExpression
69func in(lhs Expression, vals ...interface{}) BooleanExpression {
70	if len(vals) == 1 && reflect.Indirect(reflect.ValueOf(vals[0])).Kind() == reflect.Slice {
71		return NewBooleanExpression(InOp, lhs, vals[0])
72	}
73	return NewBooleanExpression(InOp, lhs, vals)
74}
75
76// used internally to create a NOT IN BooleanExpression
77func notIn(lhs Expression, vals ...interface{}) BooleanExpression {
78	if len(vals) == 1 && reflect.Indirect(reflect.ValueOf(vals[0])).Kind() == reflect.Slice {
79		return NewBooleanExpression(NotInOp, lhs, vals[0])
80	}
81	return NewBooleanExpression(NotInOp, lhs, vals)
82}
83
84// used internally to create an IS BooleanExpression
85func is(lhs Expression, val interface{}) BooleanExpression {
86	return checkBoolExpType(IsOp, lhs, val, false)
87}
88
89// used internally to create an IS NOT BooleanExpression
90func isNot(lhs Expression, val interface{}) BooleanExpression {
91	return checkBoolExpType(IsOp, lhs, val, true)
92}
93
94// used internally to create a LIKE BooleanExpression
95func like(lhs Expression, val interface{}) BooleanExpression {
96	return checkLikeExp(LikeOp, lhs, val, false)
97}
98
99// used internally to create an ILIKE BooleanExpression
100func iLike(lhs Expression, val interface{}) BooleanExpression {
101	return checkLikeExp(ILikeOp, lhs, val, false)
102}
103
104// used internally to create a NOT LIKE BooleanExpression
105func notLike(lhs Expression, val interface{}) BooleanExpression {
106	return checkLikeExp(LikeOp, lhs, val, true)
107}
108
109// used internally to create a NOT ILIKE BooleanExpression
110func notILike(lhs Expression, val interface{}) BooleanExpression {
111	return checkLikeExp(ILikeOp, lhs, val, true)
112}
113
114// used internally to create a LIKE BooleanExpression
115func regexpLike(lhs Expression, val interface{}) BooleanExpression {
116	return checkLikeExp(RegexpLikeOp, lhs, val, false)
117}
118
119// used internally to create an ILIKE BooleanExpression
120func regexpILike(lhs Expression, val interface{}) BooleanExpression {
121	return checkLikeExp(RegexpILikeOp, lhs, val, false)
122}
123
124// used internally to create a NOT LIKE BooleanExpression
125func regexpNotLike(lhs Expression, val interface{}) BooleanExpression {
126	return checkLikeExp(RegexpLikeOp, lhs, val, true)
127}
128
129// used internally to create a NOT ILIKE BooleanExpression
130func regexpNotILike(lhs Expression, val interface{}) BooleanExpression {
131	return checkLikeExp(RegexpILikeOp, lhs, val, true)
132}
133
134// checks an like rhs to create the proper like expression for strings or regexps
135func checkLikeExp(op BooleanOperation, lhs Expression, val interface{}, invert bool) BooleanExpression {
136	rhs := val
137
138	if t, ok := val.(*regexp.Regexp); ok {
139		if op == LikeOp {
140			op = RegexpLikeOp
141		} else if op == ILikeOp {
142			op = RegexpILikeOp
143		}
144		rhs = t.String()
145	}
146	if invert {
147		op = operatorInversions[op]
148	}
149	return NewBooleanExpression(op, lhs, rhs)
150}
151
152// checks a boolean operation normalizing the operation based on the RHS (e.g. "a" = true vs "a" IS TRUE
153func checkBoolExpType(op BooleanOperation, lhs Expression, rhs interface{}, invert bool) BooleanExpression {
154	if rhs == nil {
155		op = IsOp
156	} else {
157		switch reflect.Indirect(reflect.ValueOf(rhs)).Kind() {
158		case reflect.Bool:
159			op = IsOp
160		case reflect.Slice:
161			// if its a slice of bytes dont treat as an IN
162			if _, ok := rhs.([]byte); !ok {
163				op = InOp
164			}
165		case reflect.Struct:
166			switch rhs.(type) {
167			case SQLExpression:
168				op = InOp
169			case AppendableExpression:
170				op = InOp
171			case *regexp.Regexp:
172				return checkLikeExp(LikeOp, lhs, rhs, invert)
173			}
174		default:
175		}
176	}
177	if invert {
178		op = operatorInversions[op]
179	}
180	return NewBooleanExpression(op, lhs, rhs)
181}
182