Skip to content

Commit

Permalink
Improve FileEnumerator
Browse files Browse the repository at this point in the history
  • Loading branch information
Alkenso committed Sep 29, 2024
1 parent 6f9ec41 commit 2e6c57c
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ public final class FileEnumerator {
public var types: Set<URLFileResourceType> = []

/// If set, the filter is applied to every URL while enumeration.
public var locationFilter: ((URL) -> FilterVerdict)?
/// If available, includes URL file type.
public var locationFilter: ((URL, URLFileResourceType?) -> FilterVerdict)?

/// Forward flags to underlying `FileManager.DirectoryEnumerator`.
public var options: FileManager.DirectoryEnumerationOptions = []
public var options: (URL) -> FileManager.DirectoryEnumerationOptions = { _ in [] }

/// Creates `FileEnumerator` that enumerates given locations recursively
/// - Parameters:
Expand Down Expand Up @@ -99,22 +100,22 @@ extension FileEnumerator: Sequence, IteratorProtocol {
let type = try? next.resourceValues(forKeys: [.fileResourceTypeKey]).fileResourceType
var filterVerdict: FilterVerdict?

// If directory is not interested, skip whole content.
// If directory is not interested, skip whole content.
if type == .directory {
filterVerdict = locationFilter?(next)
filterVerdict = locationFilter?(next, type)
if filterVerdict?.children == false {
enumerator?.skipDescendants()
}
}

// Check if `next` is interested according to its type.
// Check if `next` is interested according to its type.
if !types.isEmpty {
guard let type else { continue }
guard types.contains(type) else { continue }
}

// Check if `next` is interested according to its URL.
if let verdict = filterVerdict ?? locationFilter?(next), !verdict.current {
// Check if `next` is interested according to its URL.
if let verdict = filterVerdict ?? locationFilter?(next, type), !verdict.current {
continue
}

Expand All @@ -125,25 +126,29 @@ extension FileEnumerator: Sequence, IteratorProtocol {
}

private func nextUnfiltered() -> URL? {
// If next file exists, just return it.
// If next file exists, just return it.
if let next = enumerator?.nextObject() as? URL {
return next
}

// All files/locations enumerated. 'nil' means the end of the sequence.
// All files/locations enumerated. 'nil' means the end of the sequence.
guard let nextLocation = locations.popLast() else {
return nil
}

// If location doesn't exists, just skip it.
// If location doesn't exists, just skip it.
var isDirectory = false
guard FileManager.default.fileExists(at: nextLocation, isDirectory: &isDirectory) else {
return nextUnfiltered()
}

// If location is directory, update enumerator.
// If location is directory, update enumerator.
if isDirectory {
enumerator = FileManager.default.enumerator(at: nextLocation, includingPropertiesForKeys: [.fileResourceTypeKey], options: options)
enumerator = FileManager.default.enumerator(
at: nextLocation,
includingPropertiesForKeys: [.fileResourceTypeKey],
options: options(nextLocation)
)
}

return nextLocation
Expand Down
12 changes: 10 additions & 2 deletions Tests/SpellbookTests/Filesystem & Bundle/FileEnumeratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,23 @@ class FileEnumeratorTests: XCTestCase {
_ = try tempDir.createFile(name: "subdir/nested/file5", content: Data())

let enumerator = FileEnumerator(tempDir.location)
enumerator.locationFilter = {
switch $0.lastPathComponent {
enumerator.locationFilter = { url, type in
switch url.lastPathComponent {
case "subdir":
XCTAssertEqual(type, .directory)
return .skip
case "subdir2":
XCTAssertEqual(type, .directory)
return .init(current: true, children: false)
case "nested":
XCTAssertEqual(type, .directory)
return .skipRecursive
default:
if url.path.contains("file") {
XCTAssertEqual(type, .regular)
} else {
XCTAssertEqual(type, .directory)
}
return .proceed
}
}
Expand Down

0 comments on commit 2e6c57c

Please sign in to comment.