diff --git a/doublestar_test.go b/doublestar_test.go index a4eeb33..3e00b3c 100644 --- a/doublestar_test.go +++ b/doublestar_test.go @@ -243,6 +243,53 @@ func TestMatch(t *testing.T) { } } +func TestCompile(t *testing.T) { + for idx, tt := range matchTests { + testCompileWith(t, idx, tt) + testMustCompileWith(t, idx, tt) + } +} + +func testCompileWith(t *testing.T, idx int, tt MatchTest) { + defer func() { + if r := recover(); r != nil { + t.Errorf("#%v. Match(%#q, %#q) panicked: %#v", idx, tt.pattern, tt.testPath, r) + } + }() + + ok := false + pat, err := Compile(tt.pattern) + if err == nil { + ok = pat.Match(tt.testPath) + } + if ok != tt.shouldMatch || err != tt.expectedErr { + t.Errorf("#%v. Match(%#q, %#q) = %v, %v want %v, %v", idx, tt.pattern, tt.testPath, ok, err, tt.shouldMatch, tt.expectedErr) + } + + if tt.isStandard { + stdOk, stdErr := path.Match(tt.pattern, tt.testPath) + if ok != stdOk || !compareErrors(err, stdErr) { + t.Errorf("#%v. Match(%#q, %#q) != path.Match(...). Got %v, %v want %v, %v", idx, tt.pattern, tt.testPath, ok, err, stdOk, stdErr) + } + } +} + +func testMustCompileWith(t *testing.T, idx int, tt MatchTest) { + defer func() { + err := recover() + if err != tt.expectedErr { + t.Errorf("#%v. Match(%#q, %#q) panicked: %#v", idx, tt.pattern, tt.testPath, err) + } + }() + + pat := MustCompile(tt.pattern) + ok := pat.Match(tt.testPath) + + if ok != tt.shouldMatch { + t.Errorf("#%v. MustCompileMatch(%#q, %#q) = %v want %v, %v", idx, tt.pattern, tt.testPath, ok, tt.shouldMatch, tt.expectedErr) + } +} + func testMatchWith(t *testing.T, idx int, tt MatchTest) { defer func() { if r := recover(); r != nil { diff --git a/match.go b/match.go index 1ff50f6..6106755 100644 --- a/match.go +++ b/match.go @@ -88,6 +88,39 @@ func PathMatchUnvalidated(pattern, name string) bool { return matched } +// Pattern is a representation of a compiled glob pattern. +type Pattern struct { + // initial implementation doesn't actually "compile", just saves the pattern to use when matching + // Obviously long term we'll want to store some kind of tree or something + pattern string +} + +// Compile parses a pattern and returns, if successful, a [Pattern] object +// that can be used to match against text. +func Compile(pattern string) (*Pattern, error) { + if !ValidatePattern(pattern) { + return nil, ErrBadPattern + } + return &Pattern{ + pattern: pattern, + }, nil +} + +// MustCompile is like Compile but panics if the expression cannot be parsed. +func MustCompile(pattern string) *Pattern { + + p, err := Compile(pattern) + if err != nil { + panic(err) + } + return p +} + +// Match reports whether the name matches this pattern +func (p *Pattern) Match(name string) bool { + return MatchUnvalidated(p.pattern, name) +} + func matchWithSeparator(pattern, name string, separator rune, validate bool, caseInsensitive bool) (matched bool, err error) { return doMatchWithSeparator(pattern, name, separator, validate, caseInsensitive, -1, -1, -1, -1, 0, 0) }