forked from redpanda-data/connect
-
Notifications
You must be signed in to change notification settings - Fork 0
/
glob.go
144 lines (131 loc) · 3.5 KB
/
glob.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package filepath
import (
"errors"
"io/fs"
"runtime"
"strings"
)
// GlobsAndSuperPaths attempts to expand a list of paths, which may include glob
// patterns and super paths (the ... thing) to a list of explicit file paths.
// Extensions must be provided, and limit the file types that are captured with
// a super path.
func GlobsAndSuperPaths(f fs.FS, paths []string, extensions ...string) ([]string, error) {
if len(extensions) == 0 {
return nil, errors.New("must specify at least one extension for super paths")
}
var superPaths, skippedPaths []string
for _, p := range paths {
if strings.HasSuffix(p, "...") {
if p == "./..." || p == "..." {
p = "."
} else {
p = strings.TrimSuffix(p, "/...")
}
if err := fs.WalkDir(f, p, func(path string, info fs.DirEntry, werr error) error {
if werr != nil {
return werr
}
if info.IsDir() {
return nil
}
for _, ext := range extensions {
if strings.HasSuffix(path, ext) {
superPaths = append(superPaths, path)
return nil
}
}
return nil
}); err != nil {
return nil, err
}
} else {
skippedPaths = append(skippedPaths, p)
}
}
resultPaths := append([]string{}, superPaths...)
if len(skippedPaths) > 0 {
globPaths, err := Globs(f, skippedPaths)
if err != nil {
return nil, err
}
resultPaths = append(resultPaths, globPaths...)
}
return resultPaths, nil
}
// hasMeta reports whether path contains any of the magic characters
// recognized by Match.
//
// Taken from path/filepath/match.go.
func hasMeta(path string) bool {
magicChars := `*?[`
if runtime.GOOS != "windows" {
magicChars = `*?[\`
}
return strings.ContainsAny(path, magicChars)
}
// Globs attempts to expand a list of paths, which may include glob patterns, to
// a list of explicit file paths. The paths are de-duplicated but are not
// sorted.
func Globs(f fs.FS, paths []string) ([]string, error) {
var expandedPaths []string
seenPaths := map[string]struct{}{}
for _, path := range paths {
var globbed []string
var err error
if segments := strings.Split(path, "**"); len(segments) == 1 {
globbed, err = fs.Glob(f, path)
} else {
globbed, err = superGlobs(f, segments)
}
if err != nil {
return nil, err
}
for _, gPath := range globbed {
if _, seen := seenPaths[gPath]; !seen {
expandedPaths = append(expandedPaths, gPath)
seenPaths[gPath] = struct{}{}
}
}
if len(globbed) == 0 && !hasMeta(path) {
if _, seen := seenPaths[path]; !seen {
expandedPaths = append(expandedPaths, path)
seenPaths[path] = struct{}{}
}
}
}
return expandedPaths, nil
}
// Inspired by https://github.com/yargevad/filepathx/blob/master/filepathx.go
func superGlobs(f fs.FS, segments []string) ([]string, error) {
matches := map[string]struct{}{"": {}}
for i, segment := range segments {
newMatches := map[string]struct{}{}
lastSegment := (len(segments) - 1) == i
for match := range matches {
paths, err := fs.Glob(f, match+segment)
if err != nil {
return nil, err
}
for _, path := range paths {
if err := fs.WalkDir(f, path, func(newPath string, info fs.DirEntry, err error) error {
if err != nil {
return err
}
if lastSegment && info.IsDir() {
return nil
}
newMatches[newPath] = struct{}{}
return nil
}); err != nil {
return nil, err
}
}
}
matches = newMatches
}
matchSlice := make([]string, 0, len(matches))
for path := range matches {
matchSlice = append(matchSlice, path)
}
return matchSlice, nil
}