fs.go
package utils
import (
"os"
"path/filepath"
"sort"
"strings"
)
// FindFilesOptions configures directory scanning
type FindFilesOptions struct {
Extension string // e.g. ".wav" or ".data"
Recursive bool // whether to walk subdirectories
SkipPrefixes []string // directory prefixes to skip (e.g. "Clips_")
SkipHidden bool // skip files/folders starting with "."
MinSize int64 // minimum file size in bytes
}
// FindFiles scans a directory for files matching the given options
func FindFiles(rootPath string, opts FindFilesOptions) ([]string, error) {
var results []string
if opts.Recursive {
if err := filepath.Walk(rootPath, makeWalkFunc(&results, opts, rootPath)); err != nil {
return nil, err
}
} else {
if err := scanFlatDir(rootPath, &results, opts); err != nil {
return nil, err
}
}
sort.Strings(results)
return results, nil
}
// shouldSkipHidden checks if a path should be skipped due to hidden file rules.
func shouldSkipHidden(name, path, rootPath string, info os.FileInfo, skipHidden bool) (bool, error) {
if !skipHidden || !strings.HasPrefix(name, ".") || path == rootPath {
return false, nil
}
if info.IsDir() {
return true, filepath.SkipDir
}
return true, nil
}
// shouldSkipDir checks if a directory should be skipped based on prefixes.
func shouldSkipDir(name, path, rootPath string, skipPrefixes []string) bool {
if path == rootPath {
return false
}
for _, prefix := range skipPrefixes {
if strings.HasPrefix(name, prefix) {
return true
}
}
return false
}
// makeWalkFunc returns a filepath.WalkFunc that collects matching files.
func makeWalkFunc(results *[]string, opts FindFilesOptions, rootPath string) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
name := info.Name()
// Skip hidden files/directories
if skip, skipErr := shouldSkipHidden(name, path, rootPath, info, opts.SkipHidden); skip {
return skipErr
}
// Check directory skip prefixes
if info.IsDir() {
if shouldSkipDir(name, path, rootPath, opts.SkipPrefixes) {
return filepath.SkipDir
}
return nil
}
// Check file
if fileMatches(name, info, opts) {
*results = append(*results, path)
}
return nil
}
}
// fileMatches checks if a file name meets the extension and size criteria.
func fileMatches(name string, info os.FileInfo, opts FindFilesOptions) bool {
if !strings.EqualFold(filepath.Ext(name), opts.Extension) {
return false
}
if info.Size() < opts.MinSize {
return false
}
return true
}
// scanFlatDir scans a single directory level for matching files.
func scanFlatDir(rootPath string, results *[]string, opts FindFilesOptions) error {
entries, err := os.ReadDir(rootPath)
if err != nil {
return err
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if opts.SkipHidden && strings.HasPrefix(name, ".") {
continue
}
if !strings.EqualFold(filepath.Ext(name), opts.Extension) {
continue
}
path := filepath.Join(rootPath, name)
if info, err := os.Stat(path); err == nil && info.Size() >= opts.MinSize {
*results = append(*results, path)
}
}
return nil
}