diff --git a/utils/file_utils.go b/utils/file_utils.go index bbad8db..540027d 100644 --- a/utils/file_utils.go +++ b/utils/file_utils.go @@ -14,6 +14,8 @@ package utils import ( + "errors" + "fmt" "io/fs" "os" "path/filepath" @@ -30,14 +32,20 @@ func NewFileDirReader(fileDir string, recursive bool, maxDepth int) (*StringArra var filePaths []string rootDepth := pathDepth(fileDir) + var errs []error + // filePaths is safely appended within WalkDir because WalkDir executes the callback sequentially. // No race conditions occur in this implementation, even with slice reallocation. err := filepath.WalkDir(fileDir, func(path string, d fs.DirEntry, err error) error { if err != nil { - return err + errs = append(errs, fmt.Errorf("%s: %w", path, err)) + if d != nil && d.IsDir() { + return fs.SkipDir + } + return nil } - if !d.IsDir() { + if d != nil && !d.IsDir() { filePaths = append(filePaths, path) return nil } @@ -54,9 +62,10 @@ func NewFileDirReader(fileDir string, recursive bool, maxDepth int) (*StringArra }) if err != nil { - return nil, err + errs = append(errs, err) } - return &StringArrayReader{strings: filePaths}, nil + + return &StringArrayReader{strings: filePaths}, errors.Join(errs...) } // pathDepth returns the depth of a given path by counting its components. diff --git a/utils/file_utils_test.go b/utils/file_utils_test.go index 7ea8826..3313d48 100644 --- a/utils/file_utils_test.go +++ b/utils/file_utils_test.go @@ -169,22 +169,53 @@ func Test_NewFileDirReader_Error(t *testing.T) { t.Parallel() rootDir := t.TempDir() - noPerm := os.FileMode(0000) - if err := os.WriteFile(filepath.Join(rootDir, "a.txt"), []byte("hello world!"), noPerm); err != nil { + rwxPerm := os.FileMode(0755) + if err := os.WriteFile(filepath.Join(rootDir, "a.txt"), []byte("hello world!"), rwxPerm); err != nil { + t.Fatalf("unexpected error while WriteFile %v", err) + } + if err := os.WriteFile(filepath.Join(rootDir, "z.txt"), []byte("hello world!"), rwxPerm); err != nil { t.Fatalf("unexpected error while WriteFile %v", err) } path := filepath.Join(rootDir, "sub") + noPerm := os.FileMode(0000) if err := os.Mkdir(path, noPerm); err != nil { t.Fatalf("unexpected error while Mkdir %v", err) } - _, err := NewFileDirReader(rootDir, false, 10) + + path = filepath.Join(rootDir, "sub_2") + if err := os.Mkdir(path, rwxPerm); err != nil { + t.Fatalf("unexpected error while Mkdir %v", err) + } + + if err := os.WriteFile(filepath.Join(path, "www.txt"), []byte("hello world!"), rwxPerm); err != nil { + t.Fatalf("unexpected error while WriteFile %v", err) + } + + sr, err := NewFileDirReader(rootDir, false, 10) if err != nil { t.Errorf("unexpected error while NewFileDirReader err:%v", err) } - _, err = NewFileDirReader(rootDir, true, 10) + + sr, err = NewFileDirReader(rootDir, true, 10) if !strings.Contains(err.Error(), "permission denied") { t.Errorf("unexpected error permissions denied message got:%v", err.Error()) } + + if diff := cmp.Diff( + &StringArrayReader{ + strings: []string{ + filepath.Join(rootDir, "a.txt"), + // note we are ignoring, and not getting back sub permission denied directory + filepath.Join(rootDir, "sub_2/www.txt"), + filepath.Join(rootDir, "z.txt"), + }, + }, + sr, + cmp.AllowUnexported(StringArrayReader{}), + ); diff != "" { + t.Errorf("unexpected StringArrayReader mismatch (-want +got):\n%s", diff) + } + } func Test_pathDepth(t *testing.T) {