Repository: apple/swift-nio
File: Sources/NIOPosix/NonBlockingFileIO.swift (Swift)

Code before:
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func unlink(path: String, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            try Posix.unlink(pathname: path)
        }
    }

    /// Creates directory at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the directory to be created.
    ///     - withIntermediateDirectories: Whether intermediate directories should be created.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func createDirectory(path: String, withIntermediateDirectories createIntermediates: Bool, mode: NIOPOSIXFileMode, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            if createIntermediates {
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
                try Posix.mkpath(pathname: path, mode: mode)
#else
                // ensure that intermediate directories exist
                let startIndex = path.startIndex
                var endIndex = path.index(after: startIndex)
                while endIndex != path.endIndex {
                    if path[endIndex] == "/" {
                        let intermediate = String(path[startIndex..<endIndex])
                        do {
                            try Posix.mkdir(pathname: intermediate, mode: S_IRWXU)
                        } catch let e as IOError where e.errnoCode == EEXIST {
                            // If intermediate item exists - continue
                        }
                    }
                    endIndex = path.index(after: endIndex)
                }
                try Posix.mkdir(pathname: path, mode: mode)
#endif
            } else {
                try Posix.mkdir(pathname: path, mode: mode)
            }
        }
    }

    /// List contents of the directory at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the directory to list the content of.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` containing the file list.
    public func listDirectory(path: String, eventLoop: EventLoop) -> EventLoopFuture<[String]> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {

Review comment (bug):
The path handling code here is at risk of being awkwardly buggy. For example, this doesn't correctly handle being called with an absolute path, instead creating a chain of relative paths from the current working directory.

Do we really need to add this feature right now (`withIntermediateDirectories`)? If we can avoid doing so, I'd prefer it.

Code after:
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func unlink(path: String, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            try Posix.unlink(pathname: path)
        }
    }

    /// List contents of the directory at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the directory to list the content of.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` containing the directory entries.
    public func listDirectory(path: String, eventLoop: EventLoop) -> EventLoopFuture<[NIODirectoryEntry]> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            let dir = try Posix.opendir(pathname: path)
            var entries: [NIODirectoryEntry] = []
            do {
                while let entry = try Posix.readdir(dir: dir) {
                    let name = withUnsafeBytes(of: entry.pointee.d_name) { pointer -> String in
                        let ptr = pointer.baseAddress!.assumingMemoryBound(to: CChar.self)
                        return String(cString: ptr)
                    }
                    entries.append(NIODirectoryEntry(ino: UInt64(entry.pointee.d_ino), type: entry.pointee.d_type, name: name))
                }
                try? Posix.closedir(dir: dir)
            } catch {
                try? Posix.closedir(dir: dir)
                throw error
            }
            return entries
        }
    }

    /// Renames the file at `path` to `newName` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the file to be renamed.
    ///     - newName: New file name.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func rename(path: String, newName: String, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            try Posix.rename(pathname: path, newName: newName)
        }
    }

    /// Removes the file at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters: