feat: rewrote glob to have a full doublestar impl
All checks were successful
default / ensure tests work (push) Successful in 35s
All checks were successful
default / ensure tests work (push) Successful in 35s
This implements a full double star glob implementation with it's own filesystem implementation.
This commit is contained in:
parent
3daeb3fc05
commit
24ad00274d
7 changed files with 1354 additions and 333 deletions
127
glob/fs.go
Normal file
127
glob/fs.go
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package glob
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type GlobFS struct {
|
||||
base fs.FS
|
||||
patterns []Pattern
|
||||
}
|
||||
|
||||
// NewGlobFS creates a new GlobFS that exposes only files matching any of the given glob patterns.
|
||||
func NewGlobFS(base fs.FS, patterns ...string) (*GlobFS, error) {
|
||||
fs := &GlobFS{base: base, patterns: []Pattern{}}
|
||||
|
||||
for _, value := range patterns {
|
||||
pattern, err := New(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs.patterns = append(fs.patterns, *pattern)
|
||||
}
|
||||
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
func (g *GlobFS) match(name string, prefix bool) bool {
|
||||
var f func(Pattern) bool
|
||||
if prefix {
|
||||
f = func(p Pattern) bool { return p.MatchPrefix(name) }
|
||||
} else {
|
||||
f = func(p Pattern) bool { return p.Match(name) }
|
||||
}
|
||||
return slices.ContainsFunc(g.patterns, f)
|
||||
}
|
||||
|
||||
func (g *GlobFS) contains(name string) (bool, error) {
|
||||
stat, err := fs.Stat(g.base, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if stat.IsDir() {
|
||||
contains := false
|
||||
err := fs.WalkDir(g.base, name, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() && !g.match(name+string(filepath.Separator), true) {
|
||||
return fs.SkipDir
|
||||
}
|
||||
|
||||
if g.match(path, false) {
|
||||
contains = true
|
||||
return fs.SkipAll
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return contains, err
|
||||
} else {
|
||||
return g.match(name, false), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GlobFS) Open(name string) (fs.File, error) {
|
||||
root := name == "."
|
||||
|
||||
// fast path some of the pattern matches
|
||||
if root || g.match(name, false) {
|
||||
return g.base.Open(name)
|
||||
}
|
||||
|
||||
ok, err := g.contains(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ok {
|
||||
return g.base.Open(name)
|
||||
} else {
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GlobFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
root := name == "."
|
||||
path := name + string(filepath.Separator)
|
||||
|
||||
// fast path no pattern matches (prefix check)
|
||||
// root dir ('.') must be handled to get initial entries
|
||||
if !root && !g.match(path, true) {
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
|
||||
entries, err := fs.ReadDir(g.base, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if we do not have any child entries, we need to check if the directory
|
||||
// itself matched some of the defined patterns, if so we should be able to
|
||||
// read it, otherwise we can not read it.
|
||||
if !root && len(entries) == 0 {
|
||||
if !g.match(path, false) {
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
}
|
||||
|
||||
children := []fs.DirEntry{}
|
||||
for _, entry := range entries {
|
||||
ok, err := g.contains(filepath.Join(name, entry.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ok {
|
||||
children = append(children, entry)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return children, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue