213 lines
6.1 KiB
Go
213 lines
6.1 KiB
Go
package spec
|
|
|
|
import (
|
|
"git.kmsign.ru/royalcat/repogen/internal/code"
|
|
)
|
|
|
|
// QuerySpec is a set of conditions of querying the database
|
|
type QuerySpec struct {
|
|
Operator Operator
|
|
Predicates []Predicate
|
|
}
|
|
|
|
// NumberOfArguments returns number of arguments required to perform the query
|
|
func (q QuerySpec) NumberOfArguments() int {
|
|
var totalArgs int
|
|
for _, predicate := range q.Predicates {
|
|
totalArgs += predicate.Comparator.NumberOfArguments()
|
|
}
|
|
return totalArgs
|
|
}
|
|
|
|
// Operator is a boolean operator for merging conditions
|
|
type Operator string
|
|
|
|
// boolean operator types
|
|
const (
|
|
OperatorAnd Operator = "AND"
|
|
OperatorOr Operator = "OR"
|
|
)
|
|
|
|
// Comparator is a comparison operator of the condition to query the data
|
|
type Comparator string
|
|
|
|
// comparator types
|
|
const (
|
|
ComparatorNot Comparator = "NOT"
|
|
ComparatorEqual Comparator = "EQUAL"
|
|
ComparatorLessThan Comparator = "LESS_THAN"
|
|
ComparatorLessThanEqual Comparator = "LESS_THAN_EQUAL"
|
|
ComparatorGreaterThan Comparator = "GREATER_THAN"
|
|
ComparatorGreaterThanEqual Comparator = "GREATER_THAN_EQUAL"
|
|
ComparatorBetween Comparator = "BETWEEN"
|
|
ComparatorIn Comparator = "IN"
|
|
ComparatorNotIn Comparator = "NOT_IN"
|
|
ComparatorTrue Comparator = "EQUAL_TRUE"
|
|
ComparatorFalse Comparator = "EQUAL_FALSE"
|
|
ComparatorExists Comparator = "EXISTS"
|
|
ComparatorNotExists Comparator = "NOT_EXISTS"
|
|
)
|
|
|
|
// ArgumentTypeFromFieldType returns a type of required argument from the given
|
|
// struct field type.
|
|
func (c Comparator) ArgumentTypeFromFieldType(t code.Type) code.Type {
|
|
switch c {
|
|
case ComparatorIn, ComparatorNotIn:
|
|
return code.ArrayType{ContainedType: t}
|
|
default:
|
|
return t
|
|
}
|
|
}
|
|
|
|
// NumberOfArguments returns the number of arguments required to perform the
|
|
// comparison.
|
|
func (c Comparator) NumberOfArguments() int {
|
|
switch c {
|
|
case ComparatorBetween:
|
|
return 2
|
|
case ComparatorTrue, ComparatorFalse, ComparatorExists, ComparatorNotExists:
|
|
return 0
|
|
default:
|
|
return 1
|
|
}
|
|
}
|
|
|
|
// Predicate is a criteria for querying a field
|
|
type Predicate struct {
|
|
FieldReference FieldReference
|
|
Comparator Comparator
|
|
ParamIndex int
|
|
}
|
|
|
|
type queryParser struct {
|
|
fieldResolver fieldResolver
|
|
StructModel code.Struct
|
|
}
|
|
|
|
func (p queryParser) parseQuery(rawTokens []string, paramIndex int) (QuerySpec,
|
|
error) {
|
|
|
|
if len(rawTokens) == 0 {
|
|
return QuerySpec{}, ErrQueryRequired
|
|
}
|
|
|
|
tokens := rawTokens
|
|
if len(tokens) == 1 && tokens[0] == "All" {
|
|
return QuerySpec{}, nil
|
|
}
|
|
|
|
if tokens[0] == "By" {
|
|
tokens = tokens[1:]
|
|
}
|
|
|
|
if len(tokens) == 0 {
|
|
return QuerySpec{}, NewInvalidQueryError(rawTokens)
|
|
}
|
|
|
|
operator, predicateTokens, err := p.splitPredicateTokens(tokens)
|
|
if err != nil {
|
|
return QuerySpec{}, err
|
|
}
|
|
|
|
querySpec := QuerySpec{
|
|
Operator: operator,
|
|
}
|
|
|
|
for _, predicateToken := range predicateTokens {
|
|
predicate, err := p.parsePredicate(predicateToken, paramIndex)
|
|
if err != nil {
|
|
return QuerySpec{}, err
|
|
}
|
|
querySpec.Predicates = append(querySpec.Predicates, predicate)
|
|
paramIndex += predicate.Comparator.NumberOfArguments()
|
|
}
|
|
|
|
return querySpec, nil
|
|
}
|
|
|
|
func (p queryParser) splitPredicateTokens(tokens []string) (Operator, [][]string, error) {
|
|
var operator Operator
|
|
var predicateTokens [][]string
|
|
var aggregatedToken []string
|
|
|
|
for _, token := range tokens {
|
|
if token != "And" && token != "Or" {
|
|
aggregatedToken = append(aggregatedToken, token)
|
|
} else if len(aggregatedToken) == 0 {
|
|
return "", nil, NewInvalidQueryError(tokens)
|
|
} else if token == "And" && operator != OperatorOr {
|
|
operator = OperatorAnd
|
|
predicateTokens = append(predicateTokens, aggregatedToken)
|
|
aggregatedToken = nil
|
|
} else if token == "Or" && operator != OperatorAnd {
|
|
operator = OperatorOr
|
|
predicateTokens = append(predicateTokens, aggregatedToken)
|
|
aggregatedToken = nil
|
|
} else {
|
|
return "", nil, NewInvalidQueryError(tokens)
|
|
}
|
|
}
|
|
if len(aggregatedToken) == 0 {
|
|
return "", nil, NewInvalidQueryError(tokens)
|
|
}
|
|
predicateTokens = append(predicateTokens, aggregatedToken)
|
|
|
|
return operator, predicateTokens, nil
|
|
}
|
|
|
|
func (p queryParser) parsePredicate(t []string, paramIndex int) (Predicate,
|
|
error) {
|
|
|
|
if len(t) > 1 && t[len(t)-1] == "Not" {
|
|
return p.createPredicate(t[:len(t)-1], ComparatorNot, paramIndex)
|
|
}
|
|
if len(t) > 2 && t[len(t)-2] == "Less" && t[len(t)-1] == "Than" {
|
|
return p.createPredicate(t[:len(t)-2], ComparatorLessThan, paramIndex)
|
|
}
|
|
if len(t) > 3 && t[len(t)-3] == "Less" && t[len(t)-2] == "Than" && t[len(t)-1] == "Equal" {
|
|
return p.createPredicate(t[:len(t)-3], ComparatorLessThanEqual, paramIndex)
|
|
}
|
|
if len(t) > 2 && t[len(t)-2] == "Greater" && t[len(t)-1] == "Than" {
|
|
return p.createPredicate(t[:len(t)-2], ComparatorGreaterThan, paramIndex)
|
|
}
|
|
if len(t) > 3 && t[len(t)-3] == "Greater" && t[len(t)-2] == "Than" && t[len(t)-1] == "Equal" {
|
|
return p.createPredicate(t[:len(t)-3], ComparatorGreaterThanEqual, paramIndex)
|
|
}
|
|
if len(t) > 2 && t[len(t)-2] == "Not" && t[len(t)-1] == "In" {
|
|
return p.createPredicate(t[:len(t)-2], ComparatorNotIn, paramIndex)
|
|
}
|
|
if len(t) > 2 && t[len(t)-2] == "Not" && t[len(t)-1] == "Exists" {
|
|
return p.createPredicate(t[:len(t)-2], ComparatorNotExists, paramIndex)
|
|
}
|
|
if len(t) > 1 && t[len(t)-1] == "In" {
|
|
return p.createPredicate(t[:len(t)-1], ComparatorIn, paramIndex)
|
|
}
|
|
if len(t) > 1 && t[len(t)-1] == "Between" {
|
|
return p.createPredicate(t[:len(t)-1], ComparatorBetween, paramIndex)
|
|
}
|
|
if len(t) > 1 && t[len(t)-1] == "True" {
|
|
return p.createPredicate(t[:len(t)-1], ComparatorTrue, paramIndex)
|
|
}
|
|
if len(t) > 1 && t[len(t)-1] == "False" {
|
|
return p.createPredicate(t[:len(t)-1], ComparatorFalse, paramIndex)
|
|
}
|
|
if len(t) > 1 && t[len(t)-1] == "Exists" {
|
|
return p.createPredicate(t[:len(t)-1], ComparatorExists, paramIndex)
|
|
}
|
|
return p.createPredicate(t, ComparatorEqual, paramIndex)
|
|
}
|
|
|
|
func (p queryParser) createPredicate(t []string, comparator Comparator,
|
|
paramIndex int) (Predicate, error) {
|
|
|
|
fields, ok := p.fieldResolver.ResolveStructField(p.StructModel, t)
|
|
if !ok {
|
|
return Predicate{}, NewStructFieldNotFoundError(t)
|
|
}
|
|
|
|
return Predicate{
|
|
FieldReference: fields,
|
|
Comparator: comparator,
|
|
ParamIndex: paramIndex,
|
|
}, nil
|
|
}
|