Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Sources/NIOFS/FileSystemProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,48 @@ extension FileSystemProtocol {
}
}
}

// MARK: - File attributes

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension FileSystemProtocol {
/// Sets the file's last access and last data modification times to the given times.
///
/// - Parameters:
/// - path: The path of the file to modify.
/// - lastAccess: The new value of the file's last access time, as time elapsed since the Epoch.
/// - lastDataModification: The new value of the file's last data modification time, as time elapsed since the Epoch.
public func setTimes(
forFileAt path: NIOFilePath,
lastAccess: FileInfo.Timespec?,
lastDataModification: FileInfo.Timespec?
) async throws {
try await self.withFileHandle(forReadingAt: path) { handle in
try await handle.setTimes(lastAccess: lastAccess, lastDataModification: lastDataModification)
}
}

/// Sets the file's last access time to the given time.
///
/// - Parameters:
/// - path: The path of the file to modify.
/// - time: The time to which the file's last access time should be set.
public func setLastAccessTime(
forFileAt path: NIOFilePath,
to time: FileInfo.Timespec
) async throws {
try await self.setTimes(forFileAt: path, lastAccess: time, lastDataModification: nil)
}

/// Sets the file's last data modification time to the given time.
///
/// - Parameters:
/// - path: The path of the file to modify.
/// - time: The time to which the file's last data modification time should be set.
public func setLastDataModificationTime(
forFileAt path: NIOFilePath,
to time: FileInfo.Timespec
) async throws {
try await self.setTimes(forFileAt: path, lastAccess: nil, lastDataModification: time)
}
}
49 changes: 47 additions & 2 deletions Sources/_NIOFileSystem/FileSystemProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public protocol FileSystemProtocol: Sendable {
/// destination of the symbolic link is returned.
/// - Returns: Information about the file at the given path or `nil` if no file exists.
func info(
forFileAt path: FilePath,
forFileAt path: NIOFilePath,
infoAboutSymbolicLink: Bool
) async throws -> FileInfo?

Expand Down Expand Up @@ -468,7 +468,7 @@ extension FileSystemProtocol {
/// - Parameters:
/// - path: The path to get information about.
/// - Returns: Information about the file at the given path or `nil` if no file exists.
public func info(forFileAt path: FilePath) async throws -> FileInfo? {
public func info(forFileAt path: NIOFilePath) async throws -> FileInfo? {
try await self.info(forFileAt: path, infoAboutSymbolicLink: false)
}

Expand Down Expand Up @@ -776,3 +776,48 @@ extension FileSystemProtocol {
}
}
}

// MARK: - File attributes

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension FileSystemProtocol {
/// Sets the file's last access and last data modification times to the given times.
///
/// - Parameters:
/// - path: The path of the file to modify.
/// - lastAccess: The new value of the file's last access time, as time elapsed since the Epoch.
/// - lastDataModification: The new value of the file's last data modification time, as time elapsed since the Epoch.
public func setTimes(
forFileAt path: NIOFilePath,
lastAccess: FileInfo.Timespec?,
lastDataModification: FileInfo.Timespec?
) async throws {
try await self.withFileHandle(forReadingAt: path) { handle in
try await handle.setTimes(lastAccess: lastAccess, lastDataModification: lastDataModification)
}
}

/// Sets the file's last access time to the given time.
///
/// - Parameters:
/// - path: The path of the file to modify.
/// - time: The time to which the file's last access time should be set.
public func setLastAccessTime(
forFileAt path: NIOFilePath,
to time: FileInfo.Timespec
) async throws {
try await self.setTimes(forFileAt: path, lastAccess: time, lastDataModification: nil)
}

/// Sets the file's last data modification time to the given time.
///
/// - Parameters:
/// - path: The path of the file to modify.
/// - time: The time to which the file's last data modification time should be set.
public func setLastDataModificationTime(
forFileAt path: NIOFilePath,
to time: FileInfo.Timespec
) async throws {
try await self.setTimes(forFileAt: path, lastAccess: nil, lastDataModification: time)
}
}
69 changes: 69 additions & 0 deletions Tests/NIOFSIntegrationTests/FileSystemTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,75 @@ final class FileSystemTests: XCTestCase {

var fs: FileSystem { .shared }


func testSetLastAccessAndDataModificationTimes() async throws {
#if os(Windows)
throw XCTSkip("setTimes is not available on Windows")
#endif
let path = try await self.fs.temporaryFilePath()
try await self.fs.withFileHandle(forWritingAt: path, options: .newFile(replaceExisting: false)) { _ in }

let originalInfo = try await self.fs.info(forFileAt: path)
let originalLastAccessTime = originalInfo?.lastAccessTime
let originalLastDataModificationTime = originalInfo?.lastDataModificationTime

let newAccessTime = FileInfo.Timespec(seconds: 10, nanoseconds: 5)
let newDataModTime = FileInfo.Timespec(seconds: 20, nanoseconds: 25)

try await self.fs.setTimes(forFileAt: path, lastAccess: newAccessTime, lastDataModification: newDataModTime)

let newInfo = try await self.fs.info(forFileAt: path)
XCTAssertEqual(newInfo?.lastAccessTime, newAccessTime)
XCTAssertEqual(newInfo?.lastDataModificationTime, newDataModTime)
XCTAssertNotEqual(newInfo?.lastAccessTime, originalLastAccessTime)
XCTAssertNotEqual(newInfo?.lastDataModificationTime, originalLastDataModificationTime)

try await self.fs.removeItem(at: path)
}

func testSetLastAccessTime() async throws {
#if os(Windows)
throw XCTSkip("setTimes is not available on Windows")
#endif
let path = try await self.fs.temporaryFilePath()
try await self.fs.withFileHandle(forWritingAt: path, options: .newFile(replaceExisting: false)) { _ in }

let originalInfo = try await self.fs.info(forFileAt: path)
let originalLastDataModificationTime = originalInfo?.lastDataModificationTime

let newAccessTime = FileInfo.Timespec(seconds: 10, nanoseconds: 5)
try await self.fs.setLastAccessTime(forFileAt: path, to: newAccessTime)

let newInfo = try await self.fs.info(forFileAt: path)
XCTAssertEqual(newInfo?.lastAccessTime, newAccessTime)
XCTAssertEqual(newInfo?.lastDataModificationTime, originalLastDataModificationTime)

try await self.fs.removeItem(at: path)
}

func testSetLastDataModificationTime() async throws {
#if os(Windows)
throw XCTSkip("setTimes is not available on Windows")
#endif
let path = try await self.fs.temporaryFilePath()
try await self.fs.withFileHandle(forWritingAt: path, options: .newFile(replaceExisting: false)) { _ in }

let originalInfo = try await self.fs.info(forFileAt: path)
let originalLastAccessTime = originalInfo?.lastAccessTime

let newDataModTime = FileInfo.Timespec(seconds: 20, nanoseconds: 25)
try await self.fs.setLastDataModificationTime(forFileAt: path, to: newDataModTime)

let newInfo = try await self.fs.info(forFileAt: path)
XCTAssertEqual(newInfo?.lastDataModificationTime, newDataModTime)
XCTAssertEqual(newInfo?.lastAccessTime, originalLastAccessTime)

try await self.fs.removeItem(at: path)
}




func testOpenFileForReading() async throws {
try await self.fs.withFileHandle(forReadingAt: .testDataReadme) { file in
let info = try await file.info()
Expand Down