-
Notifications
You must be signed in to change notification settings - Fork 125
Expand file tree
/
Copy pathRemoteFileManager.swift
More file actions
136 lines (115 loc) · 3.71 KB
/
RemoteFileManager.swift
File metadata and controls
136 lines (115 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import Peripheral
import Combine
import Foundation
@MainActor
public class RemoteFileManager: ObservableObject {
private let storage: StorageAPI
private var supportedExtensions: [String] = [
".ibtn", ".nfc", ".shd", ".sub", ".rfid", ".ir",
".fmf", ".txt", "log", "fim"
]
public enum Error: Swift.Error, Equatable {
case directoryIsNotEmpty
case unknown(String)
}
public init(storage: StorageAPI) {
self.storage = storage
}
// MARK: Directory
public func list(at path: Path) async throws -> [ExtendedElement] {
do {
return try await storage
.list(at: path)
.map { .init(element: $0, relativeTo: path) }
} catch {
logger.error("list directory: \(error)")
throw Error.unknown(.init(describing: error))
}
}
// MARK: File
public func canRead(_ file: File) -> Bool {
supportedExtensions.contains {
file.name.hasSuffix($0)
}
}
public func readFile(at path: Path) async throws -> String {
do {
let bytes = try await storage.read(at: path).drain()
return .init(decoding: bytes, as: UTF8.self)
} catch {
logger.error("read file: \(error)")
throw Error.unknown(.init(describing: error))
}
}
public func readRaw(at path: Path) async throws -> [UInt8] {
do {
return try await storage.read(at: path).drain()
} catch {
logger.error("read raw: \(error)")
throw Error.unknown(.init(describing: error))
}
}
public func writeFile(_ content: String, at path: Path) async throws {
do {
try await storage.write(at: path, string: content).drain()
} catch {
logger.error("write file: \(error)")
throw Error.unknown(.init(describing: error))
}
}
// MARK: Import
public func importFile(url: URL, at path: Path) async throws {
do {
guard let name = url.pathComponents.last else {
logger.error("import file: invalid url \(url)")
return
}
guard url.startAccessingSecurityScopedResource() else {
logger.error("import file: unable to access \(url)")
return
}
defer {
url.stopAccessingSecurityScopedResource()
}
let path = path.appending(name)
let bytes = try [UInt8](Data(contentsOf: url))
try await storage.write(at: path, bytes: bytes).drain()
} catch {
logger.error("import file: \(error)")
throw Error.unknown(.init(describing: error))
}
}
// Create
public func create(
path: Path,
isDirectory: Bool
) async throws {
do {
try await storage.create(at: path, isDirectory: isDirectory)
} catch {
logger.error("create file: \(error)")
throw Error.unknown(.init(describing: error))
}
}
// Delete
public func delete(
_ element: Element,
at path: Path,
force: Bool = false
) async throws {
do {
let path = path.appending(element.name)
try await storage.delete(at: path, force: force)
} catch let error as Peripheral.Error
where error == .storage(.notEmpty) {
throw Error.directoryIsNotEmpty
} catch {
logger.error("delete file: \(error)")
throw Error.unknown(.init(describing: error))
}
}
// Analytics
func recordFileManager() {
analytics.appOpen(target: .fileManager)
}
}