forked from mirror/glob
156 lines
2.3 KiB
Go
156 lines
2.3 KiB
Go
|
package glob
|
||
|
|
||
|
import (
|
||
|
"strings"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
Any = "*"
|
||
|
SuperAny = "**"
|
||
|
SingleAny = "?"
|
||
|
)
|
||
|
|
||
|
var Chars = []string{Any, SuperAny, SingleAny}
|
||
|
|
||
|
type Matcher interface {
|
||
|
Match(string) bool
|
||
|
}
|
||
|
|
||
|
func firstIndexOfChars(p string, any []string) (min int, c string) {
|
||
|
l := len(p)
|
||
|
min = l
|
||
|
weight := 0
|
||
|
|
||
|
for _, s := range any {
|
||
|
w := len(s)
|
||
|
i := strings.Index(p, s)
|
||
|
if i != -1 && i <= min && w > weight {
|
||
|
min = i
|
||
|
weight = w
|
||
|
c = s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if min == l {
|
||
|
return -1, ""
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func parse(p string, m []Matcher, d []string) ([]Matcher, error) {
|
||
|
if len(p) == 0 {
|
||
|
return m, nil
|
||
|
}
|
||
|
|
||
|
i, c := firstIndexOfChars(p, Chars)
|
||
|
if i == -1 {
|
||
|
return append(m, raw{p}), nil
|
||
|
}
|
||
|
|
||
|
if i > 0 {
|
||
|
m = append(m, raw{p[0:i]})
|
||
|
}
|
||
|
|
||
|
switch c {
|
||
|
case SuperAny:
|
||
|
m = append(m, multiple{})
|
||
|
case Any:
|
||
|
m = append(m, multiple{d})
|
||
|
case SingleAny:
|
||
|
m = append(m, single{d})
|
||
|
}
|
||
|
|
||
|
return parse(p[i+len(c):], m, d)
|
||
|
}
|
||
|
|
||
|
func New(pattern string, d ...string) (Matcher, error) {
|
||
|
chunks, err := parse(pattern, nil, d)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if len(chunks) == 1 {
|
||
|
return chunks[0], nil
|
||
|
}
|
||
|
|
||
|
return &composite{chunks}, nil
|
||
|
}
|
||
|
|
||
|
type raw struct {
|
||
|
s string
|
||
|
}
|
||
|
func (self raw) Match(s string) bool {
|
||
|
return self.s == s
|
||
|
}
|
||
|
func (self raw) String() string {
|
||
|
return fmt.Sprintf("[raw:%s]", self.s)
|
||
|
}
|
||
|
|
||
|
type multiple struct {
|
||
|
delimiters []string
|
||
|
}
|
||
|
func (self multiple) Match(s string) bool {
|
||
|
i, _ := firstIndexOfChars(s, self.delimiters)
|
||
|
return i == -1
|
||
|
}
|
||
|
func (self multiple) String() string {
|
||
|
return fmt.Sprintf("[multiple:%s]", self.delimiters)
|
||
|
}
|
||
|
|
||
|
type single struct {
|
||
|
delimiters []string
|
||
|
}
|
||
|
func (self single) Match(s string) bool {
|
||
|
if len(s) != 1 {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
i, _ := firstIndexOfChars(s, self.delimiters)
|
||
|
|
||
|
return i == -1
|
||
|
}
|
||
|
func (self single) String() string {
|
||
|
return fmt.Sprintf("[single:%s]", self.delimiters)
|
||
|
}
|
||
|
|
||
|
type composite struct {
|
||
|
chunks []Matcher
|
||
|
}
|
||
|
|
||
|
|
||
|
func (self composite) Match(m string) bool {
|
||
|
var prev Matcher
|
||
|
|
||
|
for _, c := range self.chunks {
|
||
|
if str, ok := c.(raw); ok {
|
||
|
i := strings.Index(m, str.s)
|
||
|
if i == -1 {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
l := len(str.s)
|
||
|
|
||
|
if prev != nil {
|
||
|
if !prev.Match(m[:i]) {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
prev = nil
|
||
|
}
|
||
|
|
||
|
m = m[i+l:]
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
prev = c
|
||
|
}
|
||
|
|
||
|
if prev != nil {
|
||
|
return prev.Match(m)
|
||
|
}
|
||
|
|
||
|
return len(m) == 0
|
||
|
}
|