Files
renamer/internal/regex/traversal.go
2025-10-31 10:12:02 +08:00

87 lines
1.7 KiB
Go

package regex
import (
"context"
"io/fs"
"path/filepath"
"strings"
"github.com/rogeecn/renamer/internal/traversal"
)
// Candidate represents a file or directory subject to regex evaluation.
type Candidate struct {
RelativePath string
OriginalPath string
BaseName string
Stem string
Extension string
IsDir bool
Depth int
}
// TraverseCandidates walks the working directory and invokes fn for each eligible candidate.
func TraverseCandidates(ctx context.Context, req *Request, fn func(Candidate) error) error {
if err := req.Validate(); err != nil {
return err
}
extensions := make(map[string]struct{}, len(req.Extensions))
for _, ext := range req.Extensions {
lower := strings.ToLower(ext)
extensions[lower] = struct{}{}
}
walker := traversal.NewWalker()
return walker.Walk(
req.WorkingDir,
req.Recursive,
req.IncludeDirectories,
req.IncludeHidden,
0,
func(relPath string, entry fs.DirEntry, depth int) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
isDir := entry.IsDir()
name := entry.Name()
stem := name
ext := ""
if !isDir {
if dot := strings.IndexRune(name, '.'); dot > 0 {
ext = name[dot:]
stem = name[:dot]
}
if len(extensions) > 0 {
lower := strings.ToLower(ext)
if _, ok := extensions[lower]; !ok {
return nil
}
}
}
rel := filepath.ToSlash(relPath)
if rel == "." {
return nil
}
candidate := Candidate{
RelativePath: rel,
OriginalPath: filepath.Join(req.WorkingDir, relPath),
BaseName: name,
Stem: stem,
Extension: ext,
IsDir: isDir,
Depth: depth,
}
return fn(candidate)
},
)
}