github.com/tobgu/qframe@v0.4.0/internal/strings/match.go (about) 1 package strings 2 3 import ( 4 "regexp" 5 "strings" 6 7 "github.com/tobgu/qframe/qerrors" 8 ) 9 10 type Matcher interface { 11 Matches(s string) bool 12 } 13 14 type CIStringMatcher struct { 15 matchString string //nolint:structcheck 16 buf []byte //nolint:structcheck 17 } 18 19 type CIPrefixMatcher CIStringMatcher 20 21 func (m *CIPrefixMatcher) Matches(s string) bool { 22 return strings.HasPrefix(ToUpper(&m.buf, s), m.matchString) 23 } 24 25 type CISuffixMatcher CIStringMatcher 26 27 func (m *CISuffixMatcher) Matches(s string) bool { 28 return strings.HasSuffix(ToUpper(&m.buf, s), m.matchString) 29 } 30 31 type CIContainsMatcher CIStringMatcher 32 33 func (m *CIContainsMatcher) Matches(s string) bool { 34 return strings.Contains(ToUpper(&m.buf, s), m.matchString) 35 } 36 37 type CIExactMatcher CIStringMatcher 38 39 func (m *CIExactMatcher) Matches(s string) bool { 40 return ToUpper(&m.buf, s) == m.matchString 41 } 42 43 type StringMatcher struct { 44 matchString string //nolint:structcheck 45 } 46 47 type PrefixMatcher StringMatcher 48 49 func (m *PrefixMatcher) Matches(s string) bool { 50 return strings.HasPrefix(s, m.matchString) 51 } 52 53 type SuffixMatcher StringMatcher 54 55 func (m *SuffixMatcher) Matches(s string) bool { 56 return strings.HasSuffix(s, m.matchString) 57 } 58 59 type ContainsMatcher StringMatcher 60 61 func (m *ContainsMatcher) Matches(s string) bool { 62 return strings.Contains(s, m.matchString) 63 } 64 65 type ExactMatcher StringMatcher 66 67 func (m *ExactMatcher) Matches(s string) bool { 68 return s == m.matchString 69 } 70 71 type RegexpMatcher struct { 72 r *regexp.Regexp 73 } 74 75 func (m *RegexpMatcher) Matches(s string) bool { 76 return m.r.MatchString(s) 77 } 78 79 func trimPercent(s string) string { 80 s = strings.TrimPrefix(s, "%") 81 s = strings.TrimSuffix(s, "%") 82 return s 83 } 84 85 func NewMatcher(comparatee string, caseSensitive bool) (Matcher, error) { 86 fuzzyStart := strings.HasPrefix(comparatee, "%") 87 fuzzyEnd := strings.HasSuffix(comparatee, "%") 88 if regexp.QuoteMeta(comparatee) != comparatee { 89 // There are regex characters in the match string 90 if !fuzzyStart { 91 comparatee = "^" + comparatee 92 } else { 93 comparatee = comparatee[1:] 94 } 95 96 if !fuzzyEnd { 97 comparatee = comparatee + "$" 98 } else { 99 comparatee = comparatee[:len(comparatee)-1] 100 } 101 102 if !caseSensitive { 103 comparatee = "(?i)" + comparatee 104 } 105 106 r, err := regexp.Compile(comparatee) 107 if err != nil { 108 return nil, qerrors.Propagate("string like", err) 109 } 110 111 return &RegexpMatcher{r: r}, nil 112 } 113 114 if !caseSensitive { 115 comparatee = strings.ToUpper(comparatee) 116 117 // Initial size, this will grow if needed 118 buf := make([]byte, 10) 119 if fuzzyStart && fuzzyEnd { 120 return &CIContainsMatcher{matchString: trimPercent(comparatee), buf: buf}, nil 121 } 122 123 if fuzzyStart { 124 return &CISuffixMatcher{matchString: trimPercent(comparatee), buf: buf}, nil 125 } 126 127 if fuzzyEnd { 128 return &CIPrefixMatcher{matchString: trimPercent(comparatee), buf: buf}, nil 129 } 130 131 return &CIExactMatcher{matchString: comparatee, buf: buf}, nil 132 } 133 134 if fuzzyStart && fuzzyEnd { 135 return &ContainsMatcher{matchString: trimPercent(comparatee)}, nil 136 } 137 138 if fuzzyStart { 139 return &SuffixMatcher{matchString: trimPercent(comparatee)}, nil 140 } 141 142 if fuzzyEnd { 143 return &PrefixMatcher{matchString: trimPercent(comparatee)}, nil 144 } 145 146 return &ExactMatcher{matchString: comparatee}, nil 147 }