Finder is a iterable Sequence; .type -> .kind
This commit is contained in:
@@ -5,33 +5,104 @@ public extension Path {
|
||||
class Finder {
|
||||
fileprivate init(path: Path) {
|
||||
self.path = path
|
||||
self.enumerator = FileManager.default.enumerator(atPath: path.string)
|
||||
}
|
||||
|
||||
/// The `path` find operations operate on.
|
||||
public let path: Path
|
||||
/// The maximum directory depth find operations will dip. Zero means no subdirectories.
|
||||
fileprivate(set) public var maxDepth: Int? = nil
|
||||
|
||||
private let enumerator: FileManager.DirectoryEnumerator!
|
||||
|
||||
/// The range of directory depths for which the find operation will return entries.b
|
||||
private(set) public var depth: ClosedRange<Int> = 1...Int.max
|
||||
|
||||
/// The kinds of filesystem entries find operations will return.
|
||||
fileprivate(set) public var kinds: Set<Path.Kind>?
|
||||
public var types: Set<EntryType> {
|
||||
return _types ?? Set(EntryType.allCases)
|
||||
}
|
||||
|
||||
private var _types: Set<EntryType>?
|
||||
|
||||
/// The file extensions find operations will return. Files *and* directories unless you filter for `kinds`.
|
||||
fileprivate(set) public var extensions: Set<String>?
|
||||
private(set) public var extensions: Set<String>?
|
||||
}
|
||||
}
|
||||
|
||||
extension Path.Finder: Sequence, IteratorProtocol {
|
||||
public func next() -> Path? {
|
||||
guard let enumerator = enumerator else {
|
||||
return nil
|
||||
}
|
||||
while let relativePath = enumerator.nextObject() as? String {
|
||||
let path = self.path/relativePath
|
||||
|
||||
#if !os(Linux) || swift(>=5.0)
|
||||
if enumerator.level > depth.upperBound {
|
||||
enumerator.skipDescendants()
|
||||
continue
|
||||
}
|
||||
if enumerator.level < depth.lowerBound {
|
||||
if path == self.path, depth.lowerBound == 0 {
|
||||
return path
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if let type = path.type, !types.contains(type) { continue }
|
||||
if let exts = extensions, !exts.contains(path.extension) { continue }
|
||||
return path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public typealias Element = Path
|
||||
}
|
||||
|
||||
public extension Path.Finder {
|
||||
/// Multiple calls will configure the Finder for the final depth call only.
|
||||
func maxDepth(_ maxDepth: Int) -> Path.Finder {
|
||||
/// A max depth of `0` returns only the path we are searching, `1` is that directory’s listing.
|
||||
func depth(max maxDepth: Int) -> Path.Finder {
|
||||
#if os(Linux) && !swift(>=5.0)
|
||||
fputs("warning: maxDepth not implemented for Swift < 5\n", stderr)
|
||||
fputs("warning: depth not implemented for Swift < 5\n", stderr)
|
||||
#endif
|
||||
self.maxDepth = maxDepth
|
||||
depth = Swift.min(maxDepth, depth.lowerBound)...maxDepth
|
||||
return self
|
||||
}
|
||||
|
||||
/// A min depth of `0` also returns the path we are searching, `1` is that directory’s listing. Default is `1` thus not returning ourself.
|
||||
func depth(min minDepth: Int) -> Path.Finder {
|
||||
#if os(Linux) && !swift(>=5.0)
|
||||
fputs("warning: depth not implemented for Swift < 5\n", stderr)
|
||||
#endif
|
||||
depth = minDepth...Swift.max(depth.upperBound, minDepth)
|
||||
return self
|
||||
}
|
||||
|
||||
/// A max depth of `0` returns only the path we are searching, `1` is that directory’s listing.
|
||||
/// A min depth of `0` also returns the path we are searching, `1` is that directory’s listing. Default is `1` thus not returning ourself.
|
||||
func depth(_ rng: Range<Int>) -> Path.Finder {
|
||||
#if os(Linux) && !swift(>=5.0)
|
||||
fputs("warning: depth not implemented for Swift < 5\n", stderr)
|
||||
#endif
|
||||
depth = rng.lowerBound...(rng.upperBound - 1)
|
||||
return self
|
||||
}
|
||||
|
||||
/// A max depth of `0` returns only the path we are searching, `1` is that directory’s listing.
|
||||
/// A min depth of `0` also returns the path we are searching, `1` is that directory’s listing. Default is `1` thus not returning ourself.
|
||||
func depth(_ rng: ClosedRange<Int>) -> Path.Finder {
|
||||
#if os(Linux) && !swift(>=5.0)
|
||||
fputs("warning: depth not implemented for Swift < 5\n", stderr)
|
||||
#endif
|
||||
depth = rng
|
||||
return self
|
||||
}
|
||||
|
||||
/// Multiple calls will configure the Finder with multiple kinds.
|
||||
func kind(_ kind: Path.Kind) -> Path.Finder {
|
||||
kinds = kinds ?? []
|
||||
kinds!.insert(kind)
|
||||
func type(_ type: Path.EntryType) -> Path.Finder {
|
||||
_types = _types ?? []
|
||||
_types!.insert(type)
|
||||
return self
|
||||
}
|
||||
|
||||
@@ -42,13 +113,6 @@ public extension Path.Finder {
|
||||
return self
|
||||
}
|
||||
|
||||
/// Enumerate and return all results, note that this may take a while since we are recursive.
|
||||
func execute() -> [Path] {
|
||||
var rv: [Path] = []
|
||||
execute{ rv.append($0); return .continue }
|
||||
return rv
|
||||
}
|
||||
|
||||
/// The return type for `Path.Finder`
|
||||
enum ControlFlow {
|
||||
/// Stop enumerating this directory, return to the parent.
|
||||
@@ -61,34 +125,23 @@ public extension Path.Finder {
|
||||
|
||||
/// Enumerate, one file at a time.
|
||||
func execute(_ closure: (Path) throws -> ControlFlow) rethrows {
|
||||
guard let finder = FileManager.default.enumerator(atPath: path.string) else {
|
||||
fputs("warning: could not enumerate: \(path)\n", stderr)
|
||||
return
|
||||
}
|
||||
while let relativePath = finder.nextObject() as? String {
|
||||
#if !os(Linux) || swift(>=5.0)
|
||||
if let maxDepth = maxDepth, finder.level > maxDepth {
|
||||
finder.skipDescendants()
|
||||
}
|
||||
#endif
|
||||
let path = self.path/relativePath
|
||||
if path == self.path { continue }
|
||||
if let kinds = kinds, let kind = path.kind, !kinds.contains(kind) { continue }
|
||||
if let exts = extensions, !exts.contains(path.extension) { continue }
|
||||
|
||||
while let path = next() {
|
||||
switch try closure(path) {
|
||||
case .skip:
|
||||
finder.skipDescendants()
|
||||
enumerator.skipDescendants()
|
||||
case .abort:
|
||||
return
|
||||
case .continue:
|
||||
break
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Pathish {
|
||||
|
||||
//MARK: Directory Listing
|
||||
|
||||
/**
|
||||
Same as the `ls` command ∴ output is ”shallow” and unsorted.
|
||||
- Note: as per `ls`, by default we do *not* return hidden files. Specify `.a` for hidden files.
|
||||
@@ -114,7 +167,7 @@ public extension Pathish {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience functions for the arraies of `Path`
|
||||
/// Convenience functions for the arrays of `Path`
|
||||
public extension Array where Element == Path {
|
||||
/// Filters the list of entries to be a list of Paths that are directories. Symlinks to directories are not returned.
|
||||
var directories: [Path] {
|
||||
@@ -127,7 +180,12 @@ public extension Array where Element == Path {
|
||||
/// - Note: symlinks that point to files that do not exist are *not* returned.
|
||||
var files: [Path] {
|
||||
return filter {
|
||||
$0.exists && !$0.isDirectory
|
||||
switch $0.type {
|
||||
case .none, .directory?:
|
||||
return false
|
||||
case .file?, .symlink?:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user