Skip to content

Commit 175e644

Browse files
feat: multiple curly braces for watcher (#2068)
Allows doing something like this: ```caddyfile watch "/app/{config,src}/*.{php,js}" ``` In the long term it would be nice to have pattern matching in the watcher repo itself
1 parent a8f75d0 commit 175e644

File tree

2 files changed

+34
-21
lines changed

2 files changed

+34
-21
lines changed

internal/watcher/pattern.go

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func (p *pattern) isValidPattern(fileName string) bool {
127127

128128
// if the pattern has size 1 we can match it directly against the filename
129129
if len(p.parsedValues) == 1 {
130-
return matchBracketPattern(p.parsedValues[0], fileNameWithoutDir)
130+
return matchCurlyBracePattern(p.parsedValues[0], fileNameWithoutDir)
131131
}
132132

133133
return p.matchPatterns(fileNameWithoutDir)
@@ -159,7 +159,7 @@ func (p *pattern) matchPatterns(fileName string) bool {
159159
cursor = j
160160
subPattern := strings.Join(partsToMatch[j:j+patternSize], string(filepath.Separator))
161161

162-
if matchBracketPattern(pattern, subPattern) {
162+
if matchCurlyBracePattern(pattern, subPattern) {
163163
cursor = j + patternSize - 1
164164

165165
break
@@ -174,29 +174,35 @@ func (p *pattern) matchPatterns(fileName string) bool {
174174
return true
175175
}
176176

177-
// we also check for the following bracket syntax: /path/*.{php,twig,yaml}
178-
func matchBracketPattern(pattern string, fileName string) bool {
179-
openingBracket := strings.Index(pattern, "{")
180-
closingBracket := strings.Index(pattern, "}")
177+
// we also check for the following syntax: /path/*.{php,twig,yaml}
178+
func matchCurlyBracePattern(pattern string, fileName string) bool {
179+
for _, subPattern := range expandCurlyBraces(pattern) {
180+
if matchPattern(subPattern, fileName) {
181+
return true
182+
}
183+
}
184+
185+
return false
186+
}
181187

182-
// if there are no brackets we can match regularly
183-
if openingBracket == -1 || closingBracket == -1 {
184-
return matchPattern(pattern, fileName)
188+
// {dir1,dir2}/path -> []string{"dir1/path", "dir2/path"}
189+
func expandCurlyBraces(s string) []string {
190+
before, rest, found := strings.Cut(s, "{")
191+
if !found {
192+
return []string{s}
185193
}
186194

187-
beforeTheBrackets := pattern[:openingBracket]
188-
betweenTheBrackets := pattern[openingBracket+1 : closingBracket]
189-
afterTheBrackets := pattern[closingBracket+1:]
195+
inside, after, found := strings.Cut(rest, "}")
196+
if !found {
197+
return []string{s} // no closing brace
198+
}
190199

191-
// all bracket entries are checked individually, only one needs to match
192-
// *.{php,twig,yaml} -> *.php, *.twig, *.yaml
193-
for pattern := range strings.SplitSeq(betweenTheBrackets, ",") {
194-
if matchPattern(beforeTheBrackets+pattern+afterTheBrackets, fileName) {
195-
return true
196-
}
200+
var out []string
201+
for _, subPattern := range strings.Split(inside, ",") {
202+
out = append(out, expandCurlyBraces(before+subPattern+after)...)
197203
}
198204

199-
return false
205+
return out
200206
}
201207

202208
func matchPattern(pattern string, fileName string) bool {

internal/watcher/pattern_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ func TestInvalidDirectoryPatterns(t *testing.T) {
259259
}
260260
}
261261

262-
func TestValidExtendedPatterns(t *testing.T) {
262+
func TestValidCurlyBracePatterns(t *testing.T) {
263263
data := []struct {
264264
pattern string
265265
dir string
@@ -272,6 +272,10 @@ func TestValidExtendedPatterns(t *testing.T) {
272272
{"/path/{dir1,dir2}/file.php", "/path/dir2/file.php"},
273273
{"/app/{app,config,resources}/**/*.php", "/app/app/subpath/file.php"},
274274
{"/app/{app,config,resources}/**/*.php", "/app/config/subpath/file.php"},
275+
{"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir1/ab.php"},
276+
{"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir2/aa.php"},
277+
{"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir2/bb.php"},
278+
{"/path/{dir1/test.php,dir2/test.php}", "/path/dir1/test.php"},
275279
}
276280

277281
for _, d := range data {
@@ -283,7 +287,7 @@ func TestValidExtendedPatterns(t *testing.T) {
283287
}
284288
}
285289

286-
func TestInvalidExtendedPatterns(t *testing.T) {
290+
func TestInvalidCurlyBracePatterns(t *testing.T) {
287291
data := []struct {
288292
pattern string
289293
dir string
@@ -293,6 +297,9 @@ func TestInvalidExtendedPatterns(t *testing.T) {
293297
{"/path/{file.php,file.twig}", "/path/file.txt"},
294298
{"/path/{dir1,dir2}/file.php", "/path/dir3/file.php"},
295299
{"/path/{dir1,dir2}/**/*.php", "/path/dir1/subpath/file.txt"},
300+
{"/path/{dir1,dir2}/{a,b}{a,b}.php", "/path/dir1/ac.php"},
301+
{"/path/{}/{a,b}{a,b}.php", "/path/dir1/ac.php"},
302+
{"/path/}dir{/{a,b}{a,b}.php", "/path/dir1/aa.php"},
296303
}
297304

298305
for _, d := range data {

0 commit comments

Comments
 (0)