internal static IEnumerable <FileSystemInfo> Traverse(DirectoryInfo root, Segment[] segments, int segmentIndex, TraverseOptions options) { if (segmentIndex == segments.Length) { return(options.EmitDirectories ? Enumerable.Repeat <FileSystemInfo>(root, 1) : emptyFileSystemInfoArray); } var segment = segments[segmentIndex]; switch (segment) { case DirectorySegment directorySegment: { var filesToEmit = (options.EmitFiles && segmentIndex == segments.Length - 1) ? options.GetFiles(root).Where(file => directorySegment.MatchesSegment(file.Name, options.CaseSensitive)).Cast <FileSystemInfo>() : emptyFileSystemInfoArray; var dirSegmentItems = from directory in options.GetDirectories(root) where directorySegment.MatchesSegment(directory.Name, options.CaseSensitive) from item in Traverse(directory, segments, segmentIndex + 1, options) select item; return(filesToEmit.Concat(dirSegmentItems)); } case DirectoryWildcard _: { var filesToEmit = (options.EmitFiles && segmentIndex == segments.Length - 1) ? options.GetFiles(root).Cast <FileSystemInfo>() : emptyFileSystemInfoArray; // match zero path segments, consuming DirectoryWildcard var zeroMatch = Traverse(root, segments, segmentIndex + 1, options); // match consume 1 path segment but not the Wildcard var files = from directory in options.GetDirectories(root) from item in Traverse(directory, segments, segmentIndex, options) select item; return(filesToEmit.Concat(zeroMatch).Concat(files)); } default: return(emptyFileSystemInfoArray); } }
public IEnumerator <FileSystemInfo> GetEnumerator() { var roots = new List <DirectoryInfo>(); var rootCache = new List <DirectoryInfo>(); roots.AddRange(_originalRoots); var segmentsLength = _segments.Length; var nextSegmentRoots = new List <DirectoryInfo>(); var segmentIndex = 0; var emitDirectories = _options.EmitDirectories; var emitFiles = _options.EmitFiles; var cache = new HashSet <string>(); void Swap(ref List <DirectoryInfo> other) { var swap = roots; roots = other; other = swap; } while (true) { // no more segments. return all current roots var noMoreSegments = segmentIndex == segmentsLength; if (emitDirectories && noMoreSegments) { foreach (var info in roots.Where(info => !cache.Contains(info.FullName))) { cache.Add(info.FullName); yield return(info); } } // no more roots or no more segments, go to next segment if (roots.Count == 0 || noMoreSegments) { roots.Clear(); if (nextSegmentRoots.Count > 0) { Swap(ref nextSegmentRoots); segmentIndex++; continue; } yield break; } var segment = _segments[segmentIndex]; var onLastSegment = segmentIndex == segmentsLength - 1; if (emitFiles && onLastSegment) { var allFiles = from job in roots let children = _options.GetFiles(job) from file in FilesMatchingSegment(children, segment, _options.CaseSensitive) select file; foreach (var info in allFiles) { if (!cache.Contains(info.FullName)) { cache.Add(info.FullName); yield return(info); } } } rootCache.Clear(); rootCache.AddRange(roots.SelectMany(job => JobsMatchingSegment(nextSegmentRoots, job, segment))); Swap(ref rootCache); } }