Skip to content

Refactor DirectoryInfoExtensions.FindFiles to Reduce Cognitive Complexity #570

@ziagham

Description

@ziagham

What version of FlowSynx?

1.2.1

Description

The FindFiles method currently exceeds the allowed Cognitive Complexity limit (16 > 15). This issue aims to refactor the method to improve readability, maintainability, and adherence to code quality standards.

File: /plugins/FlowSynx.Plugins.LocalFileSystem/Extensions/DirectoryInfoExtensions.cs
Method: FindFiles

Current Implementation

internal static class DirectoryInfoExtensions
{
    public static IEnumerable<FileInfo> FindFiles(this DirectoryInfo directoryInfo, IPluginLogger logger, ListParameters listParameters)
    {
        if (directoryInfo == null)
            throw new ArgumentNullException(nameof(directoryInfo), Resources.TheDirectoryCouldNotBeNull);

        if (!directoryInfo.Exists)
            throw new DirectoryNotFoundException(string.Format(Resources.TheDirectoryDoesNotExist, directoryInfo.FullName));

        var searchOption = listParameters.Recurse is true ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
        var files = directoryInfo.EnumerateFiles("*", searchOption);

        var result = new List<FileInfo>();
        Regex? regex = null;
        if (!string.IsNullOrEmpty(listParameters.Filter))
        {
            var regexOptions = listParameters.CaseSensitive is true ? RegexOptions.IgnoreCase : RegexOptions.None;
            regex = new Regex(listParameters.Filter, regexOptions);
        }

        foreach (var file in files)
        {
            try 
            {
                var resultCount = 0;
                var isMatched = regex != null && regex.IsMatch(file.FullName);

                if (listParameters.Filter != null && !isMatched) 
                    continue;

                result.Add(file);
                resultCount++;

                if (listParameters.MaxResults.HasValue && resultCount >= listParameters.MaxResults)
                {
                    break;
                }
            }
            catch (Exception ex) 
            {
                logger.LogError(ex.Message);
            }
        }

        return result;
    }
}

Suggested Refactoring Ideas

  • Extract smaller helper methods:
    • ValidateDirectory(DirectoryInfo directoryInfo)
    • CreateRegex(ListParameters listParameters)
    • ShouldIncludeFile(FileInfo file, Regex? regex, ListParameters listParameters)
  • Move resultCount outside the loop to avoid redundant initialization.
  • Simplify nested logic using early continue or extracted conditions.

Proposed Implementation

internal static class DirectoryInfoExtensions
{
    public static IEnumerable<FileInfo> FindFiles(this DirectoryInfo directoryInfo, IPluginLogger logger, ListParameters listParameters)
    {
        ValidateDirectory(directoryInfo);

        var searchOption = listParameters.Recurse == true 
            ? SearchOption.AllDirectories 
            : SearchOption.TopDirectoryOnly;

        var regex = CreateRegex(listParameters);
        var files = directoryInfo.EnumerateFiles("*", searchOption);
        var result = new List<FileInfo>();
        int resultCount = 0;

        foreach (var file in files)
        {
            if (!ShouldIncludeFile(file, regex, listParameters))
                continue;

            try
            {
                result.Add(file);
                resultCount++;

                if (listParameters.MaxResults.HasValue && resultCount >= listParameters.MaxResults)
                    break;
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
            }
        }

        return result;
    }

    private static void ValidateDirectory(DirectoryInfo directoryInfo)
    {
        if (directoryInfo == null)
            throw new ArgumentNullException(nameof(directoryInfo), Resources.TheDirectoryCouldNotBeNull);

        if (!directoryInfo.Exists)
            throw new DirectoryNotFoundException(string.Format(Resources.TheDirectoryDoesNotExist, directoryInfo.FullName));
    }

    private static Regex? CreateRegex(ListParameters listParameters)
    {
        if (string.IsNullOrEmpty(listParameters.Filter))
            return null;

        var regexOptions = listParameters.CaseSensitive == true 
            ? RegexOptions.None 
            : RegexOptions.IgnoreCase;

        return new Regex(listParameters.Filter, regexOptions);
    }

    private static bool ShouldIncludeFile(FileInfo file, Regex? regex, ListParameters listParameters)
    {
        if (regex == null)
            return true;

        return regex.IsMatch(file.FullName);
    }
}

Acceptance Criteria

  • Cognitive Complexity ≤ 15
  • Code passes existing unit tests (no behavioral changes)
  • Maintains:
    • Exception handling
    • Logging
    • Filtering and recursion logic
    • MaxResults functionality

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions