Skip to content

Commit fbc4434

Browse files
committed
fixup! feat: include/exclude with text/template filters
Simplifies the Matcher approach, removing the complicated Matcher types in favour of simple functions. Signed-off-by: Brian McGee <[email protected]>
1 parent 7bb0563 commit fbc4434

File tree

10 files changed

+171
-268
lines changed

10 files changed

+171
-268
lines changed

cmd/root_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ func TestIncludesAndExcludes(t *testing.T) {
437437
echo.Excludes = []string{"*.py"}
438438

439439
treefmt(t,
440-
withArgs("-c"),
440+
withArgs("-c", "-vv"),
441441
withConfig(configPath, cfg),
442442
withNoError(t),
443443
withStats(t, map[stats.Type]int{

format/composite.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var ErrFormattingFailures = errors.New("formatting failures detected")
2727
type CompositeFormatter struct {
2828
cfg *config.Config
2929
stats *stats.Stats
30-
globalMatcher *matcher.CompositeMatcher
30+
globalMatchFn matcher.MatchFn
3131

3232
unmatchedLevel log.Level
3333

@@ -38,7 +38,7 @@ type CompositeFormatter struct {
3838
// match filters the file against global excludes and returns a list of formatters that want to process the file.
3939
func (c *CompositeFormatter) match(file *walk.File) (bool, []*Formatter, error) {
4040
// first check if this file has been globally excluded
41-
result, err := c.globalMatcher.Wants(file)
41+
result, err := c.globalMatchFn(file)
4242
if err != nil {
4343
return false, nil, fmt.Errorf("error applying global excludes to %s: %w", file.RelPath, err)
4444
}
@@ -160,22 +160,23 @@ func NewCompositeFormatter(
160160
statz *stats.Stats,
161161
batchSize int,
162162
) (*CompositeFormatter, error) {
163-
matchers := make([]matcher.Matcher, 2)
163+
var (
164+
err error
165+
matchers = make([]matcher.MatchFn, 2)
166+
)
164167

165-
var err error
166-
167-
// compile global exclude globs
168-
matchers[0], err = matcher.NewGlobExclusionMatcher(cfg.Excludes)
168+
// create global exclusion matcher based on globs and templates
169+
matchers[0], err = matcher.ExcludeGlobs(cfg.Excludes)
169170
if err != nil {
170171
return nil, fmt.Errorf("failed to compile global excludes: %w", err)
171172
}
172173

173-
matchers[1], err = matcher.NewTemplateExclusionMatcher(cfg.Reject)
174+
matchers[1], err = matcher.ExcludeTemplates(cfg.Reject)
174175
if err != nil {
175176
return nil, fmt.Errorf("failed to compile global reject: %w", err)
176177
}
177178

178-
globalMatcher := matcher.NewCompositeMatcher(matchers)
179+
globalMatcher := matcher.Combine(nil, matchers)
179180

180181
// parse unmatched log level
181182
unmatchedLevel, err := log.ParseLevel(cfg.OnUnmatched)
@@ -215,7 +216,7 @@ func NewCompositeFormatter(
215216
return &CompositeFormatter{
216217
cfg: cfg,
217218
stats: statz,
218-
globalMatcher: globalMatcher,
219+
globalMatchFn: globalMatcher,
219220
unmatchedLevel: unmatchedLevel,
220221

221222
scheduler: scheduler,

format/formatter.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type Formatter struct {
3838
workingDir string
3939

4040
// internal, compiled versions of inclusion and exclusion matchers.
41-
matcher *matcher.CompositeMatcher
41+
matchFn matcher.MatchFn
4242
}
4343

4444
func (f *Formatter) Name() string {
@@ -126,7 +126,7 @@ func (f *Formatter) Apply(ctx context.Context, files []*walk.File) error {
126126
// its configured Includes and Excludes patterns and Select and Reject
127127
// templates.
128128
func (f *Formatter) Wants(file *walk.File) (matcher.Result, error) {
129-
result, err := f.matcher.Wants(file)
129+
result, err := f.matchFn(file)
130130
if err != nil {
131131
return result, fmt.Errorf("error applying matcher to %s: %w", file.RelPath, err)
132132
}
@@ -179,29 +179,30 @@ func newFormatter(
179179
return nil, fmt.Errorf("formatter '%v' has no includes or select filters", f.name)
180180
}
181181

182-
matchers := make([]matcher.Matcher, 4)
182+
includes := make([]matcher.MatchFn, 2)
183+
excludes := make([]matcher.MatchFn, 2)
183184

184-
matchers[0], err = matcher.NewGlobInclusionMatcher(cfg.Includes)
185+
includes[0], err = matcher.IncludeGlobs(cfg.Includes)
185186
if err != nil {
186187
return nil, fmt.Errorf("failed to compile formatter '%v' includes: %w", f.name, err)
187188
}
188189

189-
matchers[1], err = matcher.NewGlobExclusionMatcher(cfg.Excludes)
190+
includes[1], err = matcher.IncludeTemplates(cfg.Select)
190191
if err != nil {
191-
return nil, fmt.Errorf("failed to compile formatter '%v' excludes: %w", f.name, err)
192+
return nil, fmt.Errorf("failed to compile formatter '%v' select: %w", f.name, err)
192193
}
193194

194-
matchers[2], err = matcher.NewTemplateInclusionMatcher(cfg.Select)
195+
excludes[0], err = matcher.ExcludeGlobs(cfg.Excludes)
195196
if err != nil {
196-
return nil, fmt.Errorf("failed to compile formatter '%v' select: %w", f.name, err)
197+
return nil, fmt.Errorf("failed to compile formatter '%v' excludes: %w", f.name, err)
197198
}
198199

199-
matchers[3], err = matcher.NewTemplateExclusionMatcher(cfg.Reject)
200+
excludes[1], err = matcher.ExcludeTemplates(cfg.Reject)
200201
if err != nil {
201202
return nil, fmt.Errorf("failed to compile formatter '%v' reject: %w", f.name, err)
202203
}
203204

204-
f.matcher = matcher.NewCompositeMatcher(matchers)
205+
f.matchFn = matcher.Combine(includes, excludes)
205206

206207
return &f, nil
207208
}

matcher/composite.go

Lines changed: 0 additions & 39 deletions
This file was deleted.

matcher/glob.go

Lines changed: 15 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,66 +7,35 @@ import (
77
"github.com/numtide/treefmt/v2/walk"
88
)
99

10-
type globMatcher struct {
11-
globs []glob.Glob
12-
patterns []string
13-
}
10+
func IncludeGlobs(patterns []string) (MatchFn, error) {
11+
if len(patterns) == 0 {
12+
return noOp, nil
13+
}
1414

15-
func newGlobMatcher(patterns []string) (*globMatcher, error) {
1615
globs := make([]glob.Glob, len(patterns))
1716

1817
for i, pattern := range patterns {
1918
g, err := glob.Compile(pattern)
2019
if err != nil {
21-
return nil, fmt.Errorf("failed to compile include pattern '%v': %w", pattern, err)
20+
return nil, fmt.Errorf("failed to compile glob pattern '%v': %w", pattern, err)
2221
}
2322

2423
globs[i] = g
2524
}
2625

27-
return &globMatcher{globs: globs, patterns: patterns}, nil
28-
}
29-
30-
func (gm *globMatcher) Ignore() bool {
31-
return len(gm.globs) < 1
32-
}
33-
34-
func (gm *globMatcher) Matches(file *walk.File) (bool, error) {
35-
for _, g := range gm.globs {
36-
if g.Match(file.RelPath) {
37-
return true, nil
26+
return func(file *walk.File) (Result, error) {
27+
for _, g := range globs {
28+
if g.Match(file.RelPath) {
29+
return Wanted, nil
30+
}
3831
}
39-
}
40-
41-
return false, nil
42-
}
43-
44-
type GlobInclusionMatcher struct {
45-
*inclusionMatcher
46-
globMatcher
47-
}
48-
49-
//nolint:ireturn
50-
func NewGlobInclusionMatcher(patterns []string) (Matcher, error) {
51-
gm, err := newGlobMatcher(patterns)
52-
if err != nil {
53-
return nil, err
54-
}
55-
56-
return &GlobInclusionMatcher{globMatcher: *gm}, nil
57-
}
5832

59-
type GlobExclusionMatcher struct {
60-
*exclusionMatcher
61-
globMatcher
33+
return Indifferent, nil
34+
}, nil
6235
}
6336

64-
//nolint:ireturn
65-
func NewGlobExclusionMatcher(patterns []string) (Matcher, error) {
66-
gm, err := newGlobMatcher(patterns)
67-
if err != nil {
68-
return nil, err
69-
}
37+
func ExcludeGlobs(patterns []string) (MatchFn, error) {
38+
includeFn, err := IncludeGlobs(patterns)
7039

71-
return &GlobExclusionMatcher{globMatcher: *gm}, nil
40+
return invert(includeFn), err
7241
}

matcher/glob_test.go

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,67 +9,59 @@ import (
99
"github.com/stretchr/testify/require"
1010
)
1111

12-
func testGlobMatcher(t *testing.T, as *require.Assertions, create func([]string) (matcher.Matcher, error)) {
12+
func mockFile(t *testing.T, path string) *walk.File {
1313
t.Helper()
1414

15-
f := func(path string) *walk.File {
16-
return &walk.File{
17-
Path: path,
18-
RelPath: path,
19-
}
15+
return &walk.File{
16+
Path: path,
17+
RelPath: path,
2018
}
19+
}
20+
21+
func testGlobMatcher(t *testing.T, factory func(patterns []string) (matcher.MatchFn, error), expected matcher.Result) {
22+
t.Helper()
2123

22-
var (
23-
m matcher.Matcher
24-
err error
25-
target matcher.Result
26-
)
24+
as := require.New(t)
2725

2826
// Empty list; `Wants` should always return `true`.
29-
m, err = create([]string{})
27+
matchFn, err := factory(nil)
3028
as.NoError(err)
31-
testutil.MatcherTestEmpty(t, as, m)
3229

33-
if m.Invert() {
34-
target = matcher.Unwanted
35-
} else {
36-
target = matcher.Wanted
37-
}
30+
result, err := matchFn(&walk.File{RelPath: "test/foo/bar.txt"})
31+
as.NoError(err)
32+
as.Equal(matcher.Indifferent, result)
3833

3934
// File extension
40-
m, err = create([]string{"*.txt"})
35+
matchFn, err = factory([]string{"*.txt"})
4136
as.NoError(err)
42-
testutil.MatcherTestResults(t, as, m, map[matcher.Result][]*walk.File{
43-
target: {f("test/foo/bar.txt")},
44-
matcher.Indifferent: {f("test/foo/bar.txtz"), f("test/foo/bar.flob")},
37+
38+
testutil.MatcherTestResults(t, as, matchFn, map[matcher.Result][]*walk.File{
39+
expected: {mockFile(t, "test/foo/bar.txt")},
40+
matcher.Indifferent: {mockFile(t, "test/foo/bar.txtz"), mockFile(t, "test/foo/bar.flob")},
4541
})
4642

4743
// Prefix matching
48-
m, err = create([]string{"test/*"})
44+
matchFn, err = factory([]string{"test/*"})
4945
as.NoError(err)
50-
testutil.MatcherTestResults(t, as, m, map[matcher.Result][]*walk.File{
51-
target: {f("test/bar.txt"), f("test/foo/bar.txt")},
52-
matcher.Indifferent: {f("/test/foo/bar.txt")},
46+
testutil.MatcherTestResults(t, as, matchFn, map[matcher.Result][]*walk.File{
47+
expected: {mockFile(t, "test/bar.txt"), mockFile(t, "test/foo/bar.txt")},
48+
matcher.Indifferent: {mockFile(t, "/test/foo/bar.txt")},
5349
})
5450

5551
// Exact matches
5652
// File extension
57-
m, err = create([]string{"LICENSE"})
53+
matchFn, err = factory([]string{"LICENSE"})
5854
as.NoError(err)
59-
testutil.MatcherTestResults(t, as, m, map[matcher.Result][]*walk.File{
60-
target: {f("LICENSE")},
61-
matcher.Indifferent: {f("test/LICENSE"), f("LICENSE.txt")},
55+
testutil.MatcherTestResults(t, as, matchFn, map[matcher.Result][]*walk.File{
56+
expected: {mockFile(t, "LICENSE")},
57+
matcher.Indifferent: {mockFile(t, "test/LICENSE"), mockFile(t, "LICENSE.txt")},
6258
})
6359
}
6460

65-
func TestIncludes(t *testing.T) {
66-
r := require.New(t)
67-
68-
testGlobMatcher(t, r, matcher.NewGlobInclusionMatcher)
61+
func TestIncludeGlobs(t *testing.T) {
62+
testGlobMatcher(t, matcher.IncludeGlobs, matcher.Wanted)
6963
}
7064

71-
func TestExcludes(t *testing.T) {
72-
r := require.New(t)
73-
74-
testGlobMatcher(t, r, matcher.NewGlobExclusionMatcher)
65+
func TestExcludeGlobs(t *testing.T) {
66+
testGlobMatcher(t, matcher.ExcludeGlobs, matcher.Unwanted)
7567
}

matcher/internal/testutil/testutil.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,17 @@ func MatcherTestSetup(t *testing.T, as *require.Assertions) func(string) *walk.F
3333
return testfile
3434
}
3535

36-
func MatcherTestEmpty(t *testing.T, as *require.Assertions, m matcher.Matcher) {
37-
t.Helper()
38-
39-
as.True(m.Ignore(), "matcher is not configured to be ignored")
40-
41-
result, err := matcher.Wants(m, &walk.File{RelPath: "ENOENT"})
42-
as.NoError(err)
43-
as.Equal(matcher.Indifferent, result)
44-
}
45-
4636
func MatcherTestResults(
4737
t *testing.T,
4838
as *require.Assertions,
49-
m matcher.Matcher,
39+
matchFn matcher.MatchFn,
5040
results map[matcher.Result][]*walk.File,
5141
) {
5242
t.Helper()
5343

54-
as.False(m.Ignore(), "matcher is configured to be ignored")
55-
5644
for expected, paths := range results {
5745
for _, path := range paths {
58-
actual, err := matcher.Wants(m, path)
46+
actual, err := matchFn(path)
5947
as.NoError(err)
6048
as.Equal(expected, actual, "expected %v for path %s; got %v", expected, path, actual)
6149
}

0 commit comments

Comments
 (0)