From 24b09c385e63260692d4eaf11ac74afcd4610ff2 Mon Sep 17 00:00:00 2001 From: FabianLars Date: Sat, 7 Jan 2023 22:25:44 +0100 Subject: [PATCH] adapt plugin to notify v5 --- Cargo.lock | 13 +++ plugins/fs-watch/Cargo.toml | 3 +- plugins/fs-watch/guest-js/index.ts | 173 ++++++++++++++--------------- plugins/fs-watch/src/lib.rs | 99 ++++++----------- 4 files changed, 132 insertions(+), 156 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd37b042..1ff0a30b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2303,10 +2303,22 @@ dependencies = [ "kqueue", "libc", "mio", + "serde", "walkdir", "winapi", ] +[[package]] +name = "notify-debouncer-mini" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b" +dependencies = [ + "crossbeam-channel", + "notify", + "serde", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -4048,6 +4060,7 @@ version = "0.1.0" dependencies = [ "log", "notify", + "notify-debouncer-mini", "serde", "serde_json", "tauri", diff --git a/plugins/fs-watch/Cargo.toml b/plugins/fs-watch/Cargo.toml index b3dbd942..b8f648a9 100644 --- a/plugins/fs-watch/Cargo.toml +++ b/plugins/fs-watch/Cargo.toml @@ -15,4 +15,5 @@ serde_json.workspace = true tauri.workspace = true log.workspace = true thiserror.workspace = true -notify = "5.0" \ No newline at end of file +notify = { version = "5.0" , features = ["serde"] } +notify-debouncer-mini = { version = "0.2.1" , features = ["serde"] } diff --git a/plugins/fs-watch/guest-js/index.ts b/plugins/fs-watch/guest-js/index.ts index bb16c2c9..0fb7eb24 100644 --- a/plugins/fs-watch/guest-js/index.ts +++ b/plugins/fs-watch/guest-js/index.ts @@ -1,110 +1,103 @@ -import { invoke } from "@tauri-apps/api/tauri"; -import { UnlistenFn } from "@tauri-apps/api/event"; -import { appWindow, WebviewWindow } from "@tauri-apps/api/window"; +import { invoke } from '@tauri-apps/api/tauri'; +import { UnlistenFn } from '@tauri-apps/api/event'; +import { appWindow, WebviewWindow } from '@tauri-apps/api/window'; const w: WebviewWindow = appWindow; export interface WatchOptions { - recursive?: boolean; + recursive?: boolean; } export interface DebouncedWatchOptions extends WatchOptions { - delayMs?: number; + delayMs?: number; } -export interface RawEvent { - path: string | null; - operation: number; - cookie: number | null; -} - -export type DebouncedEvent = - | { type: "NoticeWrite"; payload: string } - | { type: "NoticeRemove"; payload: string } - | { type: "Create"; payload: string } - | { type: "Write"; payload: string } - | { type: "Chmod"; payload: string } - | { type: "Remove"; payload: string } - | { type: "Rename"; payload: string } - | { type: "Rescan"; payload: null } - | { type: "Error"; payload: { error: string; path: string | null } }; +export type RawEvent = { + type: RawEventKind; + paths: string[]; + attrs: unknown; +}; + +type RawEventKind = + | 'any ' + | { + access?: unknown; + } + | { + create?: unknown; + } + | { + modify?: unknown; + } + | { + remove?: unknown; + } + | 'other'; + +export type DebouncedEvent = { kind: 'any'; path: string } | { kind: 'AnyContinous'; path: string }; async function unwatch(id: number): Promise { - await invoke("plugin:fs-watch|unwatch", { id }); + await invoke('plugin:fs-watch|unwatch', { id }); } -export async function watch( - paths: string | string[], - options: DebouncedWatchOptions, - cb: (event: DebouncedEvent) => void -): Promise { - const opts = { - recursive: false, - delayMs: 2000, - ...options, - }; - let watchPaths; - if (typeof paths === "string") { - watchPaths = [paths]; - } else { - watchPaths = paths; - } - - const id = window.crypto.getRandomValues(new Uint32Array(1))[0]; - - await invoke("plugin:fs-watch|watch", { - id, - paths: watchPaths, - options: opts, - }); - - const unlisten = await w.listen( - `watcher://debounced-event/${id}`, - (event) => { - cb(event.payload); +export async function watch(paths: string | string[], options: DebouncedWatchOptions, cb: (event: DebouncedEvent) => void): Promise { + const opts = { + recursive: false, + delayMs: 2000, + ...options, + }; + let watchPaths; + if (typeof paths === 'string') { + watchPaths = [paths]; + } else { + watchPaths = paths; } - ); - return () => { - void unwatch(id); - unlisten(); - }; + const id = window.crypto.getRandomValues(new Uint32Array(1))[0]; + + await invoke('plugin:fs-watch|watch', { + id, + paths: watchPaths, + options: opts, + }); + + const unlisten = await w.listen(`watcher://debounced-event/${id}`, (event) => { + cb(event.payload); + }); + + return () => { + void unwatch(id); + unlisten(); + }; } -export async function watchImmediate( - paths: string | string[], - options: WatchOptions, - cb: (event: RawEvent) => void -): Promise { - const opts = { - recursive: false, - ...options, - delayMs: null, - }; - let watchPaths; - if (typeof paths === "string") { - watchPaths = [paths]; - } else { - watchPaths = paths; - } - - const id = window.crypto.getRandomValues(new Uint32Array(1))[0]; - - await invoke("plugin:fs-watch|watch", { - id, - paths: watchPaths, - options: opts, - }); - - const unlisten = await w.listen( - `watcher://raw-event/${id}`, - (event) => { - cb(event.payload); +export async function watchImmediate(paths: string | string[], options: WatchOptions, cb: (event: RawEvent) => void): Promise { + const opts = { + recursive: false, + ...options, + delayMs: null, + }; + let watchPaths; + if (typeof paths === 'string') { + watchPaths = [paths]; + } else { + watchPaths = paths; } - ); - return () => { - void unwatch(id); - unlisten(); - }; + const id = window.crypto.getRandomValues(new Uint32Array(1))[0]; + + await invoke('plugin:fs-watch|watch', { + id, + paths: watchPaths, + options: opts, + }); + + const unlisten = await w.listen(`watcher://raw-event/${id}`, (event) => { + cb(event.payload); + }); + + return () => { + void unwatch(id); + unlisten(); + }; } diff --git a/plugins/fs-watch/src/lib.rs b/plugins/fs-watch/src/lib.rs index 790507b7..c022b253 100644 --- a/plugins/fs-watch/src/lib.rs +++ b/plugins/fs-watch/src/lib.rs @@ -1,7 +1,5 @@ -use notify::{ - raw_watcher, watcher, DebouncedEvent, Op, RawEvent, RecommendedWatcher, RecursiveMode, - Watcher as _, -}; +use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; +use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer}; use serde::{ser::Serializer, Deserialize, Serialize}; use tauri::{ command, @@ -39,72 +37,33 @@ impl Serialize for Error { } #[derive(Default)] -struct WatcherCollection(Mutex)>>); +struct WatcherCollection(Mutex)>>); -#[derive(Clone, Serialize)] -struct RawEventWrapper { - path: Option, - operation: u32, - cookie: Option, +enum WatcherKind { + Debouncer(Debouncer), + Watcher(RecommendedWatcher), } -#[derive(Clone, Serialize)] -#[serde(tag = "type", content = "payload")] -enum DebouncedEventWrapper { - NoticeWrite(PathBuf), - NoticeRemove(PathBuf), - Create(PathBuf), - Write(PathBuf), - Chmod(PathBuf), - Remove(PathBuf), - Rename(PathBuf, PathBuf), - Rescan, - Error { - error: String, - path: Option, - }, -} - -impl From for DebouncedEventWrapper { - fn from(event: DebouncedEvent) -> Self { - match event { - DebouncedEvent::NoticeWrite(path) => Self::NoticeWrite(path), - DebouncedEvent::NoticeRemove(path) => Self::NoticeRemove(path), - DebouncedEvent::Create(path) => Self::Create(path), - DebouncedEvent::Write(path) => Self::Write(path), - DebouncedEvent::Chmod(path) => Self::Chmod(path), - DebouncedEvent::Remove(path) => Self::Remove(path), - DebouncedEvent::Rename(from, to) => Self::Rename(from, to), - DebouncedEvent::Rescan => Self::Rescan, - DebouncedEvent::Error(error, path) => Self::Error { - error: error.to_string(), - path, - }, - } - } -} - -fn watch_raw(window: Window, rx: Receiver, id: Id) { +fn watch_raw(window: Window, rx: Receiver>, id: Id) { spawn(move || { let event_name = format!("watcher://raw-event/{}", id); while let Ok(event) = rx.recv() { - let _ = window.emit( - &event_name, - RawEventWrapper { - path: event.path, - operation: event.op.unwrap_or_else(|_| Op::empty()).bits(), - cookie: event.cookie, - }, - ); + if let Ok(event) = event { + // TODO: Should errors be emitted too? + let _ = window.emit(&event_name, event); + } } }); } -fn watch_debounced(window: Window, rx: Receiver, id: Id) { +fn watch_debounced(window: Window, rx: Receiver, id: Id) { spawn(move || { let event_name = format!("watcher://debounced-event/{}", id); while let Ok(event) = rx.recv() { - let _ = window.emit(&event_name, DebouncedEventWrapper::from(event)); + if let Ok(event) = event { + // TODO: Should errors be emitted too? + let _ = window.emit(&event_name, event); + } } }); } @@ -132,20 +91,21 @@ async fn watch( let watcher = if let Some(delay) = options.delay_ms { let (tx, rx) = channel(); - let mut watcher = watcher(tx, Duration::from_millis(delay))?; + let mut debouncer = new_debouncer(Duration::from_millis(delay), None, tx)?; + let watcher = debouncer.watcher(); for path in &paths { watcher.watch(path, mode)?; } watch_debounced(window, rx, id); - watcher + WatcherKind::Debouncer(debouncer) } else { let (tx, rx) = channel(); - let mut watcher = raw_watcher(tx)?; + let mut watcher = RecommendedWatcher::new(tx, Config::default())?; for path in &paths { watcher.watch(path, mode)?; } watch_raw(window, rx, id); - watcher + WatcherKind::Watcher(watcher) }; watchers.0.lock().unwrap().insert(id, (watcher, paths)); @@ -155,10 +115,19 @@ async fn watch( #[command] async fn unwatch(watchers: State<'_, WatcherCollection>, id: Id) -> Result<()> { - if let Some((mut watcher, paths)) = watchers.0.lock().unwrap().remove(&id) { - for path in paths { - watcher.unwatch(path)?; - } + if let Some((watcher, paths)) = watchers.0.lock().unwrap().remove(&id) { + match watcher { + WatcherKind::Debouncer(mut debouncer) => { + for path in paths { + debouncer.watcher().unwatch(&path)? + } + } + WatcherKind::Watcher(mut watcher) => { + for path in paths { + watcher.unwatch(&path)? + } + } + }; } Ok(()) }