Repository: apple/swift-nio
File: Sources/NIOFileSystem/FileHandleProtocol.swift (Swift)

Code before:
    func withUnsafeDescriptor<R: Sendable>(
        _ execute: @Sendable @escaping (FileDescriptor) throws -> R
    ) async throws -> R

    /// Detaches and returns the file descriptor from the handle.
    ///
    /// After detaching the file descriptor the handle is rendered invalid. All methods will throw
    /// an appropriate error if called. Detaching the descriptor yields ownerships to the caller.
    ///
    /// - Returns: The detached `FileDescriptor`
    func detachUnsafeFileDescriptor() throws -> FileDescriptor

    /// Closes the file handle.
    ///
    /// It is important to close a handle once it has been finished with to avoid leaking
    /// resources. Prefer using APIs which provided scoped access to a file handle which
    /// manage lifecycles on your behalf. Note that if the handle has been detached via
    /// ``detachUnsafeFileDescriptor()`` then it is not necessary to close the handle.
    ///
    /// After closing the handle calls to other functions will throw an appropriate error.
    func close() async throws

    /// Sets the file's last access and last data modification times to the given values.
    ///
    /// If **either** time is `nil`, the current value will not be changed.
    /// If **both** times are `nil`, then both times will be set to the current time.
    ///
    /// > Important: Times are only considered valid if their nanoseconds components are one of the following:
    /// > - `UTIME_NOW` (you can use ``FileInfo/Timespec/now`` to get a Timespec set to this value),
    /// > - `UTIME_OMIT` (you can use ``FileInfo/Timespec/omit`` to get a Timespec set to this value),,
    /// > - Greater than zero and no larger than 1000 million
    ///
    /// - Parameters:
    ///   - lastAccessTime: The new value of the file's last access time, as time elapsed since the Epoch.
    ///   - lastDataModificationTime: The new value of the file's last data modification time, as time elapsed since the Epoch.
    ///
    /// - Throws: If there's an error updating the times. If this happens, the original values won't be modified.
    func setTimes(
        lastAccess: FileInfo.Timespec?,
        lastDataModification: FileInfo.Timespec?
    ) async throws
}

// MARK: - Readable

/// A handle for reading data from an open file.
///
/// ``ReadableFileHandleProtocol`` refines ``FileHandleProtocol`` to add requirements for reading data from a file.
///
/// There are two requirements for implementing this protocol:
/// 1. ``readChunk(fromAbsoluteOffset:length:)``, and

Review comment (suggestion):
IMO ["clamp/update the value to the closest valid value"](https://github.com/apple/swift-nio/pull/2735/files/8222158a110c16d705f767fd3e53caeca910aa56..d3cf9efe57bff7a541f0edf24dcda48dd0d4962a#r1631408774) is good/fine, but the API design still bothers me.

I would have preferred these 2 lines to have been obvious in the API design.
Perhaps using an (structified) enum as the single parameter that the function accepts. Or anything better. Maybe splitting up the responsibility into 2/3 different functions.

Code after:
    func withUnsafeDescriptor<R: Sendable>(
        _ execute: @Sendable @escaping (FileDescriptor) throws -> R
    ) async throws -> R

    /// Detaches and returns the file descriptor from the handle.
    ///
    /// After detaching the file descriptor the handle is rendered invalid. All methods will throw
    /// an appropriate error if called. Detaching the descriptor yields ownerships to the caller.
    ///
    /// - Returns: The detached `FileDescriptor`
    func detachUnsafeFileDescriptor() throws -> FileDescriptor

    /// Closes the file handle.
    ///
    /// It is important to close a handle once it has been finished with to avoid leaking
    /// resources. Prefer using APIs which provided scoped access to a file handle which
    /// manage lifecycles on your behalf. Note that if the handle has been detached via
    /// ``detachUnsafeFileDescriptor()`` then it is not necessary to close the handle.
    ///
    /// After closing the handle calls to other functions will throw an appropriate error.
    func close() async throws

    /// Sets the file's last access and last data modification times to the given values.
    ///
    /// If **either** time is `nil`, the current value will not be changed.
    /// If **both** times are `nil`, then both times will be set to the current time.
    ///
    /// > Important: Times are only considered valid if their nanoseconds components are one of the following:
    /// > - `UTIME_NOW` (you can use ``FileInfo/Timespec/now`` to get a Timespec set to this value),
    /// > - `UTIME_OMIT` (you can use ``FileInfo/Timespec/omit`` to get a Timespec set to this value),
    /// > - Greater than zero and no larger than 1000 million: if outside of this range, the value will be clamped to the closest valid value.
    /// > The seconds component must also be positive: if it's not, zero will be used as the value instead.
    ///
    /// - Parameters:
    ///   - lastAccessTime: The new value of the file's last access time, as time elapsed since the Epoch.
    ///   - lastDataModificationTime: The new value of the file's last data modification time, as time elapsed since the Epoch.
    ///
    /// - Throws: If there's an error updating the times. If this happens, the original values won't be modified.
    func setTimes(
        lastAccess: FileInfo.Timespec?,
        lastDataModification: FileInfo.Timespec?
    ) async throws
}

// MARK: - Readable

/// A handle for reading data from an open file.
///
/// ``ReadableFileHandleProtocol`` refines ``FileHandleProtocol`` to add requirements for reading data from a file.
///
/// There are two requirements for implementing this protocol: