172 lines
2.8 KiB
Go
172 lines
2.8 KiB
Go
package matcher
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
const radixWildcard = '*'
|
|
|
|
type radixNode struct {
|
|
exact bool
|
|
next map[rune]*radixNode
|
|
}
|
|
|
|
type radixMatcher struct {
|
|
root *radixNode
|
|
|
|
caseInsensitive bool
|
|
saved []string
|
|
regexps []*regexp.Regexp
|
|
}
|
|
|
|
type RadixOpt func(m *radixMatcher)
|
|
|
|
func RadixCaseInsensitive() RadixOpt {
|
|
return func(m *radixMatcher) {
|
|
m.caseInsensitive = true
|
|
}
|
|
}
|
|
|
|
func NewRadix(opts ...RadixOpt) *radixMatcher {
|
|
m := &radixMatcher{
|
|
root: &radixNode{
|
|
next: make(map[rune]*radixNode),
|
|
},
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt(m)
|
|
}
|
|
|
|
return m
|
|
}
|
|
|
|
func (r *radixMatcher) MatchByPattern(value string) (pattern string) {
|
|
originValue := value
|
|
if r.caseInsensitive {
|
|
value = strings.ToLower(value)
|
|
}
|
|
|
|
node := r.root
|
|
lastIdx := len([]rune(value)) - 1
|
|
|
|
var sb strings.Builder
|
|
for i, v := range value {
|
|
var ok bool
|
|
if _, ok := node.next[radixWildcard]; ok {
|
|
_, _ = sb.WriteRune(radixWildcard)
|
|
return sb.String()
|
|
}
|
|
|
|
node, ok = node.next[v]
|
|
if !ok {
|
|
return r.findByRegexp(originValue)
|
|
}
|
|
|
|
sb.WriteRune(v)
|
|
|
|
if i != lastIdx {
|
|
continue
|
|
}
|
|
|
|
if !node.exact {
|
|
return r.findByRegexp(value)
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
return r.findByRegexp(originValue)
|
|
}
|
|
|
|
func (r *radixMatcher) Match(value string) bool {
|
|
originValue := value
|
|
if r.caseInsensitive {
|
|
value = strings.ToLower(value)
|
|
}
|
|
|
|
node := r.root
|
|
lastIdx := len([]rune(value)) - 1
|
|
for i, v := range value {
|
|
var ok bool
|
|
if _, ok = node.next[radixWildcard]; ok {
|
|
return true
|
|
}
|
|
|
|
node, ok = node.next[v]
|
|
if !ok {
|
|
return r.findByRegexp(originValue) != ""
|
|
}
|
|
|
|
if i == lastIdx {
|
|
return node.exact
|
|
}
|
|
}
|
|
|
|
return r.findByRegexp(originValue) != ""
|
|
}
|
|
|
|
func (r *radixMatcher) findByRegexp(value string) (regexpPattern string) {
|
|
for _, rx := range r.regexps {
|
|
if rx.MatchString(value) {
|
|
return rx.String()
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (r *radixMatcher) RegisterRegexp(regexpPattern string) {
|
|
pattern := regexp.MustCompile(regexpPattern)
|
|
r.regexps = append(r.regexps, pattern)
|
|
}
|
|
|
|
func (r *radixMatcher) Register(pattern string) {
|
|
if len(pattern) == 0 {
|
|
panic("unable to handle empty pattern")
|
|
}
|
|
|
|
if r.caseInsensitive {
|
|
pattern = strings.ToLower(pattern)
|
|
}
|
|
|
|
r.saved = append(r.saved, pattern)
|
|
|
|
node := r.root
|
|
lastIdx := len([]rune(pattern)) - 1
|
|
for i, v := range pattern {
|
|
nextNode, ok := node.next[v]
|
|
if !ok {
|
|
nextNode = &radixNode{
|
|
next: make(map[rune]*radixNode),
|
|
}
|
|
node.next[v] = nextNode
|
|
}
|
|
node = nextNode
|
|
|
|
if v == '*' {
|
|
return
|
|
}
|
|
|
|
if i != lastIdx {
|
|
continue
|
|
}
|
|
|
|
node.exact = true
|
|
}
|
|
}
|
|
|
|
func (r *radixMatcher) Patterns() []string {
|
|
ownIdx := len(r.saved)
|
|
out := make([]string, len(r.saved)+len(r.regexps))
|
|
copy(out, r.saved[:ownIdx])
|
|
|
|
for i := 0; i < len(r.regexps); i++ {
|
|
idx := i + ownIdx
|
|
out[idx] = r.regexps[i].String()
|
|
}
|
|
|
|
return out
|
|
}
|