99 lines
1.9 KiB
Go
99 lines
1.9 KiB
Go
|
|
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
|
||
|
|
}
|