2019-01-30 16:24:07 +01:00
package rules
import (
2022-03-17 11:02:08 -06:00
"fmt"
2019-01-30 16:24:07 +01:00
"strings"
"github.com/vulcand/predicate"
2022-08-09 17:36:08 +02:00
"golang.org/x/text/cases"
"golang.org/x/text/language"
2019-01-30 16:24:07 +01:00
)
2021-05-31 18:58:05 +02:00
const (
and = "and"
or = "or"
)
2022-03-17 11:02:08 -06:00
// TreeBuilder defines the type for a Tree builder.
type TreeBuilder func ( ) * Tree
2019-01-30 16:24:07 +01:00
2022-03-17 11:02:08 -06:00
// Tree represents the rules' tree structure.
type Tree struct {
Matcher string
Not bool
Value [ ] string
RuleLeft * Tree
RuleRight * Tree
2019-01-30 16:24:07 +01:00
}
2022-03-17 11:02:08 -06:00
// NewParser constructs a parser for the given matchers.
func NewParser ( matchers [ ] string ) ( predicate . Parser , error ) {
parserFuncs := make ( map [ string ] interface { } )
2019-03-14 09:30:04 +01:00
2022-03-17 11:02:08 -06:00
for _ , matcherName := range matchers {
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 17:36:08 +02:00
parserFuncs [ cases . Title ( language . Und ) . String ( strings . ToLower ( matcherName ) ) ] = fn
2019-01-30 16:24:07 +01:00
}
2022-03-17 11:02:08 -06:00
return predicate . NewParser ( predicate . Def {
Operators : predicate . Operators {
AND : andFunc ,
OR : orFunc ,
NOT : notFunc ,
} ,
Functions : parserFuncs ,
} )
2019-01-30 16:24:07 +01:00
}
2022-03-17 11:02:08 -06:00
func andFunc ( left , right TreeBuilder ) TreeBuilder {
return func ( ) * Tree {
return & Tree {
Matcher : and ,
RuleLeft : left ( ) ,
RuleRight : right ( ) ,
2019-01-30 16:24:07 +01:00
}
}
}
2022-03-17 11:02:08 -06:00
func orFunc ( left , right TreeBuilder ) TreeBuilder {
return func ( ) * Tree {
return & Tree {
Matcher : or ,
RuleLeft : left ( ) ,
RuleRight : right ( ) ,
2019-01-30 16:24:07 +01:00
}
}
}
2022-03-17 11:02:08 -06:00
func invert ( t * Tree ) * Tree {
switch t . Matcher {
2021-05-31 18:58:05 +02:00
case or :
2022-03-17 11:02:08 -06:00
t . Matcher = and
t . RuleLeft = invert ( t . RuleLeft )
t . RuleRight = invert ( t . RuleRight )
2021-05-31 18:58:05 +02:00
case and :
2022-03-17 11:02:08 -06:00
t . Matcher = or
t . RuleLeft = invert ( t . RuleLeft )
t . RuleRight = invert ( t . RuleRight )
2021-05-31 18:58:05 +02:00
default :
2022-03-17 11:02:08 -06:00
t . Not = ! t . Not
2021-05-31 18:58:05 +02:00
}
return t
}
2022-03-17 11:02:08 -06:00
func notFunc ( elem TreeBuilder ) TreeBuilder {
return func ( ) * Tree {
2021-05-31 18:58:05 +02:00
return invert ( elem ( ) )
}
}
2022-03-17 11:02:08 -06: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 16:24:07 +01:00
}
}
2022-03-17 11:02:08 -06:00
return nil
}
2019-01-30 16:24:07 +01:00
}
2019-03-14 09:30:04 +01:00
2022-03-17 11:02:08 -06: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 09:30:04 +01:00
2022-03-17 11:02:08 -06: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 09:30:04 +01:00
}
}
2022-03-17 11:02:08 -06: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 09:30:04 +01:00
}