Compare commits

...

11 Commits

Author SHA1 Message Date
Max Howell
889d825b3a Merge pull request #24 from mxcl/rename
Rename
2019-01-31 10:08:02 -05:00
Max Howell
f1cd06fdff Add CI post success hook yamls 2019-01-31 09:58:46 -05:00
Max Howell
c6e840b9b6 Add rename 2019-01-31 08:39:54 -05:00
Max Howell
eb34ac4af8 Add overwrite parameter to move(into:) 2019-01-31 08:37:32 -05:00
Max Howell
66ae86c986 Enable codecov.io 2019-01-31 08:37:14 -05:00
Max Howell
c432f710eb Merge pull request #22 from mxcl/files()
Entry.files defaults to all files
2019-01-28 12:17:08 -05:00
Max Howell
19c0c19bb6 Entry.files defaults to all files 2019-01-28 12:04:47 -05:00
Max Howell
ee1f46954c Fixes #20
[skip ci]
2019-01-28 11:05:51 -05:00
Max Howell
2394cc1c85 Merge pull request #19 from JaapWijnen/filehandle-extensions
added extension to initialize filehandle from path
2019-01-27 16:59:44 -05:00
Jaap Wijnen
50bb319619 added extension to initialize filehandle from path 2019-01-27 22:57:19 +01:00
Max Howell
9f40068833 Badge for Swift 5 support 2019-01-26 16:15:45 -05:00
10 changed files with 125 additions and 23 deletions

2
.github/codecov.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
ignore:
- Tests

3
.github/ranger.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
merges:
- action: delete_branch
- action: tag

View File

