traefik/pkg/rules/parser.go

141 lines
2.8 KiB
Go
Raw Normal View History

package rules
import (
2022-03-17 17:02:08 +00:00
"fmt"
"strings"
"github.com/vulcand/predicate"
2022-08-09 15:36:08 +00:00
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
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
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
}
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{})
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
}
2022-03-17 17:02:08 +00:00
return predicate.NewParser(predicate.Def{
Operators: predicate.Operators{
AND: andFunc,
OR: orFunc,
NOT: notFunc,
},
Functions: parserFuncs,
})
}
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(),
}
}
}
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(),
}
}
}
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)
}
}
2022-03-17 17:02:08 +00:00
return nil
}
}
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)
}
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)
}
}
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
}