feta: add globfs
Add a fs.FS impl to allow to filter an existing fs.FS by path patterns.
This commit is contained in:
parent
285a7e8be5
commit
3daeb3fc05
2 changed files with 333 additions and 0 deletions
99
iox/globfs.go
Normal file
99
iox/globfs.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package iox
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
type GlobFS struct {
|
||||
base fs.FS
|
||||
patterns []string
|
||||
}
|
||||
|
||||
// NewGlobFS creates a new GlobFS that exposes only files matching any of the given glob patterns.
|
||||
func NewGlobFS(base fs.FS, patterns ...string) *GlobFS {
|
||||
return &GlobFS{base: base, patterns: patterns}
|
||||
}
|
||||
|
||||
// match reports whether the given path matches any of the configured patterns.
|
||||
func (g *GlobFS) match(name string) bool {
|
||||
for _, pat := range g.patterns {
|
||||
if matched, _ := path.Match(pat, name); matched {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GlobFS) contains(dir string) bool {
|
||||
queue := []string{dir}
|
||||
visited := make(map[string]struct{})
|
||||
for len(queue) > 0 {
|
||||
current := queue[0]
|
||||
queue = queue[1:] // dequeue
|
||||
|
||||
// Prevent visiting same dir multiple times
|
||||
if _, seen := visited[current]; seen {
|
||||
continue
|
||||
}
|
||||
visited[current] = struct{}{}
|
||||
|
||||
entries, err := fs.ReadDir(g.base, current)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, entry := range entries {
|
||||
rel := path.Join(current, entry.Name())
|
||||
if g.match(rel) {
|
||||
return true
|
||||
}
|
||||
if entry.IsDir() {
|
||||
queue = append(queue, rel)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GlobFS) Open(name string) (fs.File, error) {
|
||||
if g.match(name) {
|
||||
return g.base.Open(name)
|
||||
}
|
||||
|
||||
fi, err := fs.Stat(g.base, name)
|
||||
if err != nil || !fi.IsDir() {
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
if g.contains(name) {
|
||||
return g.base.Open(name)
|
||||
}
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
|
||||
func (g *GlobFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
if g.match(name) {
|
||||
return fs.ReadDir(g.base, name)
|
||||
}
|
||||
|
||||
entries, err := fs.ReadDir(g.base, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var children []fs.DirEntry
|
||||
for _, entry := range entries {
|
||||
rel := path.Join(name, entry.Name())
|
||||
if g.match(rel) {
|
||||
children = append(children, entry)
|
||||
}
|
||||
if entry.IsDir() && g.contains(rel) {
|
||||
children = append(children, entry)
|
||||
}
|
||||
}
|
||||
|
||||
if len(children) == 0 {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
return children, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue