Skip to content

Commit

Permalink
Merge pull request #14 from mxcl/delete-noop
Browse files Browse the repository at this point in the history
Delete noop
  • Loading branch information
mxcl authored Jan 26, 2019
2 parents 9ea3204 + bafb05f commit 3beba13
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 48 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ print(foo) // => /bar/foo
print(foo.isFile) // => true

// A practical example: installing a helper executable
try Bundle.resources.join("helper").copy(into: Path.home.join(".local/bin").mkpath()).chmod(0o500)
try Bundle.resources.join("helper").copy(into: Path.home.join(".local/bin").mkdir(.p)).chmod(0o500)
```

We emphasize safety and correctness, just like Swift, and also (again like
Expand Down Expand Up @@ -184,6 +184,16 @@ Path("~/foo")! // => /Users/mxcl/foo
Path("~foo") // => nil
```
*Path.swift* has the general policty that if the desired end result preexists,
then it’s a noop:
* If you try to delete a file, but the file doesn't exist, we do nothing.
* If you try to make a directory and it already exists, we do nothing.
However notably if you try to copy or move a file with specifying `overwrite`
and the file already exists at the destination and is identical, we don’t check
for that as the check was deemed too expensive to be worthwhile.
# Installation
SwiftPM:
Expand Down
4 changes: 2 additions & 2 deletions Sources/Path+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public extension CodingUserInfoKey {
*/
extension Path: Codable {
/// - SeeAlso: `CodingUserInfoKey.relativePath`
// :nodoc:
/// :nodoc:
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
if value.hasPrefix("/") {
Expand All @@ -39,7 +39,7 @@ extension Path: Codable {
}

/// - SeeAlso: `CodingUserInfoKey.relativePath`
// :nodoc:
/// :nodoc:
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let root = encoder.userInfo[.relativePath] as? Path {
Expand Down
108 changes: 64 additions & 44 deletions Sources/Path+FileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@ public extension Path {

/**
Copies a file.
try Path.root.join("bar").copy(to: Path.home/"foo")
// => "/Users/mxcl/foo"
- Note: `throws` if `to` is a directory.
- Parameter to: Destination filename.
- Parameter overwrite: If `true` and both `self` and `to` are files, overwrites `to`.
- Note: If either `self` or `to are directories, `overwrite` is ignored.
- Note: Throws if `overwrite` is `false` yet `to` is *already* identical to
`self` because even though *Path.swift’s* policy is to noop if the desired
end result preexists, checking for this condition is too expensive a
trade-off.
- Returns: `to` to allow chaining
- SeeAlso: `copy(into:overwrite:)`
*/
Expand All @@ -24,15 +32,22 @@ public extension Path {
/**
Copies a file into a directory
try Path.root.join("bar").copy(into: .home)
// => "/Users/mxcl/bar"
// Create ~/.local/bin, copy `ls` there and make the new copy executable
try Path.root.join("bin/ls").copy(into: Path.home.join(".local/bin").mkdir(.p)).chmod(0o500)
If the destination does not exist, this function creates the directory first.
// Create ~/.local/bin, copy `ls` there and make the new copy executable
try Path.root.join("bin/ls").copy(into: Path.home.join(".local/bin").mkpath()).chmod(0o500)
- Note: `throws` if `into` is a file.
- Parameter into: Destination directory
- Parameter overwrite: If true overwrites any file that already exists at `into`.
- Returns: The `Path` of the newly copied file.
- Note: `throws` if `into` is a file.
- Note: Throws if `overwrite` is `false` yet `to` is *already* identical to
`self` because even though *Path.swift’s* policy is to noop if the desired
end result preexists, checking for this condition is too expensive a
trade-off.
- SeeAlso: `copy(into:overwrite:)`
*/
@discardableResult
Expand All @@ -59,10 +74,18 @@ public extension Path {

/**
Moves a file.
- Note: `throws` if `to` is a directory.
try Path.root.join("bar").move(to: Path.home/"foo")
// => "/Users/mxcl/foo"
- Parameter to: Destination filename.
- Parameter overwrite: If true overwrites any file that already exists at `to`.
- Returns: `to` to allow chaining
- Note: `throws` if `to` is a directory.
- Note: Throws if `overwrite` is `false` yet `to` is *already* identical to
`self` because even though *Path.swift’s* policy is to noop if the desired
end result preexists, checking for this condition is too expensive a
trade-off.
- SeeAlso: move(into:overwrite:)
*/
@discardableResult
Expand All @@ -77,18 +100,21 @@ public extension Path {
/**
Moves a file into a directory
try Path.root.join("bar").move(into: .home)
// => "/Users/mxcl/bar"
If the destination does not exist, this function creates the directory first.
- Note: `throws` if `into` is a file.
- Parameter into: Destination directory
- Parameter overwrite: If true overwrites any file that already exists at `into`.
- Parameter overwrite: If true *overwrites* any file that already exists at `into`.
- Note: `throws` if `into` is a file.
- Returns: The `Path` of destination filename.
- SeeAlso: move(into:overwrite:)
*/
@discardableResult
func move(into: Path) throws -> Path {
if !into.exists {
try into.mkpath()
try into.mkdir(.p)
} else if !into.isDirectory {
throw CocoaError.error(.fileWriteFileExists)
}
Expand All @@ -97,10 +123,16 @@ public extension Path {
return rv
}

/// Deletes the path, recursively if a directory.
/**
Deletes the path, recursively if a directory.
- Note: noop: if the path doesn’t exist
∵ *Path.swift* doesn’t error if desired end result preexists.
*/
@inlinable
func delete() throws {
try FileManager.default.removeItem(at: url)
if exists {
try FileManager.default.removeItem(at: url)
}
}

/**
Expand All @@ -113,49 +145,37 @@ public extension Path {
return try "".write(to: self)
}

/// Helper due to Linux Swift being incomplete.
private func _foo(go: () throws -> Void) throws {
#if !os(Linux)
/**
Creates the directory at this path.
- Note: Does not create any intermediary directories.
- Parameter options: Specify `mkdir(.p)` to create intermediary directories.
- Note: We do not error if the directory already exists (even without `.p`)
because *Path.swift* noops if the desired end result preexists.
- Returns: `self` to allow chaining.
*/
@discardableResult
func mkdir(_ options: MakeDirectoryOptions? = nil) throws -> Path {
do {
try go()
let wid = options == .p
try FileManager.default.createDirectory(at: self.url, withIntermediateDirectories: wid, attributes: nil)
} catch CocoaError.Code.fileWriteFileExists {
// noop
}
#else
do {
try go()
//noop (fails to trigger on Linux)
} catch {
#if os(Linux)
let error = error as NSError
guard error.domain == NSCocoaErrorDomain, error.code == CocoaError.Code.fileWriteFileExists.rawValue else {
throw error
}
}
#endif
}

/**
Creates the directory at this path.
- Note: Does not create any intermediary directories.
- Returns: `self` to allow chaining.
*/
@discardableResult
func mkdir() throws -> Path {
try _foo {
try FileManager.default.createDirectory(at: self.url, withIntermediateDirectories: false, attributes: nil)
#else
throw error
#endif
}
return self
}
}

/**
Creates the directory at this path.
- Note: Creates any intermediary directories, if required.
- Returns: `self` to allow chaining.
*/
@discardableResult
func mkpath() throws -> Path {
try _foo {
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
}
return self
}
/// Options for `Path.mkdir`
public enum MakeDirectoryOptions {
/// Creates intermediary directories. Works the same as mkdir -p.
case p
}
2 changes: 1 addition & 1 deletion Tests/PathTests/PathTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class PathTests: XCTestCase {
try Path.mktemp {
for _ in 0...1 {
try $0.join("a").mkdir()
try $0.join("b/c").mkpath()
try $0.join("b/c").mkdir(.p)
}
}
}
Expand Down

0 comments on commit 3beba13

Please sign in to comment.