github.com/hdt3213/godis@v1.2.9/lib/wildcard/wildcard.go (about)

     1  package wildcard
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  	"strings"
     7  )
     8  
     9  // Pattern represents a wildcard pattern
    10  type Pattern struct {
    11  	exp *regexp.Regexp
    12  }
    13  
    14  var replaceMap = map[byte]string{
    15  	// characters in the wildcard that must be escaped in the regexp
    16  	'+': `\+`,
    17  	')': `\)`,
    18  	'$': `\$`,
    19  	'.': `\.`,
    20  	'{': `\{`,
    21  	'}': `\}`,
    22  	'|': `\|`,
    23  	'*': ".*",
    24  	'?': ".",
    25  }
    26  
    27  var errEndWithEscape = "end with escape \\"
    28  
    29  // CompilePattern convert wildcard string to Pattern
    30  func CompilePattern(src string) (*Pattern, error) {
    31  	regexSrc := strings.Builder{}
    32  	regexSrc.WriteByte('^')
    33  	for i := 0; i < len(src); i++ {
    34  		ch := src[i]
    35  		if ch == '\\' {
    36  			if i == len(src)-1 {
    37  				return nil, errors.New(errEndWithEscape)
    38  			}
    39  			regexSrc.WriteByte(ch)
    40  			regexSrc.WriteByte(src[i+1])
    41  			i++ // skip escaped character
    42  		} else if ch == '^' {
    43  			if i == 0 {
    44  				regexSrc.WriteString(`\^`)
    45  			} else if i == 1 {
    46  				if src[i-1] == '[' {
    47  					regexSrc.WriteString(`^`) // src is: [^
    48  				} else {
    49  					regexSrc.WriteString(`\^`)
    50  				}
    51  			} else {
    52  				if src[i-1] == '[' && src[i-2] != '\\' {
    53  					regexSrc.WriteString(`^`) // src is: [^, except \[^
    54  				} else {
    55  					regexSrc.WriteString(`\^`)
    56  				}
    57  			}
    58  		} else if escaped, toEscape := replaceMap[ch]; toEscape {
    59  			regexSrc.WriteString(escaped)
    60  		} else {
    61  			regexSrc.WriteByte(ch)
    62  		}
    63  	}
    64  	regexSrc.WriteByte('$')
    65  	re, err := regexp.Compile(regexSrc.String())
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return &Pattern{
    70  		exp: re,
    71  	}, nil
    72  }
    73  
    74  // IsMatch returns whether the given string matches pattern
    75  func (p *Pattern) IsMatch(s string) bool {
    76  	return p.exp.Match([]byte(s))
    77  }