Compare commits

..

8 Commits

Author SHA1 Message Date
Max Howell
f062ed9ce3 Fix CI deploy 2020-01-24 12:01:34 -05:00
Max Howell
f1f7ee33b1 [ci skip] List all support Swift 2020-01-24 11:17:42 -05:00
Max Howell
694d04f18b Prepare 1.0.0 release 2020-01-24 11:03:07 -05:00
Max Howell
5636a7ac65 Update Swift Linux test versions 2020-01-18 12:10:32 -05:00
Max Howell
3e964833ff Fix README documentation for Finder 2020-01-18 12:10:32 -05:00
Max Howell
30122659a5 Update linux-tests; fail if warnings on travis
* Update linux-tests; fail if warnings on travis

* Fix warnings on Linux

* Typo

* Can’t test these on Linux
2019-08-18 16:52:24 -04:00
Max Howell
0ef50dff2e Finder is a iterable Sequence; .type -> .kind 2019-07-24 14:39:47 -04:00
Max Howell
dfad7367b7 Get out documentation %age up 2019-07-21 21:35:48 -04:00
13 changed files with 265 additions and 154 deletions

2
.github/jazzy.yml vendored
View File

@@ -3,7 +3,7 @@ custom_categories:
- name: Path
children:
- Path
- /(_:_:)
- Pathish
xcodebuild_arguments:
- UseModernBuildSystem=NO
output:

View File

