diff --git a/filepathx.go b/filepathx.go index 0129314..6f352d4 100644 --- a/filepathx.go +++ b/filepathx.go @@ -4,6 +4,7 @@ package filepathx import ( + "io/fs" "os" "path/filepath" "strings" @@ -22,6 +23,16 @@ func Glob(pattern string) ([]string, error) { return Globs(strings.Split(pattern, "**")).Expand() } +// GlobFS adds double-star support to the core path/filepath fs.Glob function. +// It's useful when your globs might have double-stars, but you're not sure. +func GlobFS(f fs.FS, pattern string) ([]string, error) { + if !strings.Contains(pattern, "**") { + // passthru to core package if no double-star + return filepath.Glob(pattern) + } + return Globs(strings.Split(pattern, "**")).ExpandFS(f) +} + // Expand finds matches for the provided Globs. func (globs Globs) Expand() ([]string, error) { var matches = []string{""} // accumulate here @@ -60,3 +71,50 @@ func (globs Globs) Expand() ([]string, error) { return matches, nil } + +func (globs Globs) ExpandFS(f fs.FS) ([]string, error) { + var matches = []string{""} // accumulate here + for _, glob := range globs { + var hits []string + var hitMap = map[string]bool{} + for _, match := range matches { + pattern := match + glob + // strip ending / + if strings.HasSuffix(pattern, "/") { + pattern = pattern[:len(pattern)-1] + } + if pattern == "" { + pattern = "*" + } + paths, err := fs.Glob(f, pattern) + if err != nil { + return nil, err + } + for _, path := range paths { + err = fs.WalkDir(f, path, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + // save deduped match from current iteration + if _, ok := hitMap[path]; !ok { + hits = append(hits, path) + hitMap[path] = true + } + return nil + }) + if err != nil { + return nil, err + } + } + } + matches = hits + } + + // fix up return value for nil input + if globs == nil && len(matches) > 0 && matches[0] == "" { + matches = matches[1:] + } + + return matches, nil + +}