aspnet/FileSystem

Searching

dazinator opened this issue · 1 comments

It would be nice to be able to Search for a bunch of IFileInfo's using a pattern.

I have implemented this as an extension method - would you consider adding something like this in future?


   // ReSharper disable once CheckNamespace
    // Extension method put in root namespace for discoverability purposes.
    public static class IFileProviderExtensions
    {

         /// <summary>
        /// returns all items in the directory that match the specified glob pattern.
        /// </summary>
        /// <param name="globPattern"></param>
        /// <returns></returns>
        public static IEnumerable<IFileInfo> Search(this IFileProvider fileProvider, string globPattern)
        {
            var results = new GlobMatchingEnumerableFileInfos(fileProvider, globPattern);
            return results;
        }

}


 public class GlobMatchingEnumerableFileInfos : IEnumerable<IFileInfo>
    {
        private readonly IFileProvider _fileProvider;
        private readonly string _pattern;
     
        public GlobMatchingEnumerableFileInfos(IFileProvider fileProvider, string pattern)
        {
            _fileProvider = fileProvider;
            _pattern = pattern;
        }

        public IEnumerator<IFileInfo> GetEnumerator()
        {

            var folders = new Stack<KeyValuePair<string, IFileInfo>>();
            var glob = new Glob(_pattern); // this is an open source glob matcher I am using. You will no doubt have your own glob pattern matching implementation...

            var folderPath = "";
            var currentFolder = _fileProvider.GetDirectoryContents("");
            
            while (currentFolder != null)
            {               
                foreach (var item in currentFolder)
                {
                    var itemPath = $"{folderPath}/{item.Name}";
                    var isMatch = glob.IsMatch(itemPath);
                    if (isMatch)
                    {
                        yield return item;
                    }

                    if (item.IsDirectory)
                    {
                        // add the nested folder to a queue for later processing its items in the loop.
                        folders.Push(new KeyValuePair<string, IFileInfo>(itemPath, item));
                        continue;
                    }

                }

                if (folders.Count > 0)
                {
                    var folderItem = folders.Pop();
                    folderPath = folderItem.Key;
                    currentFolder = _fileProvider.GetDirectoryContents(folderPath);
                }
                else
                {
                    // finished iteating entire directory.
                    currentFolder = null;
                }

            }

        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }


    }

Then usage is like:

IFileInfo[] results = fileProvider.Search("*.js").ToArray();

This type of cuntionality is useful for instance when watching using a pattern.
For example if watching "/js/*.js" - when that change tolen is signalled, there is no way to know which particular file caused the token to be signalled. Therefore you have to re-query the file provider to get all the files that match the pattern. This is where a Search("pattern") function would come in handy.

This issue was moved to dotnet/aspnetcore#2545