Fixes & increased code coverage

This commit is contained in:
Max Howell
2019-01-31 11:21:22 -05:00
parent ab9a70e947
commit 62ea0d47b3
7 changed files with 182 additions and 19 deletions

View File

@@ -1,34 +1,35 @@
import Foundation
//#if os(Linux)
//import func Glibc.chmod
//#endif
public extension Path {
//MARK: Filesystem Attributes
/**
Returns the creation-time of the file.
- Note: Returns UNIX-time-zero if there is no creation-time, this should only happen if the file doesnt exist.
- Note: Returns `nil` if there is no creation-time, this should only happen if the file doesnt exist.
- Important: On Linux this is filesystem dependendent and may not exist.
*/
var ctime: Date {
var ctime: Date? {
do {
let attrs = try FileManager.default.attributesOfItem(atPath: string)
return attrs[.creationDate] as? Date ?? Date(timeIntervalSince1970: 0)
return attrs[.creationDate] as? Date
} catch {
//TODO log error
return Date(timeIntervalSince1970: 0)
return nil
}
}
/**
Returns the modification-time of the file.
- Note: Returns the creation time if there is no modification time.
- Note: Returns UNIX-time-zero if neither are available, this should only happen if the file doesnt exist.
- Note: If this returns `nil` and the file exists, something is very wrong.
*/
var mtime: Date {
var mtime: Date? {
do {
let attrs = try FileManager.default.attributesOfItem(atPath: string)
return attrs[.modificationDate] as? Date ?? ctime
return attrs[.modificationDate] as? Date
} catch {
//TODO log error
return Date(timeIntervalSince1970: 0)
return nil
}
}
@@ -39,27 +40,40 @@ public extension Path {
*/
@discardableResult
func chmod(_ octal: Int) throws -> Path {
// #if os(Linux)
// Glibc.chmod(string, __mode_t(octal))
// #else
try FileManager.default.setAttributes([.posixPermissions: octal], ofItemAtPath: string)
// #endif
return self
}
/// - Note: If file is already locked, does nothing
/// - Note: If file doesnt exist, throws
/**
- Note: If file is already locked, does nothing.
- Note: If file doesnt exist, throws.
- Important: On Linux does nothing.
*/
@discardableResult
func lock() throws -> Path {
#if !os(Linux)
var attrs = try FileManager.default.attributesOfItem(atPath: string)
let b = attrs[.immutable] as? Bool ?? false
if !b {
attrs[.immutable] = true
try FileManager.default.setAttributes(attrs, ofItemAtPath: string)
}
#endif
return self
}
/// - Note: If file isnt locked, does nothing
/// - Note: If file doesnt exist, does nothing
/**
- Note: If file isnt locked, does nothing.
- Note: If file doesnt exist, does nothing.
- Important: On Linux does nothing.
*/
@discardableResult
func unlock() throws -> Path {
#if !os(Linux)
var attrs: [FileAttributeKey: Any]
do {
attrs = try FileManager.default.attributesOfItem(atPath: string)
@@ -71,6 +85,7 @@ public extension Path {
attrs[.immutable] = false
try FileManager.default.setAttributes(attrs, ofItemAtPath: string)
}
#endif
return self
}
}

View File

@@ -130,6 +130,9 @@ public extension Path {
Deletes the path, recursively if a directory.
- Note: noop: if the path doesnt exist
*Path.swift* doesnt error if desired end result preexists.
- Note: On UNIX will this function will succeed if the parent directory is writable and the current user has permission.
- Note: This function will fail if the file or directory is locked
- SeeAlso: `lock()`
*/
@inlinable
func delete() throws {

View File

@@ -10,6 +10,6 @@ extension Path: CustomStringConvertible {
extension Path: CustomDebugStringConvertible {
/// Returns eg. `Path(string: "/foo")`
public var debugDescription: String {
return "Path(string: \(string))"
return "Path(\(string))"
}
}

View File

@@ -1,4 +1,9 @@
import Foundation
#if os(Linux)
import func Glibc.access
#else
import func Darwin.access
#endif
public extension Path {
//MARK: Filesystem Properties
@@ -32,11 +37,22 @@ public extension Path {
/// Returns true if the path represents an actual file that is also deletable by the current user.
var isDeletable: Bool {
return FileManager.default.isDeletableFile(atPath: string)
#if os(Linux) && !swift(>=5.1)
return exists && access(parent.string, W_OK) == 0
#else
// FileManager.isDeletableFile returns true if there is *not* a file there
return exists && FileManager.default.isDeletableFile(atPath: string)
#endif
}
/// Returns true if the path represents an actual file that is also executable by the current user.
var isExecutable: Bool {
return FileManager.default.isExecutableFile(atPath: string)
if access(string, X_OK) == 0 {
// FileManager.isExxecutableFile returns true even if there is *not*
// a file there *but* if there was it could be *made* executable
return FileManager.default.isExecutableFile(atPath: string)
} else {
return false
}
}
}