Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

small optimization for fsfinder #136

Merged
merged 13 commits into from
Aug 3, 2024
14 changes: 12 additions & 2 deletions pkg/finder/finder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,7 @@ func Test_FileSystemFinderMultipleFinder(t *testing.T) {
func Test_FileSystemFinderDuplicateFiles(t *testing.T) {
fsFinder := FileSystemFinderInit(
WithPathRoots(
"../../test/fixtures/subdir/good.json",
"../../test/fixtures/subdir/",
"../../test/fixtures/subdir/../subdir/good.json",
),
)

Expand Down Expand Up @@ -252,3 +250,15 @@ func Test_FileFinderBadPath(t *testing.T) {
t.Errorf("Error should be thrown for bad path")
}
}

func Benchmark_Finder(b *testing.B) {
fsFinder := FileSystemFinderInit(
WithPathRoots("../../test/fixtures/"),
)

b.ResetTimer()

for i := 0; i < b.N; i++ {
_, _ = fsFinder.Find()
}
}
56 changes: 30 additions & 26 deletions pkg/finder/fsfinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ func WithDepth(depthVal int) FSFinderOptions {
}
func FileSystemFinderInit(opts ...FSFinderOptions) *FileSystemFinder {
defaultExcludeDirs := make(map[string]struct{})
defaultExcludeFileTypes := make(map[string]struct{})
defaultPathRoots := []string{"."}

fsfinder := &FileSystemFinder{
PathRoots: defaultPathRoots,
FileTypes: filetype.FileTypes,
ExcludeDirs: defaultExcludeDirs,
PathRoots: defaultPathRoots,
FileTypes: filetype.FileTypes,
ExcludeDirs: defaultExcludeDirs,
ExcludeFileTypes: defaultExcludeFileTypes,
}

for _, opt := range opts {
Expand All @@ -78,29 +80,19 @@ func (fsf FileSystemFinder) Find() ([]FileMetadata, error) {
seen := make(map[string]struct{}, 0)
uniqueMatches := make([]FileMetadata, 0)
for _, pathRoot := range fsf.PathRoots {
matches, err := fsf.findOne(pathRoot)
matches, err := fsf.findOne(pathRoot, seen)
if err != nil {
return nil, err
}
for _, match := range matches {
absPath, err := filepath.Abs(match.Path)
if err != nil {
return nil, err
}
if _, ok := seen[absPath]; ok {
continue
}
uniqueMatches = append(uniqueMatches, match)
seen[absPath] = struct{}{}
}
uniqueMatches = append(uniqueMatches, matches...)
}
return uniqueMatches, nil
}

// findOne recursively walks through all subdirectories (excluding the excluded subdirectories)
// and identifying if the file matches a type defined in the fileTypes array for a
// single path and returns the file metadata.
func (fsf FileSystemFinder) findOne(pathRoot string) ([]FileMetadata, error) {
func (fsf FileSystemFinder) findOne(pathRoot string, seenMap map[string]struct{}) ([]FileMetadata, error) {
var matchingFiles []FileMetadata

// check that the path exists before walking it or the error returned
Expand All @@ -119,28 +111,40 @@ func (fsf FileSystemFinder) findOne(pathRoot string) ([]FileMetadata, error) {
err := filepath.WalkDir(pathRoot,
func(path string, dirEntry fs.DirEntry, err error) error {
// determine if directory is in the excludeDirs list or if the depth is greater than the maxDepth
_, isExcluded := fsf.ExcludeDirs[dirEntry.Name()]
if dirEntry.IsDir() && ((fsf.Depth != nil && strings.Count(path, string(os.PathSeparator)) > maxDepth) || isExcluded) {
return filepath.SkipDir
if dirEntry.IsDir() {
_, isExcluded := fsf.ExcludeDirs[dirEntry.Name()]
if isExcluded || (fsf.Depth != nil && strings.Count(path, string(os.PathSeparator)) > maxDepth) {
return filepath.SkipDir
}
}

if !dirEntry.IsDir() {
// filepath.Ext() returns the extension name with a dot so it
// needs to be removed.

walkFileExtension := strings.TrimPrefix(filepath.Ext(path), ".")
extensionLowerCase := strings.ToLower(walkFileExtension)

if _, ok := fsf.ExcludeFileTypes[walkFileExtension]; ok {
if _, isExcluded := fsf.ExcludeFileTypes[extensionLowerCase]; isExcluded {
return nil
}
extensionLowerCase := strings.ToLower(walkFileExtension)

for _, fileType := range fsf.FileTypes {
if _, ok := fileType.Extensions[extensionLowerCase]; ok {
fileMetadata := FileMetadata{dirEntry.Name(), path, fileType}
matchingFiles = append(matchingFiles, fileMetadata)
break
if _, isMatched := fileType.Extensions[extensionLowerCase]; isMatched {
absPath, err := filepath.Abs(path)
if err != nil {
return err
}

if _, seen := seenMap[absPath]; !seen {
fileMetadata := FileMetadata{dirEntry.Name(), absPath, fileType}
matchingFiles = append(matchingFiles, fileMetadata)
seenMap[absPath] = struct{}{}
}

return nil
}
}
fsf.ExcludeFileTypes[extensionLowerCase] = struct{}{}
}

return nil
Expand Down
Loading