Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions file_handling.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
type File struct {
Path string
info os.FileInfo
isDir *bool
}

// NewFile resolves path to an absolute path and wraps it in a *File. It
Expand All @@ -27,6 +28,16 @@ func NewFile(path string) (*File, error) {
return &File{Path: absPath}, nil
}

// NewChildFile joins name under an already-absolute parent without calling filepath.Abs again.
func NewChildFile(parent *File, name string, entry os.DirEntry) *File {
child := &File{Path: filepath.Join(parent.Path, name)}
if entry != nil {
isDir := entry.IsDir()
child.isDir = &isDir
}
return child
}

func (f *File) Base() string {
return filepath.Base(f.Path)
}
Expand All @@ -35,6 +46,18 @@ func (f *File) Dir() string {
return filepath.Dir(f.Path)
}

// IsDir reports whether f is a directory, using DirEntry metadata when available.
func (f *File) IsDir() bool {
if f.isDir != nil {
return *f.isDir
}
info, err := f.Info()
if err != nil {
return false
}
return info.IsDir()
}

// Info lazily stats the file and caches the result. It returns an error if
// the underlying os.Stat fails.
func (f *File) Info() (os.FileInfo, error) {
Expand Down
14 changes: 14 additions & 0 deletions file_handling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ import (
)

// TestNewFile exercises NewFile's path-resolution behavior.

func TestNewChildFile(t *testing.T) {
tmp := t.TempDir()
parent, err := NewFile(tmp)
if err != nil {
t.Fatal(err)
}
child := NewChildFile(parent, "sub", nil)
want := filepath.Join(parent.Path, "sub")
if child.Path != want {
t.Fatalf("NewChildFile path = %q; want %q", child.Path, want)
}
}

func TestNewFile(t *testing.T) {
tmp := t.TempDir()

Expand Down
17 changes: 3 additions & 14 deletions find_replace.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,8 @@ func (fr *findReplace) WalkDir(f *File) {
return
}

for _, file := range files {
childPath := filepath.Join(f.Path, file.Name())
childFile, err := NewFile(childPath)
if err != nil {
log.Print(err)
fr.errs.add(err)
continue
}
for _, entry := range files {
childFile := NewChildFile(f, entry.Name(), entry)
wg.Add(1)
go func() {
defer wg.Done()
Expand All @@ -151,13 +145,8 @@ func (fr *findReplace) WalkDir(f *File) {
// the rename step; the failure is returned so the walker can log it and
// continue with siblings.
func (fr *findReplace) HandleFile(f *File) error {
info, err := f.Info()
if err != nil {
return err
}

// If file is a directory, recurse immediately (depth-first).
if info.IsDir() {
if f.IsDir() {
// Ignore certain directories
if f.Base() == ".git" {
return nil
Expand Down