From 5247d898cd18a7de2cd4eca3bb076ee67b3f41d7 Mon Sep 17 00:00:00 2001 From: "T. R. Bernstein" Date: Wed, 11 Mar 2026 18:40:08 +0100 Subject: [PATCH] Implement unwatching a path --- Sources/Inotify/Inotify.swift | 7 +++++++ Sources/Inotify/InotifyError.swift | 3 +++ Tests/InotifyIntegrationTests/WatchTests.swift | 15 +++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/Sources/Inotify/Inotify.swift b/Sources/Inotify/Inotify.swift index 7e078cd..e29dbc8 100644 --- a/Sources/Inotify/Inotify.swift +++ b/Sources/Inotify/Inotify.swift @@ -21,6 +21,13 @@ public actor Inotify { return wd } + public func removeWatch(_ wd: Int32) throws { + guard inotify_rm_watch(self.fd, wd) == 0 else { + throw InotifyError.removeWatchFailed(watchDescriptor: wd, errno: cinotify_get_errno()) + } + watches.removeValue(forKey: wd) + } + deinit { cinotify_deinit(self.fd) } diff --git a/Sources/Inotify/InotifyError.swift b/Sources/Inotify/InotifyError.swift index 824a336..390a43b 100644 --- a/Sources/Inotify/InotifyError.swift +++ b/Sources/Inotify/InotifyError.swift @@ -3,6 +3,7 @@ import CInotify public enum InotifyError: Error, Sendable, CustomStringConvertible { case initFailed(errno: Int32) case addWatchFailed(path: String, errno: Int32) + case removeWatchFailed(watchDescriptor: Int32, errno: Int32) public var description: String { switch self { @@ -10,6 +11,8 @@ public enum InotifyError: Error, Sendable, CustomStringConvertible { "inotify_init1 failed: \(readableErrno(code))" case .addWatchFailed(let path, let code): "inotify_add_watch failed for '\(path)': \(readableErrno(code))" + case .removeWatchFailed(let wd, let code): + "inotify_rm_watch failed for wd \(wd): \(readableErrno(code))" } } diff --git a/Tests/InotifyIntegrationTests/WatchTests.swift b/Tests/InotifyIntegrationTests/WatchTests.swift index 178e13d..f4c6a64 100644 --- a/Tests/InotifyIntegrationTests/WatchTests.swift +++ b/Tests/InotifyIntegrationTests/WatchTests.swift @@ -18,4 +18,19 @@ struct WatchTests { try await watcher.addWatch(path: "/nonexistent-\(UUID())", mask: .allEvents) } } + + @Test func removeWatchSucceeds() async throws { + try await withTempDir { dir in + let watcher = try Inotify() + let wd = try await watcher.addWatch(path: dir, mask: .allEvents) + try await watcher.removeWatch(wd) + } + } + + @Test func removeInvalidWatchThrows() async throws { + let watcher = try Inotify() + await #expect(throws: InotifyError.self) { + try await watcher.removeWatch(9999) + } + } }