diff --git a/README.md b/README.md index 0b3b77d..4fe922a 100644 --- a/README.md +++ b/README.md @@ -26,27 +26,43 @@ let connection = SSHConnection( try await connection.start() ``` -Once connected, you can start executing concrete SSH operations on child communication channels. As `SSH Client` means to be a high level interface, you do not directly interact with them. Instead you use interfaces dedicated to your use case. +Once connected, you can start executing concrete SSH operations on child communication channels. +As `SSH Client` means to be a high level interface, you do not directly interact with them. +Instead you use interfaces dedicated to your use case. + +- SSH commands +```swift +let response = try await connection.execute("echo Hello\n") +// Handle response + +for try await chunk in connection.stream("echo World\n") { + // Handle chunk +} +``` - SSH shell ```swift let shell = try await connection.requestShell() for try await chunk in shell.data { - // ... + // Handle chunk } ``` - SFTP client ```swift let sftpClient = try await connection.requestSFTPClient() -// sftp operations -``` -- SSH commands -```swift -let response = try await connection.execute("echo Hello\n") -// Handle response -``` +// directories +try await sftpClient.createDirectory(at: "./new") +try await sftpClient.removeDirectory(at: "./new") + +// files +let file = try await client.openFile(at: "./new/file.txt", flags: .create) +try await file.write("Hello World!".data(using: .utf8)!) +try await file.close() + +// and more +``` You keep track of the connection state, using the dedicated `stateUpdateHandler` property: ```swift diff --git a/Sources/SSHClient/Async/SFTPClient+Async.swift b/Sources/SSHClient/Async/SFTPClient+Async.swift index 8f1d1be..be19ec0 100644 --- a/Sources/SSHClient/Async/SFTPClient+Async.swift +++ b/Sources/SSHClient/Async/SFTPClient+Async.swift @@ -30,12 +30,12 @@ public extension SFTPFile { } public extension SFTPClient { - func openFile(filePath: String, + func openFile(at filePath: SFTPFilePath, flags: SFTPOpenFileFlags, attributes: SFTPFileAttributes = .none) async throws -> SFTPFile { try await withCheckedResultContinuation { completion in openFile( - filePath: filePath, + at: filePath, flags: flags, attributes: attributes, completion: completion @@ -43,13 +43,13 @@ public extension SFTPClient { } } - func withFile(filePath: String, + func withFile(at filePath: SFTPFilePath, flags: SFTPOpenFileFlags, attributes: SFTPFileAttributes = .none, _ closure: @escaping (SFTPFile) async -> Void) async throws { try await withCheckedResultContinuation { completion in withFile( - filePath: filePath, + at: filePath, flags: flags, attributes: attributes, { file, close in Task { @@ -62,41 +62,41 @@ public extension SFTPClient { } } - func listDirectory(atPath path: String) async throws -> [SFTPPathComponent] { + func listDirectory(at path: SFTPFilePath) async throws -> [SFTPPathComponent] { try await withCheckedResultContinuation { completion in - listDirectory(atPath: path, completion: completion) + listDirectory(at: path, completion: completion) } } - func getAttributes(at filePath: String) async throws -> SFTPFileAttributes { + func getAttributes(at filePath: SFTPFilePath) async throws -> SFTPFileAttributes { try await withCheckedResultContinuation { completion in getAttributes(at: filePath, completion: completion) } } - func createDirectory(atPath path: String, + func createDirectory(at path: SFTPFilePath, attributes: SFTPFileAttributes = .none) async throws { try await withCheckedResultContinuation { completion in - createDirectory(atPath: path, attributes: attributes, completion: completion) + createDirectory(at: path, attributes: attributes, completion: completion) } } - func moveItem(atPath current: String, - toPath destination: String) async throws { + func moveItem(at current: SFTPFilePath, + to destination: SFTPFilePath) async throws { try await withCheckedResultContinuation { completion in - moveItem(atPath: current, toPath: destination, completion: completion) + moveItem(at: current, to: destination, completion: completion) } } - func removeDirectory(atPath path: String) async throws { + func removeDirectory(at path: SFTPFilePath) async throws { try await withCheckedResultContinuation { completion in - removeDirectory(atPath: path, completion: completion) + removeDirectory(at: path, completion: completion) } } - func removeFile(atPath path: String) async throws { + func removeFile(at path: SFTPFilePath) async throws { try await withCheckedResultContinuation { completion in - removeFile(atPath: path, completion: completion) + removeFile(at: path, completion: completion) } } diff --git a/Sources/SSHClient/Internal/SFTP/SFTPChannel.swift b/Sources/SSHClient/Internal/SFTP/SFTPChannel.swift index e3e5f6a..0ce1030 100644 --- a/Sources/SSHClient/Internal/SFTP/SFTPChannel.swift +++ b/Sources/SSHClient/Internal/SFTP/SFTPChannel.swift @@ -15,12 +15,12 @@ protocol SFTPChannel { func readFile(_ file: SFTPMessage.ReadFile.Payload) -> Future func writeFile(_ file: SFTPMessage.WriteFile.Payload) -> Future func mkdir(_ dir: SFTPMessage.MkDir.Payload) -> Future - func rmdir(path: String) -> Future - func rmFile(path: String) -> Future + func rmdir(path: SFTPFilePath) -> Future + func rmFile(path: SFTPFilePath) -> Future func readDir(_ handle: SFTPFileHandle) -> Future - func openDir(path: String) -> Future - func realpath(path: String) -> Future - func stat(path: String) -> Future + func openDir(path: SFTPFilePath) -> Future + func realpath(path: SFTPFilePath) -> Future + func stat(path: SFTPFilePath) -> Future func rename(_ payload: SFTPMessage.Rename.Payload) -> Future } @@ -118,7 +118,7 @@ class IOSFTPChannel: SFTPChannel { } } - func rmdir(path: String) -> Future { + func rmdir(path: SFTPFilePath) -> Future { allocateRequestID().flatMap { id in self.send(.rmdir(.init(requestId: id, filePath: path))) } @@ -127,7 +127,7 @@ class IOSFTPChannel: SFTPChannel { } } - func rmFile(path: String) -> Future { + func rmFile(path: SFTPFilePath) -> Future { allocateRequestID().flatMap { id in self.send(.remove(.init(requestId: id, filename: path))) } @@ -157,7 +157,7 @@ class IOSFTPChannel: SFTPChannel { } } - func openDir(path: String) -> Future { + func openDir(path: SFTPFilePath) -> Future { allocateRequestID().flatMap { id in self.send(.opendir(.init(requestId: id, path: path))) } @@ -171,7 +171,7 @@ class IOSFTPChannel: SFTPChannel { } } - func realpath(path: String) -> Future { + func realpath(path: SFTPFilePath) -> Future { allocateRequestID().flatMap { id in self.send(.realpath(.init(requestId: id, path: path))) } @@ -185,7 +185,7 @@ class IOSFTPChannel: SFTPChannel { } } - func stat(path: String) -> Future { + func stat(path: SFTPFilePath) -> Future { allocateRequestID().flatMap { id in self.send(.stat(.init(requestId: id, path: path))) } diff --git a/Sources/SSHClient/Internal/SFTP/SFTPMessage.swift b/Sources/SSHClient/Internal/SFTP/SFTPMessage.swift index dd1a801..2d0b7ac 100644 --- a/Sources/SSHClient/Internal/SFTP/SFTPMessage.swift +++ b/Sources/SSHClient/Internal/SFTP/SFTPMessage.swift @@ -8,11 +8,11 @@ typealias SFTPFileHandle = ByteBuffer typealias SFTPRequestID = UInt32 public struct SFTPPathComponent: Sendable { - public let filename: String + public let filename: SFTPFilePath public let longname: String public let attributes: SFTPFileAttributes - init(filename: String, longname: String, attributes: SFTPFileAttributes) { + init(filename: SFTPFilePath, longname: String, attributes: SFTPFileAttributes) { self.filename = filename self.longname = longname self.attributes = attributes @@ -125,7 +125,7 @@ enum SFTPMessage { struct OpenFile { struct Payload { // Called `filename` in spec - var filePath: String + var filePath: SFTPFilePath var pFlags: SFTPOpenFileFlags var attributes: SFTPFileAttributes } @@ -184,7 +184,7 @@ enum SFTPMessage { struct Remove { let requestId: SFTPRequestID - var filename: String + var filename: SFTPFilePath } struct FileSetStat { @@ -199,7 +199,7 @@ enum SFTPMessage { struct SetStat { struct Payload { - var path: String + var path: SFTPFilePath var attributes: SFTPFileAttributes } @@ -209,8 +209,8 @@ enum SFTPMessage { struct Symlink { struct Payload { - var linkPath: String - var targetPath: String + var linkPath: SFTPFilePath + var targetPath: SFTPFilePath } let requestId: SFTPRequestID @@ -219,7 +219,7 @@ enum SFTPMessage { struct Readlink { let requestId: SFTPRequestID - var path: String + var path: SFTPFilePath } struct FileData { @@ -229,7 +229,7 @@ enum SFTPMessage { struct MkDir { struct Payload { - let filePath: String + let filePath: SFTPFilePath let attributes: SFTPFileAttributes } @@ -239,29 +239,29 @@ enum SFTPMessage { struct RmDir { let requestId: SFTPRequestID - var filePath: String + var filePath: SFTPFilePath } struct OpenDir { let requestId: SFTPRequestID - var path: String + var path: SFTPFilePath } struct Stat { static let id = SFTPMessageType.stat let requestId: SFTPRequestID - var path: String + var path: SFTPFilePath } struct LStat { let requestId: SFTPRequestID - var path: String + var path: SFTPFilePath } struct RealPath { let requestId: SFTPRequestID - var path: String + var path: SFTPFilePath } struct Name { @@ -282,8 +282,8 @@ enum SFTPMessage { struct Rename { struct Payload { - let oldPath: String - let newPath: String + let oldPath: SFTPFilePath + let newPath: SFTPFilePath } let requestId: SFTPRequestID diff --git a/Sources/SSHClient/Internal/SFTP/SFTPMessageParser.swift b/Sources/SSHClient/Internal/SFTP/SFTPMessageParser.swift index 9bbfb0a..7395041 100644 --- a/Sources/SSHClient/Internal/SFTP/SFTPMessageParser.swift +++ b/Sources/SSHClient/Internal/SFTP/SFTPMessageParser.swift @@ -60,7 +60,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { .init( requestId: requestId, payload: .init( - filePath: filePath, + filePath: SFTPFilePath(filePath), pFlags: SFTPOpenFileFlags(rawValue: pFlags), attributes: attributes ) @@ -172,7 +172,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .stat( .init( requestId: requestId, - path: path + path: SFTPFilePath(path) ) ) case .mkdir: @@ -187,7 +187,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { .init( requestId: requestId, payload: .init( - filePath: path, + filePath: SFTPFilePath(path), attributes: attributes ) ) @@ -202,7 +202,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .rmdir( .init( requestId: requestId, - filePath: path + filePath: SFTPFilePath(path) ) ) case .lstat: @@ -215,7 +215,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .lstat( .init( requestId: requestId, - path: path + path: SFTPFilePath(path) ) ) case .realpath: @@ -228,7 +228,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .realpath( .init( requestId: requestId, - path: path + path: SFTPFilePath(path) ) ) case .opendir: @@ -241,7 +241,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .opendir( .init( requestId: requestId, - path: handle + path: SFTPFilePath(handle) ) ) case .readdir: @@ -288,7 +288,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { } components.append( .init( - filename: filename, + filename: SFTPFilePath(filename), longname: longname, attributes: attributes ) @@ -323,7 +323,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .remove( .init( requestId: requestId, - filename: filename + filename: SFTPFilePath(filename) ) ) case .setstat: @@ -339,7 +339,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { .init( requestId: requestId, payload: .init( - path: path, + path: SFTPFilePath(path), attributes: attributes ) ) @@ -373,7 +373,7 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .readlink( .init( requestId: requestId, - path: path + path: SFTPFilePath(path) ) ) case .symlink: @@ -389,8 +389,8 @@ struct SFTPMessageParser: ByteToMessageDecoder { .init( requestId: requestId, payload: .init( - linkPath: linkPath, - targetPath: targetPath + linkPath: SFTPFilePath(linkPath), + targetPath: SFTPFilePath(targetPath) ) ) ) @@ -406,8 +406,8 @@ struct SFTPMessageParser: ByteToMessageDecoder { message = .rename(.init( requestId: requestId, payload: .init( - oldPath: oldPath, - newPath: newPath + oldPath: SFTPFilePath(oldPath), + newPath: SFTPFilePath(newPath) ) )) case .extended, .extendedReply: diff --git a/Sources/SSHClient/Internal/SFTP/SFTPSerializer.swift b/Sources/SSHClient/Internal/SFTP/SFTPSerializer.swift index c2cf64d..df148b5 100644 --- a/Sources/SSHClient/Internal/SFTP/SFTPSerializer.swift +++ b/Sources/SSHClient/Internal/SFTP/SFTPSerializer.swift @@ -18,7 +18,7 @@ final class SFTPMessageSerializer: MessageToByteEncoder { } case .openFile(let openFile): out.writeInteger(openFile.requestId) - out.writeSSHString(openFile.payload.filePath) + out.writeSSHString(openFile.payload.filePath.encode()) out.writeInteger(openFile.payload.pFlags.rawValue) out.writeSFTPFileAttributes(openFile.payload.attributes) case .closeFile(var closeFile): @@ -47,34 +47,34 @@ final class SFTPMessageSerializer: MessageToByteEncoder { out.writeSSHString(&data.data) case .mkdir(let mkdir): out.writeInteger(mkdir.requestId) - out.writeSSHString(mkdir.payload.filePath) + out.writeSSHString(mkdir.payload.filePath.encode()) out.writeSFTPFileAttributes(mkdir.payload.attributes) case .rmdir(let rmdir): out.writeInteger(rmdir.requestId) - out.writeSSHString(rmdir.filePath) + out.writeSSHString(rmdir.filePath.encode()) case .stat(let stat): out.writeInteger(stat.requestId) - out.writeSSHString(stat.path) + out.writeSSHString(stat.path.encode()) case .lstat(let lstat): out.writeInteger(lstat.requestId) - out.writeSSHString(lstat.path) + out.writeSSHString(lstat.path.encode()) case .attributes(let fstat): out.writeInteger(fstat.requestId) out.writeSFTPFileAttributes(fstat.attributes) case .realpath(let realPath): out.writeInteger(realPath.requestId) - out.writeSSHString(realPath.path) + out.writeSSHString(realPath.path.encode()) case .name(let name): out.writeInteger(name.requestId) out.writeInteger(name.count) for component in name.components { - out.writeSSHString(component.filename) + out.writeSSHString(component.filename.encode()) out.writeSSHString(component.longname) out.writeSFTPFileAttributes(component.attributes) } case .opendir(let opendir): out.writeInteger(opendir.requestId) - out.writeSSHString(opendir.path) + out.writeSSHString(opendir.path.encode()) case .readdir(var readdir): out.writeInteger(readdir.requestId) out.writeSSHString(&readdir.handle) @@ -83,22 +83,22 @@ final class SFTPMessageSerializer: MessageToByteEncoder { out.writeSSHString(&fstat.handle) case .remove(let remove): out.writeInteger(remove.requestId) - out.writeSSHString(remove.filename) + out.writeSSHString(remove.filename.encode()) case .fsetstat(var fsetstat): out.writeSSHString(&fsetstat.payload.handle) out.writeSFTPFileAttributes(fsetstat.payload.attributes) case .setstat(let setstat): - out.writeSSHString(setstat.payload.path) + out.writeSSHString(setstat.payload.path.encode()) out.writeSFTPFileAttributes(setstat.payload.attributes) case .symlink(let symlink): - out.writeSSHString(symlink.payload.linkPath) - out.writeSSHString(symlink.payload.targetPath) + out.writeSSHString(symlink.payload.linkPath.encode()) + out.writeSSHString(symlink.payload.targetPath.encode()) case .readlink(let readlink): - out.writeSSHString(readlink.path) + out.writeSSHString(readlink.path.encode()) case .rename(let rename): out.writeInteger(rename.requestId) - out.writeSSHString(rename.payload.oldPath) - out.writeSSHString(rename.payload.newPath) + out.writeSSHString(rename.payload.oldPath.encode()) + out.writeSSHString(rename.payload.newPath.encode()) } let length = out.writerIndex - lengthIndex - 4 out.setInteger(UInt32(length), at: lengthIndex) diff --git a/Sources/SSHClient/SFTPClient.swift b/Sources/SSHClient/SFTPClient.swift index 3216e6c..33b25b5 100644 --- a/Sources/SSHClient/SFTPClient.swift +++ b/Sources/SSHClient/SFTPClient.swift @@ -46,7 +46,7 @@ public final class SFTPClient: @unchecked Sendable, SSHSession { public var closeHandler: ((SFTPClientError?) -> Void)? - public func listDirectory(atPath path: String, + public func listDirectory(at path: SFTPFilePath, completion: @escaping ((Result<[SFTPPathComponent], Error>) -> Void)) { let newPath = recursivelyExecute( { path in @@ -81,14 +81,14 @@ public final class SFTPClient: @unchecked Sendable, SSHSession { .whenComplete(on: updateQueue, completion) } - public func getAttributes(at filePath: String, + public func getAttributes(at filePath: SFTPFilePath, completion: @escaping ((Result) -> Void)) { sftpChannel.stat(path: filePath) .map { $0.attributes } .whenComplete(on: updateQueue, completion) } - public func openFile(filePath: String, + public func openFile(at filePath: SFTPFilePath, flags: SFTPOpenFileFlags, attributes: SFTPFileAttributes = .none, updateQueue: DispatchQueue = .main, @@ -110,13 +110,13 @@ public final class SFTPClient: @unchecked Sendable, SSHSession { .whenComplete(on: updateQueue, completion) } - public func withFile(filePath: String, + public func withFile(at filePath: SFTPFilePath, flags: SFTPOpenFileFlags, attributes: SFTPFileAttributes = .none, _ closure: @escaping (SFTPFile, @escaping () -> Void) -> Void, completion: @escaping (Result) -> Void) { openFile( - filePath: filePath, + at: filePath, flags: flags, attributes: attributes, updateQueue: .global(qos: .utility) @@ -132,7 +132,7 @@ public final class SFTPClient: @unchecked Sendable, SSHSession { } } - public func createDirectory(atPath path: String, + public func createDirectory(at path: SFTPFilePath, attributes: SFTPFileAttributes = .none, completion: @escaping ((Result) -> Void)) { let message = SFTPMessage.MkDir.Payload( @@ -144,8 +144,8 @@ public final class SFTPClient: @unchecked Sendable, SSHSession { .whenComplete(on: updateQueue, completion) } - public func moveItem(atPath current: String, - toPath destination: String, + public func moveItem(at current: SFTPFilePath, + to destination: SFTPFilePath, completion: @escaping ((Result) -> Void)) { let message = SFTPMessage.Rename.Payload(oldPath: current, newPath: destination) return sftpChannel.rename(message) @@ -153,14 +153,14 @@ public final class SFTPClient: @unchecked Sendable, SSHSession { .whenComplete(on: updateQueue, completion) } - public func removeDirectory(atPath path: String, + public func removeDirectory(at path: SFTPFilePath, completion: @escaping ((Result) -> Void)) { sftpChannel.rmdir(path: path) .mapAsVoid() .whenComplete(on: updateQueue, completion) } - public func removeFile(atPath path: String, + public func removeFile(at path: SFTPFilePath, completion: @escaping ((Result) -> Void)) { sftpChannel .rmFile(path: path) diff --git a/Sources/SSHClient/SFTPFile.swift b/Sources/SSHClient/SFTPFile.swift index 9a529a1..993d086 100644 --- a/Sources/SSHClient/SFTPFile.swift +++ b/Sources/SSHClient/SFTPFile.swift @@ -5,14 +5,14 @@ public final class SFTPFile: @unchecked Sendable { private var isActive: Bool private let handle: SFTPFileHandle - private let path: String + private let path: SFTPFilePath private let channel: SFTPChannel private let updateQueue: DispatchQueue // MARK: - Life Cycle init(channel: SFTPChannel, - path: String, + path: SFTPFilePath, handle: SFTPFileHandle, updateQueue: DispatchQueue) { isActive = true diff --git a/Sources/SSHClient/SFTPFilePath.swift b/Sources/SSHClient/SFTPFilePath.swift new file mode 100644 index 0000000..2611c8c --- /dev/null +++ b/Sources/SSHClient/SFTPFilePath.swift @@ -0,0 +1,28 @@ + +import Foundation + +public struct SFTPFilePath: Sendable, Hashable { + private let stringValue: String + + public init(_ string: String) { + stringValue = string + } + + // MARK: - Public + + public var string: String { + stringValue + } + + // MARK: - Internal + + func encode() -> String { + stringValue + } +} + +extension SFTPFilePath: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.init(value) + } +} diff --git a/Tests/SSHClientTests/SFTPTests.swift b/Tests/SSHClientTests/SFTPTests.swift index 7da9b30..5cf4cf1 100644 --- a/Tests/SSHClientTests/SFTPTests.swift +++ b/Tests/SSHClientTests/SFTPTests.swift @@ -37,7 +37,7 @@ class SFTPTests: XCTestCase { let path = sftpServer.preferredWorkingDirectoryPath(components: "new") let exp = XCTestExpectation() client.createDirectory( - atPath: path + at: SFTPFilePath(path) ) { result in XCTAssertTrue(result.isSuccess) XCTAssertTrue(self.sftpServer.fileExists(atPath: path)) @@ -52,10 +52,10 @@ class SFTPTests: XCTestCase { try sftpServer.createDirectory(atPath: path) let files = sftpServer.createRandomFiles(atPath: path, count: 10) let exp = XCTestExpectation() - client.listDirectory(atPath: path) { result in + client.listDirectory(at: SFTPFilePath(path)) { result in switch result { case .success(let content): - let received = content.map { $0.filename } + let received = content.map { $0.filename }.map { $0.encode() } XCTAssertEqual( received.sorted(), (files + [".", ".."]).sorted() @@ -77,7 +77,7 @@ class SFTPTests: XCTestCase { let exp1 = XCTestExpectation() let inner1 = XCTestExpectation() inner1.isInverted = true - client.withFile(filePath: path, flags: []) { _, completion in + client.withFile(at: SFTPFilePath(path), flags: []) { _, completion in inner1.fulfill() completion() } completion: { result in @@ -89,7 +89,7 @@ class SFTPTests: XCTestCase { // with create let exp = XCTestExpectation() let inner = XCTestExpectation() - client.withFile(filePath: path, flags: [.create]) { _, completion in + client.withFile(at: SFTPFilePath(path), flags: [.create]) { _, completion in inner.fulfill() completion() } completion: { result in @@ -114,7 +114,7 @@ class SFTPTests: XCTestCase { let exp = XCTestExpectation() let inner = XCTestExpectation() inner.isInverted = true - client.withFile(filePath: path, flags: [.create, .forceCreate]) { _, completion in + client.withFile(at: SFTPFilePath(path), flags: [.create, .forceCreate]) { _, completion in inner.fulfill() completion() } completion: { result in @@ -130,7 +130,7 @@ class SFTPTests: XCTestCase { sftpServer.createFile(atPath: path, contents: "hello.world".data(using: .utf8)!) let exp = XCTestExpectation() let inner = XCTestExpectation() - client.withFile(filePath: path, flags: [.truncate]) { _, completion in + client.withFile(at: SFTPFilePath(path), flags: [.truncate]) { _, completion in inner.fulfill() completion() } completion: { result in @@ -155,7 +155,7 @@ class SFTPTests: XCTestCase { sftpServer.createFile(atPath: path, contents: fileContent) let exp = XCTestExpectation() let inner = XCTestExpectation() - client.withFile(filePath: path, flags: [.read]) { file, completion in + client.withFile(at: SFTPFilePath(path), flags: [.read]) { file, completion in let group = DispatchGroup() // all group.enter() @@ -204,7 +204,7 @@ class SFTPTests: XCTestCase { let exp = XCTestExpectation() let inner = XCTestExpectation() client.withFile( - filePath: path, + at: SFTPFilePath(path), flags: .write, { file, completion in let first = "helloworld".data(using: .utf8)! @@ -243,7 +243,7 @@ class SFTPTests: XCTestCase { let exp = XCTestExpectation() let inner = XCTestExpectation() client.withFile( - filePath: path, + at: SFTPFilePath(path), flags: [.write, .append], { file, completion in file.write( @@ -274,7 +274,7 @@ class SFTPTests: XCTestCase { // read let expRead = XCTestExpectation() let innerRead = XCTestExpectation() - client.withFile(filePath: path, flags: .read) { file, completion in + client.withFile(at: SFTPFilePath(path), flags: .read) { file, completion in file.write("data".data(using: .utf8)!) { result in XCTAssertTrue(result.isFailure) completion() @@ -288,7 +288,7 @@ class SFTPTests: XCTestCase { // write let expWrite = XCTestExpectation() let innerWrite = XCTestExpectation() - client.withFile(filePath: path, flags: .write) { file, completion in + client.withFile(at: SFTPFilePath(path), flags: .write) { file, completion in file.read { result in XCTAssertTrue(result.isFailure) completion() @@ -312,7 +312,7 @@ class SFTPTests: XCTestCase { let modificationDate = sftpServer.itemModificationDate(atPath: path) // from client let exp = XCTestExpectation() - client.getAttributes(at: path) { (result: Result) in + client.getAttributes(at: SFTPFilePath(path)) { (result: Result) in switch result { case .success(let attributes): XCTAssertEqual(data.count, attributes.size.flatMap { Int($0) }) @@ -329,7 +329,7 @@ class SFTPTests: XCTestCase { // from file handle let fileExp = XCTestExpectation() let fileInnerExp = XCTestExpectation() - client.withFile(filePath: path, flags: []) { file, completion in + client.withFile(at: SFTPFilePath(path), flags: []) { file, completion in file.readAttributes { result in switch result { case .success(let attributes): @@ -362,12 +362,12 @@ class SFTPTests: XCTestCase { let exp = XCTestExpectation() for _ in 0 ..< 100 { group.enter() - client.getAttributes(at: filePath) { result in + client.getAttributes(at: SFTPFilePath(filePath)) { result in group.leave() XCTAssertTrue(result.isSuccess) } group.enter() - client.listDirectory(atPath: rootPath) { result in + client.listDirectory(at: SFTPFilePath(rootPath)) { result in group.leave() XCTAssertTrue(result.isSuccess) } @@ -399,7 +399,7 @@ class SFTPTests: XCTestCase { let newPath = sftpServer.preferredWorkingDirectoryPath(components: "new/new.test") try sftpServer.createDirectory(atPath: sftpServer.preferredWorkingDirectoryPath(components: "new")) let exp = XCTestExpectation() - client.moveItem(atPath: filePath, toPath: newPath) { result in + client.moveItem(at: SFTPFilePath(filePath), to: SFTPFilePath(newPath)) { result in XCTAssertTrue(result.isSuccess) XCTAssertFalse(self.sftpServer.fileExists(atPath: filePath)) XCTAssertEqual( @@ -426,7 +426,7 @@ class SFTPTests: XCTestCase { let dst = sftpServer.preferredWorkingDirectoryPath(components: "DIR2/DIR1_NEW") let fileDst = sftpServer.preferredWorkingDirectoryPath(components: "DIR2/DIR1_NEW/file.test") let exp = XCTestExpectation() - client.moveItem(atPath: dir1Path, toPath: dst) { result in + client.moveItem(at: SFTPFilePath(dir1Path), to: SFTPFilePath(dst)) { result in XCTAssertTrue(result.isSuccess) XCTAssertFalse(self.sftpServer.fileExists(atPath: dir1Path)) XCTAssertTrue(self.sftpServer.fileExists(atPath: dst)) @@ -448,7 +448,7 @@ class SFTPTests: XCTestCase { sftpServer.createFile(atPath: filePath, contents: fileContent) XCTAssertTrue(sftpServer.fileExists(atPath: filePath)) let exp = XCTestExpectation() - client.removeFile(atPath: filePath) { result in + client.removeFile(at: SFTPFilePath(filePath)) { result in XCTAssertTrue(result.isSuccess) XCTAssertFalse(self.sftpServer.fileExists(atPath: filePath)) exp.fulfill() @@ -462,7 +462,7 @@ class SFTPTests: XCTestCase { try sftpServer.createDirectory(atPath: filePath) XCTAssertTrue(sftpServer.fileExists(atPath: filePath)) let exp = XCTestExpectation() - client.removeDirectory(atPath: filePath) { result in + client.removeDirectory(at: SFTPFilePath(filePath)) { result in XCTAssertTrue(result.isSuccess) XCTAssertFalse(self.sftpServer.fileExists(atPath: filePath)) exp.fulfill() @@ -478,7 +478,7 @@ class SFTPTests: XCTestCase { sftpServer.createFile(atPath: filePath, contents: "helloworld".data(using: .utf8)!) let exp = XCTestExpectation() let inner = XCTestExpectation() - client.withFile(filePath: filePath, flags: [.write, .read]) { file, completion in + client.withFile(at: SFTPFilePath(filePath), flags: [.write, .read]) { file, completion in file.read { result in XCTAssertTrue(result.isFailure) inner.fulfill()