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(originValue) } 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 }