Spaces:
Sleeping
Sleeping
File size: 4,019 Bytes
6ee917b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import Foundation
extension FileHandle {
/// Check if the file was deleted on the file system. Linux keep the file alive, as long as some processes have it open.
public func wasDeleted() -> Bool {
// This field contains the number of hard links to the file.
return fileStats().st_nlink == 0
}
public func fileSize() -> Int {
return Int(fileStats().st_size)
}
public func fileSizeAndModificationTime() -> (size: Int, modificationTime: Date, creationTime: Date) {
let stats = fileStats()
return (Int(stats.st_size), stats.modificationTime, stats.creationTime)
}
/// Return file `stat` structure
public func fileStats() -> stat {
var stats = stat()
guard fstat(fileDescriptor, &stats) != -1 else {
let error = String(cString: strerror(errno))
fatalError("fstat failed on open file descriptor. Error \(errno) \(error)")
}
return stats
}
}
public enum FileHandleError: Error {
case cannotMoveFile(from: String, to: String, errno: Int32, error: String)
}
extension FileManager {
/// Rename file and replace if `to` already exists. https://www.gnu.org/software/libc/manual/html_node/Renaming-Files.html
public func moveFileOverwrite(from: String, to: String) throws {
guard rename(from, to) != -1 else {
let error = String(cString: strerror(errno))
throw FileHandleError.cannotMoveFile(from: from, to: to, errno: errno, error: error)
}
}
public func removeItemIfExists(at: String) throws {
if fileExists(atPath: at) {
try removeItem(atPath: at)
}
}
/// Return file `stat` structure
public func fileStats(at: String) -> stat? {
var stats = stat()
let ret = stat(at, &stats)
guard ret != -1 else {
if errno == 2 {
// No such file or directory
return nil
}
let error = String(cString: strerror(errno))
fatalError("fstat failed on open file descriptor. Error \(errno) \(error), ret=\(ret)")
}
return stats
}
/// Get modification and creation time
public func fileSizeAndModificationTime(at: String) -> (size: Int, modificationTime: Date, creationTime: Date)? {
guard let stats = fileStats(at: at) else {
return nil
}
return (Int(stats.st_size), stats.modificationTime, stats.creationTime)
}
/// Wait until the file was not updated for at least 60 seconds. If the file does not exist, do nothing
public func waitIfFileWasRecentlyModified(at: String, waitTimeMinutes: Int = 15) {
// Wait up to 15 minutes
for _ in 0 ..< (waitTimeMinutes*6) {
guard let mTime = FileManager.default.fileStats(at: at)?.modificationTime,
mTime > Date().addingTimeInterval(-60) else {
break
}
print("Another process is writing to \(at). Check in 10s. Waiting up to \(waitTimeMinutes) minutes.")
sleep(10)
}
}
}
extension stat {
/// Last modification time of the file
public var modificationTime: Date {
#if os(Linux)
let seconds = Double(st_mtim.tv_sec)
let nanosends = Double(st_mtim.tv_nsec)
#else
let seconds = Double(st_mtimespec.tv_sec)
let nanosends = Double(st_mtimespec.tv_nsec)
#endif
return Date(timeIntervalSince1970: seconds + nanosends / 1_000_000)
}
/// Creation time of the file / inode
public var creationTime: Date {
#if os(Linux)
let seconds = Double(st_ctim.tv_sec)
let nanosends = Double(st_ctim.tv_nsec)
#else
let seconds = Double(st_ctimespec.tv_sec)
let nanosends = Double(st_ctimespec.tv_nsec)
#endif
return Date(timeIntervalSince1970: seconds + nanosends / 1_000_000)
}
}
|