165 lines
3.8 KiB
Go
165 lines
3.8 KiB
Go
|
// Licensed to Elasticsearch B.V. under one or more contributor
|
||
|
// license agreements. See the NOTICE file distributed with
|
||
|
// this work for additional information regarding copyright
|
||
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
||
|
// the Apache License, Version 2.0 (the "License"); you may
|
||
|
// not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing,
|
||
|
// software distributed under the License is distributed on an
|
||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
// KIND, either express or implied. See the License for the
|
||
|
// specific language governing permissions and limitations
|
||
|
// under the License.
|
||
|
|
||
|
package match
|
||
|
|
||
|
import "regexp/syntax"
|
||
|
|
||
|
type Matcher struct {
|
||
|
stringMatcher
|
||
|
}
|
||
|
|
||
|
type ExactMatcher struct {
|
||
|
stringMatcher
|
||
|
}
|
||
|
|
||
|
type stringMatcher interface {
|
||
|
// MatchString tries to find a matching substring.
|
||
|
MatchString(s string) (matched bool)
|
||
|
|
||
|
// Match tries to find a matching substring.
|
||
|
Match(bs []byte) (matched bool)
|
||
|
|
||
|
// Describe the generator
|
||
|
String() string
|
||
|
}
|
||
|
|
||
|
func MustCompile(pattern string) Matcher {
|
||
|
m, err := Compile(pattern)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
func MustCompileExact(pattern string) ExactMatcher {
|
||
|
m, err := CompileExact(pattern)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
// CompileString matches a substring only, the input is not interpreted as
|
||
|
// regular expression
|
||
|
func CompileString(in string) (Matcher, error) {
|
||
|
if in == "" {
|
||
|
return Matcher{(*emptyStringMatcher)(nil)}, nil
|
||
|
}
|
||
|
return Matcher{&substringMatcher{in, []byte(in)}}, nil
|
||
|
}
|
||
|
|
||
|
// Compile regular expression to string matcher. String matcher by default uses
|
||
|
// regular expressions as provided by regexp library, but tries to optimize some
|
||
|
// common cases, replacing expensive patterns with cheaper custom implementations
|
||
|
// or removing terms not necessary for string matching.
|
||
|
func Compile(pattern string) (Matcher, error) {
|
||
|
regex, err := syntax.Parse(pattern, syntax.Perl)
|
||
|
if err != nil {
|
||
|
return Matcher{}, err
|
||
|
}
|
||
|
|
||
|
regex = optimize(regex).Simplify()
|
||
|
m, err := compile(regex)
|
||
|
return Matcher{m}, err
|
||
|
}
|
||
|
|
||
|
func CompileExact(pattern string) (ExactMatcher, error) {
|
||
|
regex, err := syntax.Parse(pattern, syntax.Perl)
|
||
|
if err != nil {
|
||
|
return ExactMatcher{}, err
|
||
|
}
|
||
|
|
||
|
regex = regex.Simplify()
|
||
|
if regex.Op != syntax.OpConcat {
|
||
|
regex = &syntax.Regexp{
|
||
|
Op: syntax.OpConcat,
|
||
|
Sub: []*syntax.Regexp{
|
||
|
patBeginText,
|
||
|
regex,
|
||
|
patEndText,
|
||
|
},
|
||
|
Flags: regex.Flags,
|
||
|
}
|
||
|
} else {
|
||
|
if !eqPrefixRegex(regex, patBeginText) {
|
||
|
regex.Sub = append([]*syntax.Regexp{patBeginText}, regex.Sub...)
|
||
|
}
|
||
|
if !eqSuffixRegex(regex, patEndText) {
|
||
|
regex.Sub = append(regex.Sub, patEndText)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
regex = optimize(regex).Simplify()
|
||
|
m, err := compile(regex)
|
||
|
return ExactMatcher{m}, err
|
||
|
}
|
||
|
|
||
|
func (m *Matcher) Unpack(s string) error {
|
||
|
tmp, err := Compile(s)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
*m = tmp
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (m *Matcher) MatchAnyString(strs []string) bool {
|
||
|
return matchAnyStrings(m.stringMatcher, strs)
|
||
|
}
|
||
|
|
||
|
func (m *Matcher) MatchAllStrings(strs []string) bool {
|
||
|
return matchAllStrings(m.stringMatcher, strs)
|
||
|
}
|
||
|
|
||
|
func (m *ExactMatcher) MatchAnyString(strs []string) bool {
|
||
|
return matchAnyStrings(m.stringMatcher, strs)
|
||
|
}
|
||
|
|
||
|
func (m *ExactMatcher) MatchAllStrings(strs []string) bool {
|
||
|
return matchAllStrings(m.stringMatcher, strs)
|
||
|
}
|
||
|
|
||
|
func (m *ExactMatcher) Unpack(s string) error {
|
||
|
tmp, err := CompileExact(s)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
*m = tmp
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func matchAnyStrings(m stringMatcher, strs []string) bool {
|
||
|
for _, s := range strs {
|
||
|
if m.MatchString(s) {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func matchAllStrings(m stringMatcher, strs []string) bool {
|
||
|
for _, s := range strs {
|
||
|
if !m.MatchString(s) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|