This commit is contained in:
s.kamardin 2015-11-30 18:24:20 +03:00
parent d4d579d13e
commit 678ba81174
3 changed files with 99 additions and 57 deletions

88
glob.go
View File

@ -13,40 +13,28 @@ const (
var Chars = []string{Any, SuperAny, SingleAny} var Chars = []string{Any, SuperAny, SingleAny}
type Matcher interface { type Glob interface {
Match(string) bool Match(string) bool
} }
func firstIndexOfChars(p string, any []string) (min int, c string) { func New(pattern string, d ...string) Glob {
l := len(p) chunks := parse(pattern, nil, strings.Join(d, ""))
min = l
weight := 0
for _, s := range any { if len(chunks) == 1 {
w := len(s) return chunks[0]
i := strings.Index(p, s)
if i != -1 && i <= min && w > weight {
min = i
weight = w
c = s
}
} }
if min == l { return &composite{chunks}
return -1, ""
}
return
} }
func parse(p string, m []Matcher, d []string) ([]Matcher, error) { func parse(p string, m []Glob, d string) []Glob {
if len(p) == 0 { if len(p) == 0 {
return m, nil return m
} }
i, c := firstIndexOfChars(p, Chars) i, c := firstIndexOfChars(p, Chars)
if i == -1 { if i == -1 {
return append(m, raw{p}), nil return append(m, raw{p})
} }
if i > 0 { if i > 0 {
@ -65,19 +53,6 @@ func parse(p string, m []Matcher, d []string) ([]Matcher, error) {
return parse(p[i+len(c):], m, 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 { type raw struct {
s string s string
} }
@ -89,39 +64,36 @@ func (self raw) String() string {
} }
type multiple struct { type multiple struct {
delimiters []string delimiters string
} }
func (self multiple) Match(s string) bool { func (self multiple) Match(s string) bool {
i, _ := firstIndexOfChars(s, self.delimiters) return strings.IndexAny(s, self.delimiters) == -1
return i == -1
} }
func (self multiple) String() string { func (self multiple) String() string {
return fmt.Sprintf("[multiple:%s]", self.delimiters) return fmt.Sprintf("[multiple:%s]", self.delimiters)
} }
type single struct { type single struct {
delimiters []string delimiters string
} }
func (self single) Match(s string) bool { func (self single) Match(s string) bool {
if len(s) != 1 { return len(s) == 1 && strings.IndexAny(s, self.delimiters) == -1
return false
}
i, _ := firstIndexOfChars(s, self.delimiters)
return i == -1
} }
func (self single) String() string { func (self single) String() string {
return fmt.Sprintf("[single:%s]", self.delimiters) return fmt.Sprintf("[single:%s]", self.delimiters)
} }
type composite struct { type composite struct {
chunks []Matcher chunks []Glob
} }
func (self composite) Match(m string) bool { func (self composite) Match(m string) bool {
var prev Matcher var prev Glob
for _, c := range self.chunks { for _, c := range self.chunks {
if str, ok := c.(raw); ok { if str, ok := c.(raw); ok {
@ -153,3 +125,25 @@ func (self composite) Match(m string) bool {
return len(m) == 0 return len(m) == 0
} }
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
}

View File

@ -56,10 +56,12 @@ func TestGlob(t *testing.T) {
glob(true, "a.?.c", "a.b.c", "."), glob(true, "a.?.c", "a.b.c", "."),
glob(true, "a.?.?", "a.b.c", "."), glob(true, "a.?.?", "a.b.c", "."),
glob(true, "?at", "cat"), glob(true, "?at", "cat"),
glob(true, "?at", "fat"),
glob(true, "*", "abc"), glob(true, "*", "abc"),
glob(true, "**", "a.b.c", "."), glob(true, "**", "a.b.c", "."),
glob(false, "?at", "at"), glob(false, "?at", "at"),
glob(false, "?at", "fat", "f"),
glob(false, "a.*", "a.b.c", "."), glob(false, "a.*", "a.b.c", "."),
glob(false, "a.?.c", "a.bb.c", "."), glob(false, "a.?.c", "a.bb.c", "."),
glob(false, "*", "a.b.c", "."), glob(false, "*", "a.b.c", "."),
@ -74,11 +76,7 @@ func TestGlob(t *testing.T) {
glob(false, "*is", "this is a test"), glob(false, "*is", "this is a test"),
glob(false, "*no*", "this is a test"), glob(false, "*no*", "this is a test"),
}{ }{
g, err := New(test.pattern, test.delimiters...) g := New(test.pattern, test.delimiters...)
if err != nil {
t.Error(err)
continue
}
result := g.Match(test.match) result := g.Match(test.match)
if result != test.should { if result != test.should {
@ -91,14 +89,14 @@ func TestGlob(t *testing.T) {
const Pattern = "*cat*eyes*" const Pattern = "*cat*eyes*"
const ExpPattern = ".*cat.*eyes.*" const ExpPattern = ".*cat.*eyes.*"
const String = "my cat has very bright eyes" const String = "my cat has very bright eyes"
//const Pattern = "*.google.com"
//const ExpPattern = ".*google\\.com"
//const String = "mail.google.com"
func BenchmarkGobwas(b *testing.B) { func BenchmarkGobwas(b *testing.B) {
for i := 0; i < b.N; i++ { m := New(Pattern)
m, err := New(Pattern)
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
_ = m.Match(String) _ = m.Match(String)
} }
} }

50
readme.md Normal file
View File

@ -0,0 +1,50 @@
# glob.[go](https://golang.org)
Simple globbing library.
## Install
```shell
go get github.com/gobwas/glob
```
## Example
```go
package main
import "github.com/gobwas/glob"
func main() {
var g glob.Glob
// create simple glob
g = glob.New("*.github.com")
g.Match("api.github.com") // true
// create new glob with set of delimiters as ["."]
g = glob.New("api.*.com", ".")
g.Match("api.github.com") // true
g.Match("api.gi.hub.com") // false
// create new glob with set of delimiters as ["."]
// but now with super wildcard
g = glob.New("api.**.com", ".")
g.Match("api.github.com") // true
g.Match("api.gi.hub.com") // true
// create glob with single symbol wildcard
g = glob.New("?at")
g.Match("cat") // true
g.Match("fat") // true
g.Match("at") // false
// create glob with single symbol wildcard and delimiters ["f"]
g = glob.New("?at", "f")
g.Match("cat") // true
g.Match("fat") // false
g.Match("at") // false
}
```