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  }