traefik/pkg/provider/constraints/constraints_tags.go
2024-02-19 15:44:03 +01:00

85 lines
1.6 KiB
Go

package constraints
import (
"errors"
"regexp"
"slices"
"github.com/vulcand/predicate"
)
type constraintTagFunc func([]string) bool
// MatchTags reports whether the expression matches with the given tags.
// The expression must match any logical boolean combination of:
// - `Tag(tagValue)`
// - `TagRegex(regexValue)`.
func MatchTags(tags []string, expr string) (bool, error) {
if expr == "" {
return true, nil
}
p, err := predicate.NewParser(predicate.Def{
Operators: predicate.Operators{
AND: andTagFunc,
NOT: notTagFunc,
OR: orTagFunc,
},
Functions: map[string]interface{}{
"Tag": tagFn,
"TagRegex": tagRegexFn,
},
})
if err != nil {
return false, err
}
parse, err := p.Parse(expr)
if err != nil {
return false, err
}
fn, ok := parse.(constraintTagFunc)
if !ok {
return false, errors.New("not a constraintTagFunc")
}
return fn(tags), nil
}
func tagFn(name string) constraintTagFunc {
return func(tags []string) bool {
return slices.Contains(tags, name)
}
}
func tagRegexFn(expr string) constraintTagFunc {
return func(tags []string) bool {
exp, err := regexp.Compile(expr)
if err != nil {
return false
}
return slices.ContainsFunc(tags, func(tag string) bool {
return exp.MatchString(tag)
})
}
}
func andTagFunc(a, b constraintTagFunc) constraintTagFunc {
return func(tags []string) bool {
return a(tags) && b(tags)
}
}
func orTagFunc(a, b constraintTagFunc) constraintTagFunc {
return func(tags []string) bool {
return a(tags) || b(tags)
}
}
func notTagFunc(a constraintTagFunc) constraintTagFunc {
return func(tags []string) bool {
return !a(tags)
}
}