60 lines
2.4 KiB
Swift
60 lines
2.4 KiB
Swift
// SPDX-FileCopyrightText: 2026 Kaede Akino
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import Foundation
|
|
import TermindDomain
|
|
|
|
public nonisolated protocol RemoteFileSystem: Sendable {
|
|
func listDirectory(at path: String) async throws -> [RemoteFile]
|
|
func attributes(at path: String) async throws -> RemoteFile?
|
|
func readFile(at path: String, maximumBytes: Int) async throws -> Data
|
|
func writeFile(_ data: Data, at path: String, overwrite: Bool) async throws
|
|
func setPermissions(at path: String, to permissions: UInt32) async throws
|
|
func createDirectory(at path: String) async throws
|
|
func rename(from source: String, to destination: String, overwrite: Bool) async throws
|
|
func removeFile(at path: String) async throws
|
|
func removeDirectory(at path: String) async throws
|
|
}
|
|
|
|
public nonisolated enum RemoteFileName {
|
|
public static func isValid(_ name: String) -> Bool {
|
|
!name.isEmpty && name != "." && name != ".." && !name.contains("/")
|
|
&& !name.unicodeScalars.contains(where: { CharacterSet.controlCharacters.contains($0) })
|
|
}
|
|
}
|
|
|
|
nonisolated extension RemoteFileSystem {
|
|
public func directoryContents(at path: String) async throws -> [RemoteFile] {
|
|
try await listDirectory(at: path).sorted {
|
|
if $0.isDirectory != $1.isDirectory { return $0.isDirectory }
|
|
return $0.name.localizedStandardCompare($1.name) == .orderedAscending
|
|
}
|
|
}
|
|
|
|
public func createDirectory(named name: String, in directory: String) async throws {
|
|
guard RemoteFileName.isValid(name) else { throw CocoaError(.fileWriteInvalidFileName) }
|
|
try await createDirectory(at: RemotePath.join(directory, name))
|
|
}
|
|
|
|
public func rename(_ file: RemoteFile, to name: String) async throws {
|
|
guard RemoteFileName.isValid(name) else { throw CocoaError(.fileWriteInvalidFileName) }
|
|
try await rename(
|
|
from: file.path,
|
|
to: RemotePath.join(RemotePath.parent(of: file.path), name),
|
|
overwrite: false
|
|
)
|
|
}
|
|
|
|
public func setPermissions(of file: RemoteFile, to permissions: UInt32) async throws {
|
|
try await setPermissions(at: file.path, to: permissions & 0o7777)
|
|
}
|
|
|
|
public func remove(_ file: RemoteFile) async throws {
|
|
if file.isDirectory {
|
|
try await removeDirectory(at: file.path)
|
|
} else {
|
|
try await removeFile(at: file.path)
|
|
}
|
|
}
|
|
}
|