1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
package wildcard
import (
"regexp"
"strings"
"github.com/pkg/errors"
)
// New returns a wildcard object for a string that contains "*" symbols.
func New(s string) (*Wildcard, error) {
reStr, err := Wildcard2Regexp(s)
if err != nil {
return nil, errors.Wrapf(err, "failed to translate wildcard %q to regexp", s)
}
re, err := regexp.Compile(reStr)
if err != nil {
return nil, errors.Wrapf(err, "failed to compile regexp %q (translated from wildcard %q)", reStr, s)
}
w := &Wildcard{
orig: s,
re: re,
}
return w, nil
}
// Wildcard2Regexp translates a wildcard string to a regexp string.
func Wildcard2Regexp(wildcard string) (string, error) {
s := regexp.QuoteMeta(wildcard)
if strings.Contains(s, "\\*\\*") {
return "", errors.New("invalid wildcard: \"**\"")
}
s = strings.ReplaceAll(s, "\\*", "(.*)")
s = "^" + s + "$"
return s, nil
}
// Wildcard is a wildcard matcher object.
type Wildcard struct {
orig string
re *regexp.Regexp
}
// String implements fmt.Stringer.
func (w *Wildcard) String() string {
return w.orig
}
// Match returns a non-nil Match on match.
func (w *Wildcard) Match(q string) *Match {
submatches := w.re.FindStringSubmatch(q)
if len(submatches) == 0 {
return nil
}
m := &Match{
w: w,
Submatches: submatches,
// FIXME: avoid executing regexp twice
idx: w.re.FindStringSubmatchIndex(q),
}
return m
}
// Match is a matched result.
type Match struct {
w *Wildcard
Submatches []string // 0: the entire query, 1: the first submatch, 2: the second submatch, ...
idx []int
}
// String implements fmt.Stringer.
func (m *Match) String() string {
if len(m.Submatches) == 0 {
return ""
}
return m.Submatches[0]
}
// Format formats submatch strings like "$1", "$2".
func (m *Match) Format(f string) (string, error) {
if m.w == nil || len(m.Submatches) == 0 || len(m.idx) == 0 {
return "", errors.New("invalid state")
}
var b []byte
b = m.w.re.ExpandString(b, f, m.Submatches[0], m.idx)
return string(b), nil
}
|