@@ -1,10 +1,14 @@
# only run for: merge commits, releases and pull-requests
if: type != push OR branch = master OR branch =~ /^\d+\.\d+\.\d+(-.*)?$/
if: type != push OR branch = master OR branch =~ /^deploy-\d+\.\d+\.\d+(-.*)?$/ OR branch =~ /^\d+\.\d+\.\d+(-.*)?$/
stages:
- name: pretest
if: NOT branch =~ /^deploy-\d+\.\d+\.\d+(-.*)?$/
- name: test
if: NOT branch =~ /^deploy-\d+\.\d+\.\d+(-.*)?$/
- name: deploy
if: branch =~ /^deploy-\d+\.\d+\.\d+(-.*)?$/
- name: publish
if: branch =~ /^\d+\.\d+\.\d+(-.*)?$/
os: osx
@@ -16,18 +20,21 @@ xcode_scheme: Path.swift-Package
jobs:
include:
- name: macOS / Swift 4.0.3
before_script: swift build -Xswiftc -warnings-as-errors
script: swift test --parallel -Xswiftc -swift-version -Xswiftc 4
- name: macOS / Swift 4.2.1
- &std
name: macOS / Swift 4.2.1
before_script: swift build -Xswiftc -warnings-as-errors
script: swift test --parallel
- name: macOS / Swift 5.0
- <<: *std
name: macOS / Swift 5.0
osx_image: xcode10.2
script: swift test --parallel
- name: macOS / Swift 5.1
- <<: *std
name: macOS / Swift 5.1
osx_image: xcode11
script: swift test --parallel
- &xcodebuild
before_install: swift package generate-xcodeproj --enable-code-coverage
@@ -57,15 +64,20 @@ jobs:
language: generic
sudo: false
install: eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
before_script: swift build -Xswiftc -warnings-as-errors
script: swift test --parallel
- <<: *linux
env: SWIFT_VERSION='5.0.2'
name: Linux / Swift 5.0.2
env: SWIFT_VERSION=5.0.3
name: Linux / Swift 5.0.3
- <<: *linux
env: SWIFT_VERSION=5.1-DEVELOPMENT-SNAPSHOT-2019-07-03-a
name: Linux / Swift 5.1 (2019-07-03)
env: SWIFT_VERSION=5.1.3
name: Linux / Swift 5.1.3
- <<: *linux
env: SWIFT_VERSION=5.2-DEVELOPMENT-SNAPSHOT-2020-01-22-a
name: Linux / Swift 5.2.0-dev+2020-01-22-a
- stage: pretest
name: Check Linux tests are syncd
@@ -74,6 +86,21 @@ jobs:
osx_image: xcode10.2
- stage: deploy
name: Deploy
osx_image: xcode11
env: HOMEBREW_NO_INSTALL_CLEANUP=1
install: brew install mxcl/made/swift-sh
script:
- set -e
- export VERSION=$(echo $TRAVIS_TAG | cut -c 8-)
- git tag "$VERSION"
- git remote set-url origin "https://$GITHUB_TOKEN@github.com/$TRAVIS_REPO_SLUG.git"
- git fetch --unshallow origin
- git push origin "$VERSION"
- swift sh <(curl https://raw.githubusercontent.com/mxcl/ops/master/deploy) publish-release
- git push origin :$TRAVIS_TAG
- stage: publish
name: Jazzy
osx_image: xcode10.2
install: gem install jazzy

View File

@@ -44,10 +44,8 @@ Swift), we provide a thoughtful and comprehensive (yet concise) API.
# Support mxcl
Hi, Im Max Howell and I have written a lot of open source software, and
probably you already use some of it (Homebrew anyone?). I work full-time on
open source and its hard; currently I earn *less* than minimum wage. Please
help me continue my work, I appreciate it x
Hi, Im Max Howell and I have written a lot of open source software—generally
a good deal of my free time 👨🏻‍💻.
<a href="https://www.patreon.com/mxcl">
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
@@ -183,7 +181,7 @@ Path.home.find().execute { path in
Which is configurable:
```swift
Path.home.find().maxDepth(1).extension("swift").kind(.file) { path in
Path.home.find().depth(max: 1).extension("swift").type(.file) { path in
//
}
```
@@ -300,7 +298,7 @@ for that as the check was deemed too expensive to be worthwhile.
If a `Path` is a symlink but the destination of the link does not exist `exists`
returns `false`. This seems to be the correct thing to do since symlinks are
meant to be an abstraction for filesystems. To instead verify that there is
no filesystem entry there at all check if `kind` is `nil`.
no filesystem entry there at all check if `type` is `nil`.
## We do not provide change directory functionality
@@ -337,28 +335,19 @@ actual filesystem path, however we also check the URL has a `file` scheme first.
SwiftPM:
```swift
package.append(.package(url: "https://github.com/mxcl/Path.swift.git", from: "0.13.0"))
package.append(.package(url: "https://github.com/mxcl/Path.swift.git", from: "1.0.0"))
```
CocoaPods:
```ruby
pod 'Path.swift', '~> 0.13'
pod 'Path.swift', '~> 1.0.0'
```
Carthage:
> Waiting on: [@Carthage#1945](https://github.com/Carthage/Carthage/pull/1945).
## Pre1.0 status
We are pre 1.0, thus we can change the API as we like, and we will (to the
pursuit of getting it *right*)! We will tag 1.0 as soon as possible.
### Get push notifications for new releases
https://mxcl.dev/canopy/
# Alternatives
* [Pathos](https://github.com/dduan/Pathos) by Daniel Duan
@@ -368,7 +357,7 @@ https://mxcl.dev/canopy/
[badge-platforms]: https://img.shields.io/badge/platforms-macOS%20%7C%20Linux%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS-lightgrey.svg
[badge-languages]: https://img.shields.io/badge/swift-4.2%20%7C%205.0-orange.svg
[badge-languages]: https://img.shields.io/badge/swift-4.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-orange.svg
[docs]: https://mxcl.dev/Path.swift/Structs/Path.html
[badge-jazzy]: https://raw.githubusercontent.com/mxcl/Path.swift/gh-pages/badge.svg?sanitize=true
[badge-codecov]: https://codecov.io/gh/mxcl/Path.swift/branch/master/graph/badge.svg

View File

@@ -30,6 +30,29 @@ public extension Pathish {
}
}
/// The type of the entry.
/// - SeeAlso: `Path.EntryType`
@available(*, deprecated, message: "- SeeAlso: Path.type")
var kind: Path.EntryType? {
return type
}
/// The type of the entry.
/// - SeeAlso: `Path.EntryType`
var type: Path.EntryType? {
var buf = stat()
guard lstat(string, &buf) == 0 else {
return nil
}
if buf.st_mode & S_IFMT == S_IFLNK {
return .symlink
} else if buf.st_mode & S_IFMT == S_IFDIR {
return .directory
} else {
return .file
}
}
/**
Sets the files attributes using UNIX octal notation.
@@ -40,6 +63,8 @@ public extension Pathish {
try FileManager.default.setAttributes([.posixPermissions: octal], ofItemAtPath: string)
return Path(self)
}
//MARK: Filesystem Locking
/**
Applies the macOS filesystem lock attribute.
@@ -83,24 +108,17 @@ public extension Pathish {
#endif
return Path(self)
}
var kind: Path.Kind? {
var buf = stat()
guard lstat(string, &buf) == 0 else {
return nil
}
if buf.st_mode & S_IFMT == S_IFLNK {
return .symlink
} else if buf.st_mode & S_IFMT == S_IFDIR {
return .directory
} else {
return .file
}
}
}
/// The `extension` that provides `Kind`.
public extension Path {
enum Kind {
case file, symlink, directory
/// A filesystem entrys kind, file, directory, symlink etc.
enum EntryType: CaseIterable {
/// The entry is a file.
case file
/// The entry is a symlink.
case symlink
/// The entry is a directory.
case directory
}
}

View File

@@ -1,8 +1,9 @@
import Foundation
/// The `extension` that provides static properties that are common directories.
extension Path {
//MARK: Common Directories
/// Returns a `Path` containing `FileManager.default.currentDirectoryPath`.
public static var cwd: DynamicPath {
return .init(string: FileManager.default.currentDirectoryPath)

View File

@@ -4,8 +4,9 @@ import Glibc
#endif
public extension Pathish {
//MARK: File Management
/**
Copies a file.
@@ -25,11 +26,11 @@ public extension Pathish {
*/
@discardableResult
func copy<P: Pathish>(to: P, overwrite: Bool = false) throws -> Path {
if overwrite, let tokind = to.kind, tokind != .directory, kind != .directory {
if overwrite, let tokind = to.type, tokind != .directory, type != .directory {
try FileManager.default.removeItem(at: to.url)
}
#if os(Linux) && !swift(>=5.2) // check if fixed
if !overwrite, to.kind != nil {
#if os(Linux) && !swift(>=5.3) // check if fixed
if !overwrite, to.type != nil {
throw CocoaError.error(.fileWriteFileExists)
}
#endif
@@ -61,15 +62,15 @@ public extension Pathish {
*/
@discardableResult
func copy<P: Pathish>(into: P, overwrite: Bool = false) throws -> Path {
if into.kind == nil {
if into.type == nil {
try into.mkdir(.p)
}
let rv = into/basename()
if overwrite, let kind = rv.kind, kind != .directory {
if overwrite, let kind = rv.type, kind != .directory {
try FileManager.default.removeItem(at: rv.url)
}
#if os(Linux) && !swift(>=5.2) // check if fixed
if !overwrite, rv.kind != nil {
#if os(Linux) && !swift(>=5.3) // check if fixed
if !overwrite, rv.type != nil {
throw CocoaError.error(.fileWriteFileExists)
}
#endif
@@ -95,7 +96,7 @@ public extension Pathish {
*/
@discardableResult
func move<P: Pathish>(to: P, overwrite: Bool = false) throws -> Path {
if overwrite, let kind = to.kind, kind != .directory {
if overwrite, let kind = to.type, kind != .directory {
try FileManager.default.removeItem(at: to.url)
}
try FileManager.default.moveItem(at: url, to: to.url)
@@ -119,13 +120,13 @@ public extension Pathish {
*/
@discardableResult
func move<P: Pathish>(into: P, overwrite: Bool = false) throws -> Path {
switch into.kind {
switch into.type {
case nil:
try into.mkdir(.p)
fallthrough
case .directory?:
let rv = into/basename()
if overwrite, let rvkind = rv.kind, rvkind != .directory {
if overwrite, let rvkind = rv.type, rvkind != .directory {
try FileManager.default.removeItem(at: rv.url)
}
try FileManager.default.moveItem(at: url, to: rv.url)
@@ -147,7 +148,7 @@ public extension Pathish {
*/
@inlinable
func delete() throws {
if kind != nil {
if type != nil {
try FileManager.default.removeItem(at: url)
}
}
@@ -159,7 +160,7 @@ public extension Pathish {
@inlinable
@discardableResult
func touch() throws -> Path {
if kind == nil {
if type == nil {
guard FileManager.default.createFile(atPath: string, contents: nil) else {
throw CocoaError.error(.fileWriteUnknown)
}
@@ -203,7 +204,7 @@ public extension Pathish {
}
/**
Renames the file at path.
Renames the file (basename only) at path.
Path.root.foo.bar.rename(to: "baz") // => /foo/baz
@@ -233,7 +234,7 @@ public extension Pathish {
*/
@discardableResult
func symlink<P: Pathish>(into dir: P) throws -> Path {
switch dir.kind {
switch dir.type {
case nil, .symlink?:
try dir.mkdir(.p)
fallthrough

View File

@@ -1,32 +1,108 @@
import Foundation
public extension Path {
/// The builder for `Path.find()`
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
fileprivate(set) public var maxDepth: Int? = nil
fileprivate(set) public var kinds: Set<Path.Kind>?
fileprivate(set) public var extensions: Set<String>?
private let enumerator: FileManager.DirectoryEnumerator!
/// The range of directory depths for which the find operation will return entries.
private(set) public var depth: ClosedRange<Int> = 1...Int.max
/// The kinds of filesystem entries find operations will return.
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`.
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 directorys 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 directorys 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 directorys listing.
/// A min depth of `0` also returns the path we are searching, `1` is that directorys 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 directorys listing.
/// A min depth of `0` also returns the path we are searching, `1` is that directorys 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
}
@@ -37,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.
@@ -56,35 +125,22 @@ 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 Listings
public extension Pathish {
//MARK: Directory Listing
/**
Same as the `ls` command output is shallow and unsorted.
@@ -105,12 +161,13 @@ public extension Pathish {
}
}
/// Recursively find files under this path. If the path is a file, no files will be found.
func find() -> Path.Finder {
return .init(path: Path(self))
}
}
/// 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] {
@@ -123,12 +180,17 @@ 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
}
}
}
}
/// Options for `Path.mkdir(_:)`
/// Options for `Path.ls(_:)`
public enum ListDirectoryOptions {
/// Creates intermediary directories; works the same as `mkdir -p`.
case a

View File

@@ -6,11 +6,13 @@ import Darwin
#endif
public extension Pathish {
//MARK: Filesystem Properties
/**
- Returns: `true` if the path represents an actual filesystem entry.
- Note: If `self` is a symlink the return value represents the destination.
- Note: If `self` is a symlink the return value represents the destination, thus if the destination doesnt exist this returns `false` even if the symlink exists.
- Note: To determine if a symlink exists, use `.type`.
*/
var exists: Bool {
return FileManager.default.fileExists(atPath: string)

View File

@@ -36,6 +36,7 @@ let _realpath = Glibc.realpath
to the anti-pattern where Path.swift suddenly feels like Javascript otherwise.
- Note: A `Path` does not necessarily represent an actual filesystem entry.
- SeeAlso: `Pathish` for most methods you will use on `Path` instances.
*/
public struct Path: Pathish {
@@ -135,6 +136,8 @@ public struct Path: Pathish {
}
public extension Pathish {
//MARK: Filesystem Representation
/// Returns a `URL` representing this file path.
var url: URL {
return URL(fileURLWithPath: string)
@@ -173,7 +176,7 @@ public extension Pathish {
Returns the filename extension of this path.
- Remark: If there is no extension returns "".
- Remark: If the filename ends with any number of ".", returns "".
- Note: We special case eg. `foo.tar.gz`.
- Note: We special case eg. `foo.tar.gz`there are a limited number of these specializations, feel free to PR more.
*/
@inlinable
var `extension`: String {
@@ -200,14 +203,14 @@ public extension Pathish {
/**
Splits the string representation on the directory separator.
- Important: The first element is always "/" to be consistent with `NSString.pathComponents`.
- Important: `NSString.pathComponents` will always return an initial `/` in its array for absolute paths to indicate that the path was absolute, we dont do this because we are *always* absolute paths.
*/
@inlinable
var components: [String] {
return ["/"] + string.split(separator: "/").map(String.init)
return string.split(separator: "/").map(String.init)
}
//MARK: Pathing
//MARK:- Pathing
/**
Joins a path and a string to produce a new path.
@@ -404,7 +407,7 @@ private func join_<S>(prefix: String, pathComponents: S) -> String where S: Sequ
return rv
}
/// A path that supports arbituary dot notation, eg. Path.root.usr.bin
/// A path that supports arbituary dot notation, eg. `Path.root.usr.bin`
@dynamicMemberLookup
public struct DynamicPath: Pathish {
/// The normalized string representation of the underlying filesystem path
@@ -416,7 +419,7 @@ public struct DynamicPath: Pathish {
}
/// Converts a `Path` to a `DynamicPath`
public init(_ path: Path) {
public init<P: Pathish>(_ path: P) {
string = path.string
}

View File

@@ -1,13 +1,6 @@
/// A type that represents a filesystem path, if you conform your type
/// to `Pathish` it is your responsibility to ensure the string is correctly normalized
public protocol Pathish: Hashable, Comparable {
/// The normalized string representation of the underlying filesystem path
var string: String { get }
}
public extension Pathish {
static func ==<P: Pathish> (lhs: Self, rhs: P) -> Bool {
return lhs.string == rhs.string
}
}

View File

@@ -2,39 +2,54 @@ import XCTest
import Path
extension PathTests {
func testFindMaxDepth0() throws {
#if !os(Linux) || swift(>=5)
func testFindMaxDepth1() throws {
try Path.mktemp { tmpdir in
try tmpdir.a.touch()
try tmpdir.b.touch()
try tmpdir.c.mkdir().join("e").touch()
XCTAssertEqual(
Set(tmpdir.find().maxDepth(0).execute()),
Set([tmpdir.a, tmpdir.b, tmpdir.c].map(Path.init)))
do {
let finder = tmpdir.find().depth(max: 1)
XCTAssertEqual(finder.depth, 1...1)
#if !os(Linux) || swift(>=5)
XCTAssertEqual(Set(finder), Set([tmpdir.a, tmpdir.b, tmpdir.c].map(Path.init)))
#endif
}
do {
let finder = tmpdir.find().depth(max: 0)
XCTAssertEqual(finder.depth, 0...0)
#if !os(Linux) || swift(>=5)
XCTAssertEqual(Set(finder), Set())
#endif
}
}
#endif
}
func testFindMaxDepth1() throws {
#if !os(Linux) || swift(>=5)
func testFindMaxDepth2() throws {
try Path.mktemp { tmpdir in
try tmpdir.a.touch()
try tmpdir.b.mkdir().join("c").touch()
try tmpdir.b.d.mkdir().join("e").touch()
#if !os(Linux)
XCTAssertEqual(
Set(tmpdir.find().maxDepth(1).execute()),
Set([tmpdir.a, tmpdir.b, tmpdir.b.c].map(Path.init)))
#else
// Linux behavior is different :-/
XCTAssertEqual(
Set(tmpdir.find().maxDepth(1).execute()),
Set([tmpdir.a, tmpdir.b, tmpdir.b.d, tmpdir.b.c].map(Path.init)))
#endif
do {
let finder = tmpdir.find().depth(max: 2)
XCTAssertEqual(finder.depth, 1...2)
#if !os(Linux) || swift(>=5)
XCTAssertEqual(
Set(finder),
Set([tmpdir.a, tmpdir.b, tmpdir.b.d, tmpdir.b.c].map(Path.init)))
#endif
}
do {
let finder = tmpdir.find().depth(max: 3)
XCTAssertEqual(finder.depth, 1...3)
#if !os(Linux) || swift(>=5)
XCTAssertEqual(
Set(finder),
Set([tmpdir.a, tmpdir.b, tmpdir.b.d, tmpdir.b.c, tmpdir.b.d.e].map(Path.init)))
#endif
}
}
#endif
}
func testFindExtension() throws {
@@ -43,27 +58,27 @@ extension PathTests {
try tmpdir.join("bar.txt").touch()
XCTAssertEqual(
Set(tmpdir.find().extension("json").execute()),
Set(tmpdir.find().extension("json")),
[tmpdir.join("foo.json")])
XCTAssertEqual(
Set(tmpdir.find().extension("txt").extension("json").execute()),
Set(tmpdir.find().extension("txt").extension("json")),
[tmpdir.join("foo.json"), tmpdir.join("bar.txt")])
}
}
func testFindKinds() throws {
func testFindTypes() throws {
try Path.mktemp { tmpdir in
try tmpdir.foo.mkdir()
try tmpdir.bar.touch()
XCTAssertEqual(
Set(tmpdir.find().kind(.file).execute()),
Set(tmpdir.find().type(.file)),
[tmpdir.join("bar")])
XCTAssertEqual(
Set(tmpdir.find().kind(.directory).execute()),
Set(tmpdir.find().type(.directory)),
[tmpdir.join("foo")])
XCTAssertEqual(
Set(tmpdir.find().kind(.file).kind(.directory).execute()),
Set(tmpdir.find().type(.file).type(.directory)),
Set(["foo", "bar"].map(tmpdir.join)))
}
}

View File

@@ -76,10 +76,10 @@ class PathTests: XCTestCase {
try Path.mktemp { tmpdir in
XCTAssertTrue(tmpdir.exists)
XCTAssertFalse(try tmpdir.bar.symlink(as: tmpdir.foo).exists)
XCTAssertTrue(tmpdir.foo.kind == .symlink)
XCTAssertTrue(tmpdir.foo.type == .symlink)
XCTAssertTrue(try tmpdir.bar.touch().symlink(as: tmpdir.baz).exists)
XCTAssertTrue(tmpdir.bar.kind == .file)
XCTAssertTrue(tmpdir.kind == .directory)
XCTAssertTrue(tmpdir.bar.type == .file)
XCTAssertTrue(tmpdir.type == .directory)
}
}
@@ -393,18 +393,18 @@ class PathTests: XCTestCase {
// regression test: can delete a symlink that points to a non-existent file
let bar5 = try tmpdir.bar4.symlink(as: tmpdir.bar5)
XCTAssertEqual(bar5.kind, .symlink)
XCTAssertEqual(bar5.type, .symlink)
XCTAssertFalse(bar5.exists)
XCTAssertNoThrow(try bar5.delete())
XCTAssertEqual(bar5.kind, nil)
XCTAssertEqual(bar5.type, nil)
// test that deleting a symlink *only* deletes the symlink
let bar7 = try tmpdir.bar6.touch().symlink(as: tmpdir.bar7)
XCTAssertEqual(bar7.kind, .symlink)
XCTAssertEqual(bar7.type, .symlink)
XCTAssertTrue(bar7.exists)
XCTAssertNoThrow(try bar7.delete())
XCTAssertEqual(bar7.kind, nil)
XCTAssertEqual(tmpdir.bar6.kind, .file)
XCTAssertEqual(bar7.type, nil)
XCTAssertEqual(tmpdir.bar6.type, .file)
}
}
@@ -619,8 +619,8 @@ class PathTests: XCTestCase {
}
func testPathComponents() throws {
XCTAssertEqual(Path.root.foo.bar.components, ["/", "foo", "bar"])
XCTAssertEqual(Path.root.components, ["/"])
XCTAssertEqual(Path.root.foo.bar.components, ["foo", "bar"])
XCTAssertEqual(Path.root.components, [])
}
func testFlatMap() throws {
@@ -637,9 +637,9 @@ class PathTests: XCTestCase {
try Path.mktemp { tmpdir in
let foo = try tmpdir.foo.touch()
let bar = try foo.symlink(as: tmpdir.bar)
XCTAssertEqual(tmpdir.kind, .directory)
XCTAssertEqual(foo.kind, .file)
XCTAssertEqual(bar.kind, .symlink)
XCTAssertEqual(tmpdir.type, .directory)
XCTAssertEqual(foo.type, .file)
XCTAssertEqual(bar.type, .symlink)
}
}
}

View File

@@ -25,9 +25,9 @@ extension PathTests {
("testFileReference", testFileReference),
("testFilesystemAttributes", testFilesystemAttributes),
("testFindExtension", testFindExtension),
("testFindKinds", testFindKinds),
("testFindMaxDepth0", testFindMaxDepth0),
("testFindMaxDepth1", testFindMaxDepth1),
("testFindMaxDepth2", testFindMaxDepth2),
("testFindTypes", testFindTypes),
("testFlatMap", testFlatMap),
("testInitializerForRelativePath", testInitializerForRelativePath),
("testIsDirectory", testIsDirectory),