|
1 | 1 | import Foundation |
| 2 | +import Dispatch |
| 3 | +import Logging |
2 | 4 |
|
3 | 5 | /// A common part of the File and Folder functionality that allows you to set up the required paths and FileManager that the file system should use. |
4 | 6 | public final class Store<fileSystem: FileSystem> { |
@@ -176,3 +178,81 @@ extension Store where fileSystem == Folder { |
176 | 178 | return File(store: store) |
177 | 179 | } |
178 | 180 | } |
| 181 | + |
| 182 | +// MARK: - Permission |
| 183 | +public extension Store { |
| 184 | + /// Returns the file or folder permissions as a POSIX mode_t (Int). |
| 185 | + func getPermissions() -> Int? { |
| 186 | + let attributes = try? fileManager.attributesOfItem(atPath: path.rawValue) |
| 187 | + return attributes?[.posixPermissions] as? Int |
| 188 | + } |
| 189 | + |
| 190 | + /// Sets the file or folder permissions using a POSIX mode_t (Int). |
| 191 | + func setPermissions(_ permissions: Int) throws { |
| 192 | + do { |
| 193 | + try fileManager.setAttributes([.posixPermissions: permissions], ofItemAtPath: path.rawValue) |
| 194 | + } catch { |
| 195 | + throw FileError.writeFailed(path: path, error: error) |
| 196 | + } |
| 197 | + } |
| 198 | +} |
| 199 | + |
| 200 | +// MARK: - Symbolic Link |
| 201 | +public extension Store { |
| 202 | + /// Checks if the path is a symbolic link. |
| 203 | + func isSymbolicLink() -> Bool { |
| 204 | + let attributes = try? fileManager.attributesOfItem(atPath: path.rawValue) |
| 205 | + let fileType = attributes?[.type] as? FileAttributeType |
| 206 | + return fileType == .typeSymbolicLink |
| 207 | + } |
| 208 | + |
| 209 | + /// Creates a symbolic link at this path pointing to the destination path. |
| 210 | + func createSymbolicLink(to destinationPath: Path) throws { |
| 211 | + do { |
| 212 | + try fileManager.createSymbolicLink(atPath: path.rawValue, withDestinationPath: destinationPath.rawValue) |
| 213 | + } catch { |
| 214 | + throw FileError.writeFailed(path: path, error: error) |
| 215 | + } |
| 216 | + } |
| 217 | + |
| 218 | + /// Returns the destination path of the symbolic link, if this is a symbolic link. |
| 219 | + func destinationOfSymbolicLink() -> Path? { |
| 220 | + do { |
| 221 | + let dest = try fileManager.destinationOfSymbolicLink(atPath: path.rawValue) |
| 222 | + return Path(dest) |
| 223 | + } catch { |
| 224 | + return nil |
| 225 | + } |
| 226 | + } |
| 227 | +} |
| 228 | + |
| 229 | +// MARK: - File/Folder Change Watch |
| 230 | +public extension Store { |
| 231 | + /// Starts watching for changes to the file or folder at this path. |
| 232 | + /// - Parameters: |
| 233 | + /// - eventHandler: Called when a change is detected. |
| 234 | + /// - Returns: A DispatchSourceFileSystemObject? (macOS, iOS), or nil if not supported. Uses swift-log for logging. |
| 235 | + @discardableResult |
| 236 | + func watch(eventHandler: @escaping () -> Void) -> Any? { |
| 237 | +#if os(macOS) || os(iOS) |
| 238 | + let fd = open(path.rawValue, O_EVTONLY) |
| 239 | + guard fd != -1 else { return nil } |
| 240 | + let source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .all, queue: .main) |
| 241 | + source.setEventHandler(handler: eventHandler) |
| 242 | + source.setCancelHandler { |
| 243 | + close(fd) |
| 244 | + } |
| 245 | + source.resume() |
| 246 | + return source |
| 247 | +#elseif os(Linux) |
| 248 | + // Linux: inotify-based implementation needed (not implemented here) |
| 249 | + let logger = Logger(label: "Store.Watch") |
| 250 | + logger.warning("Watch is not implemented for Linux in this version.") |
| 251 | + return nil |
| 252 | +#else |
| 253 | + let logger = Logger(label: "Store.Watch") |
| 254 | + logger.warning("Watch is not supported on this platform.") |
| 255 | + return nil |
| 256 | +#endif |
| 257 | + } |
| 258 | +} |
0 commit comments