@@ -19,9 +19,10 @@ jobs:
name: macOS / Swift 4.2.1
- &xcodebuild
before_install: swift package generate-xcodeproj
before_install: swift package generate-xcodeproj --enable-code-coverage
xcode_destination: platform=iOS Simulator,OS=latest,name=iPhone XS
name: iOS / Swift 4.2.1
after_success: bash <(curl -s https://codecov.io/bash)
- <<: *xcodebuild
xcode_destination: platform=tvOS Simulator,OS=latest,name=Apple TV
name: tvOS / Swift 4.2.1
@@ -34,6 +35,7 @@ jobs:
-scheme Path.swift-Package \
-destination 'platform=watchOS Simulator,OS=latest,name=Apple Watch Series 4 - 40mm' \
build | xcpretty
after_success: false
- &linux
env: SWIFT_VERSION=4.2.1

View File

@@ -33,7 +33,7 @@ print(foo) // => /bar/foo
print(foo.isFile) // => true
// we support dynamic members (_use_sparingly_):
let prefs = Path.home.Library.Preferences
let prefs = Path.home.Library.Preferences // => /Users/mxcl/Library/Preferences
// a practical example: installing a helper executable
try Bundle.resources.join("helper").copy(into: Path.home.join(".local/bin").mkdir(.p)).chmod(0o500)
@@ -107,8 +107,9 @@ let ls = Path.root.usr.bin.ls // => /usr/bin/ls
```
This is less commonly useful than you would think, hence our documentation
does not use it. Usually you are joining variables or other `String` arguments.
However when you need it, its *lovely*.
does not use it. Usually you are joining variables or other `String` arguments
or trying to describe files (and files usually have extensions). However when
you need it, its *lovely*.
## Initializing from user-input
@@ -160,9 +161,9 @@ for entry in Path.home.ls() where entry.path.mtime > yesterday {
//
}
let dirs = Path.home.ls().directories().filter {
//
}
let dirs = Path.home.ls().directories
let files = Path.home.ls().files
let swiftFiles = Path.home.ls().files(withExtension: "swift")
```
@@ -220,7 +221,7 @@ package.append(.package(url: "https://github.com/mxcl/Path.swift", from: "0.5.0"
CocoaPods:
```ruby
pod 'Path.swift' ~> '0.5.0'
pod 'Path.swift', '~> 0.5'
```
Carthage:
@@ -244,5 +245,5 @@ https://codebasesaga.com/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-orange.svg
[badge-languages]: https://img.shields.io/badge/swift-4.2%20%7C%205.0-orange.svg
[online API documentation]: https://mxcl.github.io/Path.swift/Structs/Path.html

View File

@@ -86,3 +86,24 @@ public extension Data {
return to
}
}
/// Extensions on `FileHandle` that work with `Path` rather than `String` or `URL`
public extension FileHandle {
/// Initializes this `FileHandle` for reading at the location of the provided path.
@inlinable
convenience init(forReadingAt path: Path) throws {
try self.init(forReadingFrom: path.url)
}
/// Initializes this `FileHandle` for writing at the location of the provided path.
@inlinable
convenience init(forWritingAt path: Path) throws {
try self.init(forWritingTo: path.url)
}
/// Initializes this `FileHandle` for reading and writing at the location of the provided path.
@inlinable
convenience init(forUpdatingAt path: Path) throws {
try self.init(forUpdating: path.url)
}
}

View File

@@ -90,7 +90,7 @@ public extension Path {
*/
@discardableResult
func move(to: Path, overwrite: Bool = false) throws -> Path {
if overwrite, to.exists {
if overwrite, to.isFile {
try FileManager.default.removeItem(at: to.url)
}
try FileManager.default.moveItem(at: url, to: to.url)
@@ -112,13 +112,16 @@ public extension Path {
- SeeAlso: move(into:overwrite:)
*/
@discardableResult
func move(into: Path) throws -> Path {
func move(into: Path, overwrite: Bool = false) throws -> Path {
if !into.exists {
try into.mkdir(.p)
} else if !into.isDirectory {
throw CocoaError.error(.fileWriteFileExists)
}
let rv = into/basename()
if overwrite, rv.isFile {
try FileManager.default.removeItem(at: rv.url)
}
try FileManager.default.moveItem(at: url, to: rv.url)
return rv
}
@@ -171,6 +174,13 @@ public extension Path {
}
return self
}
@discardableResult
func rename(_ newname: String) throws -> Path {
let newpath = parent/newname
try FileManager.default.moveItem(atPath: string, toPath: newpath.string)
return newpath
}
}
/// Options for `Path.mkdir(_:)`

View File

@@ -51,7 +51,14 @@ public extension Array where Element == Entry {
}
}
/// Filters the list of entries to be a list of Paths that are files with the specified extension
/// Filters the list of entries to be a list of Paths that are files.
var files: [Path] {
return compactMap {
$0.kind == .file ? $0.path : nil
}
}
/// Filters the list of entries to be a list of Paths that are files with the specified extension.
func files(withExtension ext: String) -> [Path] {
return compactMap {
$0.kind == .file && $0.path.extension == ext ? $0.path : nil

View File

@@ -36,7 +36,7 @@ public struct Path: Equatable, Hashable, Comparable {
self.string = string
}
/// Returns `nil` unless fed an absolute path
/// Returns `nil` unless fed an absolute path.
public init?(_ description: String) {
guard description.starts(with: "/") || description.starts(with: "~/") else { return nil }
self.init(string: (description as NSString).standardizingPath)
@@ -73,10 +73,15 @@ public struct Path: Equatable, Hashable, Comparable {
/**
Returns the filename extension of this path.
- Remark: Implemented via `NSString.pathExtension`.
- Note: We special case eg. `foo.tar.gz`.
*/
@inlinable
public var `extension`: String {
return (string as NSString).pathExtension
if string.hasSuffix(".tar.gz") {
return "tar.gz"
} else {
return (string as NSString).pathExtension
}
}
//MARK: Pathing
@@ -91,7 +96,7 @@ public struct Path: Equatable, Hashable, Comparable {
- Parameter pathComponent: The string to join with this path.
- Returns: A new joined path.
- SeeAlso: `Path./(_:, _:)`
- SeeAlso: `Path./(_:_:)`
*/
public func join<S>(_ pathComponent: S) -> Path where S: StringProtocol {
//TODO standardizingPath does more than we want really (eg tilde expansion)

View File

@@ -15,20 +15,26 @@ class PathTests: XCTestCase {
let tmpdir_ = try TemporaryDirectory()
let tmpdir = tmpdir_.path
try tmpdir.a.mkdir().c.touch()
try tmpdir.b.touch()
try tmpdir.join("b.swift").touch()
try tmpdir.c.touch()
try tmpdir.join(".d").mkdir().e.touch()
var paths = Set<String>()
let lsrv = try tmpdir.ls()
var dirs = 0
for entry in try tmpdir.ls() {
for entry in lsrv {
if entry.kind == .directory {
dirs += 1
}
paths.insert(entry.path.basename())
}
XCTAssertEqual(dirs, 2)
XCTAssertEqual(paths, ["a", "b", "c", ".d"])
XCTAssertEqual(dirs, lsrv.directories.count)
XCTAssertEqual(["a", ".d"], Set(lsrv.directories.map{ $0.relative(to: tmpdir) }))
XCTAssertEqual(["b.swift", "c"], Set(lsrv.files.map{ $0.relative(to: tmpdir) }))
XCTAssertEqual(["b.swift"], Set(lsrv.files(withExtension: "swift").map{ $0.relative(to: tmpdir) }))
XCTAssertEqual(["c"], Set(lsrv.files(withExtension: "").map{ $0.relative(to: tmpdir) }))
XCTAssertEqual(paths, ["a", "b.swift", "c", ".d"])
}
@@ -69,6 +75,18 @@ class PathTests: XCTestCase {
XCTAssert((Path.root/"bin").isDirectory)
}
func testExtension() {
XCTAssertEqual(Path.root.join("a.swift").extension, "swift")
XCTAssertEqual(Path.root.join("a").extension, "")
XCTAssertEqual(Path.root.join("a.").extension, "")
XCTAssertEqual(Path.root.join("a..").extension, "")
XCTAssertEqual(Path.root.join("a..swift").extension, "swift")
XCTAssertEqual(Path.root.join("a..swift.").extension, "")
XCTAssertEqual(Path.root.join("a.tar.gz").extension, "tar.gz")
XCTAssertEqual(Path.root.join("a..tar.gz").extension, "tar.gz")
XCTAssertEqual(Path.root.join("a..tar..gz").extension, "gz")
}
func testMktemp() throws {
var path: Path!
try Path.mktemp {
@@ -147,12 +165,42 @@ class PathTests: XCTestCase {
}
func testCopyInto() throws {
try Path.mktemp { root1 in
let bar1 = try root1.join("bar").touch()
try Path.mktemp { root2 in
let bar2 = try root2.join("bar").touch()
XCTAssertThrowsError(try bar1.copy(into: root2))
try bar1.copy(into: root2, overwrite: true)
XCTAssertTrue(bar1.exists)
XCTAssertTrue(bar2.exists)
}
}
}
func testMoveInto() throws {
try Path.mktemp { root1 in
let bar1 = try root1.join("bar").touch()
try Path.mktemp { root2 in
let bar2 = try root2.join("bar").touch()
XCTAssertThrowsError(try bar1.move(into: root2))
try bar1.move(into: root2, overwrite: true)
XCTAssertFalse(bar1.exists)
XCTAssertTrue(bar2.exists)
}
}
}
func testRename() throws {
try Path.mktemp { root in
let bar = try root.join("bar").touch()
try Path.mktemp { root in
try root.join("bar").touch()
XCTAssertThrowsError(try bar.copy(into: root))
try bar.copy(into: root, overwrite: true)
do {
let file = try root.bar.touch()
let foo = try file.rename("foo")
XCTAssertFalse(file.exists)
XCTAssertTrue(foo.isFile)
}
do {
let file = try root.bar.touch()
XCTAssertThrowsError(try file.rename("foo"))
}
}
}

View File

@@ -10,12 +10,15 @@ extension PathTests {
("testEnumeration", testEnumeration),
("testEnumerationSkippingHiddenFiles", testEnumerationSkippingHiddenFiles),
("testExists", testExists),
("testExtension", testExtension),
("testIsDirectory", testIsDirectory),
("testJoin", testJoin),
("testMkpathIfExists", testMkpathIfExists),
("testMktemp", testMktemp),
("testMoveInto", testMoveInto),
("testRelativePathCodable", testRelativePathCodable),
("testRelativeTo", testRelativeTo),
("testRename", testRename),
]
}