File: FileManagerProtocol%2BFilesSequence.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (75 lines) | stat: -rw-r--r-- 3,546 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2024 Apple Inc. and the Swift project authors
 Licensed under Apache License v2.0 with Runtime Library Exception

 See https://swift.org/LICENSE.txt for license information
 See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

package import Foundation

extension FileManagerProtocol {
    /// Returns a sequence of all the files in the directory structure from the starting point.
    /// - Parameters:
    ///   - startingPoint: The file or directory that's the top of the directory structure that the file manager traverses.
    ///   - options: Options for how the file manager enumerates the contents of directories. Defaults to `.skipsHiddenFiles`.
    /// - Returns: A sequence of the files in the directory structure.
    package func recursiveFiles(startingPoint: URL, options: FileManager.DirectoryEnumerationOptions = .skipsHiddenFiles) -> IteratorSequence<_FilesIterator> {
        IteratorSequence(_FilesIterator(fileManager: self, startingPoint: startingPoint, options: options))
    }
}

// FIXME: This should be private and `FileManagerProtocol.recursiveFiles(startingPoint:options:)` should return `some Sequence<ULR>`
// but because of https://github.com/swiftlang/swift/issues/77955 it needs to be exposed as an explicit type to avoid a SIL Validation error in the Swift compiler.

/// An iterator that traverses the directory structure and returns the files in breadth-first order.
package struct _FilesIterator: IteratorProtocol {
    /// The file manager that the iterator uses to traverse the directory structure.
    private var fileManager: any FileManagerProtocol // This can't be a generic because of https://github.com/swiftlang/swift/issues/77955
    private var options: FileManager.DirectoryEnumerationOptions
    
    private var foundFiles: [URL]
    private var foundDirectories: [URL]
    
    fileprivate init(fileManager: any FileManagerProtocol, startingPoint: URL, options: FileManager.DirectoryEnumerationOptions) {
        self.fileManager = fileManager
        self.options = options
        
        // Check if the starting point is a file or a directory.
        if fileManager.directoryExists(atPath: startingPoint.path) {
            foundFiles       = []
            foundDirectories = [startingPoint]
        } else {
            foundFiles       = [startingPoint]
            foundDirectories = []
        }
    }
    
    package mutating func next() -> URL? {
        // If the iterator has already found some files, return those first
        if !foundFiles.isEmpty {
            return foundFiles.removeFirst()
        }
        
        // Otherwise, check the next found directory and add its contents
        guard !foundDirectories.isEmpty else {
            // Traversed the entire directory structure
            return nil
        }
        
        let directory = foundDirectories.removeFirst()
        guard let (newFiles, newDirectories) = try? fileManager.contentsOfDirectory(at: directory, options: options) else {
            // The iterator protocol doesn't have a mechanism for raising errors. If an error occurs we
            return nil
        }
        
        foundFiles.append(contentsOf: newFiles)
        foundDirectories.append(contentsOf: newDirectories)
        
        // Iterate again after adding new found files and directories.
        // This enables the iterator do recurse multiple layers of directories until it finds a file.
        return next()
    }
}