2019-01-30 15:24:07 +00:00
|
|
|
package rules
|
|
|
|
|
|
|
|
import (
|
2022-03-17 17:02:08 +00:00
|
|
|
"fmt"
|
2019-01-30 15:24:07 +00:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/vulcand/predicate"
|
2022-08-09 15:36:08 +00:00
|
|
|
"golang.org/x/text/cases"
|
|
|
|
"golang.org/x/text/language"
|
2019-01-30 15:24:07 +00:00
|
|
|
)
|
|
|
|
|
2021-05-31 16:58:05 +00:00
|
|
|
const (
|
|
|
|
and = "and"
|
|
|
|
or = "or"
|
|
|
|
)
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
// TreeBuilder defines the type for a Tree builder.
|
|
|
|
type TreeBuilder func() *Tree
|
2019-01-30 15:24:07 +00:00
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
// Tree represents the rules' tree structure.
|
|
|
|
type Tree struct {
|
|
|
|
Matcher string
|
|
|
|
Not bool
|
|
|
|
Value []string
|
|
|
|
RuleLeft *Tree
|
|
|
|
RuleRight *Tree
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
// NewParser constructs a parser for the given matchers.
|
|
|
|
func NewParser(matchers []string) (predicate.Parser, error) {
|
|
|
|
parserFuncs := make(map[string]interface{})
|
2019-03-14 08:30:04 +00:00
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
for _, matcherName := range matchers {
|
|
|
|
matcherName := matcherName
|
|
|
|
fn := func(value ...string) TreeBuilder {
|
|
|
|
return func() *Tree {
|
|
|
|
return &Tree{
|
|
|
|
Matcher: matcherName,
|
|
|
|
Value: value,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parserFuncs[matcherName] = fn
|
|
|
|
parserFuncs[strings.ToLower(matcherName)] = fn
|
|
|
|
parserFuncs[strings.ToUpper(matcherName)] = fn
|
2022-08-09 15:36:08 +00:00
|
|
|
parserFuncs[cases.Title(language.Und).String(strings.ToLower(matcherName))] = fn
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
return predicate.NewParser(predicate.Def{
|
|
|
|
Operators: predicate.Operators{
|
|
|
|
AND: andFunc,
|
|
|
|
OR: orFunc,
|
|
|
|
NOT: notFunc,
|
|
|
|
},
|
|
|
|
Functions: parserFuncs,
|
|
|
|
})
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
func andFunc(left, right TreeBuilder) TreeBuilder {
|
|
|
|
return func() *Tree {
|
|
|
|
return &Tree{
|
|
|
|
Matcher: and,
|
|
|
|
RuleLeft: left(),
|
|
|
|
RuleRight: right(),
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
func orFunc(left, right TreeBuilder) TreeBuilder {
|
|
|
|
return func() *Tree {
|
|
|
|
return &Tree{
|
|
|
|
Matcher: or,
|
|
|
|
RuleLeft: left(),
|
|
|
|
RuleRight: right(),
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
func invert(t *Tree) *Tree {
|
|
|
|
switch t.Matcher {
|
2021-05-31 16:58:05 +00:00
|
|
|
case or:
|
2022-03-17 17:02:08 +00:00
|
|
|
t.Matcher = and
|
|
|
|
t.RuleLeft = invert(t.RuleLeft)
|
|
|
|
t.RuleRight = invert(t.RuleRight)
|
2021-05-31 16:58:05 +00:00
|
|
|
case and:
|
2022-03-17 17:02:08 +00:00
|
|
|
t.Matcher = or
|
|
|
|
t.RuleLeft = invert(t.RuleLeft)
|
|
|
|
t.RuleRight = invert(t.RuleRight)
|
2021-05-31 16:58:05 +00:00
|
|
|
default:
|
2022-03-17 17:02:08 +00:00
|
|
|
t.Not = !t.Not
|
2021-05-31 16:58:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
func notFunc(elem TreeBuilder) TreeBuilder {
|
|
|
|
return func() *Tree {
|
2021-05-31 16:58:05 +00:00
|
|
|
return invert(elem())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
// ParseMatchers returns the subset of matchers in the Tree matching the given matchers.
|
|
|
|
func (tree *Tree) ParseMatchers(matchers []string) []string {
|
|
|
|
switch tree.Matcher {
|
|
|
|
case and, or:
|
|
|
|
return append(tree.RuleLeft.ParseMatchers(matchers), tree.RuleRight.ParseMatchers(matchers)...)
|
|
|
|
default:
|
|
|
|
for _, matcher := range matchers {
|
|
|
|
if tree.Matcher == matcher {
|
|
|
|
return lower(tree.Value)
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
return nil
|
|
|
|
}
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
2019-03-14 08:30:04 +00:00
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
// CheckRule validates the given rule.
|
|
|
|
func CheckRule(rule *Tree) error {
|
|
|
|
if len(rule.Value) == 0 {
|
|
|
|
return fmt.Errorf("no args for matcher %s", rule.Matcher)
|
|
|
|
}
|
2019-03-14 08:30:04 +00:00
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
for _, v := range rule.Value {
|
|
|
|
if len(v) == 0 {
|
|
|
|
return fmt.Errorf("empty args for matcher %s, %v", rule.Matcher, rule.Value)
|
2019-03-14 08:30:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func lower(slice []string) []string {
|
|
|
|
var lowerStrings []string
|
|
|
|
for _, value := range slice {
|
|
|
|
lowerStrings = append(lowerStrings, strings.ToLower(value))
|
|
|
|
}
|
|
|
|
|
|
|
|
return lowerStrings
|
2019-03-14 08:30:04 +00:00
|
|
|
}
|