adapt plugin to notify v5

pull/71/head
FabianLars 2 years ago
parent b351c38e88
commit 24b09c385e
No known key found for this signature in database
GPG Key ID: 3B12BC1DEBF61125

13
Cargo.lock generated

@ -2303,10 +2303,22 @@ dependencies = [
"kqueue", "kqueue",
"libc", "libc",
"mio", "mio",
"serde",
"walkdir", "walkdir",
"winapi", "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]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.46.0" version = "0.46.0"
@ -4048,6 +4060,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"log", "log",
"notify", "notify",
"notify-debouncer-mini",
"serde", "serde",
"serde_json", "serde_json",
"tauri", "tauri",

@ -15,4 +15,5 @@ serde_json.workspace = true
tauri.workspace = true tauri.workspace = true
log.workspace = true log.workspace = true
thiserror.workspace = true thiserror.workspace = true
notify = "5.0" notify = { version = "5.0" , features = ["serde"] }
notify-debouncer-mini = { version = "0.2.1" , features = ["serde"] }

@ -1,6 +1,6 @@
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from '@tauri-apps/api/tauri';
import { UnlistenFn } from "@tauri-apps/api/event"; import { UnlistenFn } from '@tauri-apps/api/event';
import { appWindow, WebviewWindow } from "@tauri-apps/api/window"; import { appWindow, WebviewWindow } from '@tauri-apps/api/window';
const w: WebviewWindow = appWindow; const w: WebviewWindow = appWindow;
@ -12,39 +12,42 @@ export interface DebouncedWatchOptions extends WatchOptions {
delayMs?: number; delayMs?: number;
} }
export interface RawEvent { export type RawEvent = {
path: string | null; type: RawEventKind;
operation: number; paths: string[];
cookie: number | null; attrs: unknown;
};
type RawEventKind =
| 'any '
| {
access?: unknown;
}
| {
create?: unknown;
}
| {
modify?: unknown;
}
| {
remove?: unknown;
} }
| 'other';
export type DebouncedEvent = export type DebouncedEvent = { kind: 'any'; path: string } | { kind: 'AnyContinous'; path: string };
| { 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 } };
async function unwatch(id: number): Promise<void> { async function unwatch(id: number): Promise<void> {
await invoke("plugin:fs-watch|unwatch", { id }); await invoke('plugin:fs-watch|unwatch', { id });
} }
export async function watch( export async function watch(paths: string | string[], options: DebouncedWatchOptions, cb: (event: DebouncedEvent) => void): Promise<UnlistenFn> {
paths: string | string[],
options: DebouncedWatchOptions,
cb: (event: DebouncedEvent) => void
): Promise<UnlistenFn> {
const opts = { const opts = {
recursive: false, recursive: false,
delayMs: 2000, delayMs: 2000,
...options, ...options,
}; };
let watchPaths; let watchPaths;
if (typeof paths === "string") { if (typeof paths === 'string') {
watchPaths = [paths]; watchPaths = [paths];
} else { } else {
watchPaths = paths; watchPaths = paths;
@ -52,18 +55,15 @@ export async function watch(
const id = window.crypto.getRandomValues(new Uint32Array(1))[0]; const id = window.crypto.getRandomValues(new Uint32Array(1))[0];
await invoke("plugin:fs-watch|watch", { await invoke('plugin:fs-watch|watch', {
id, id,
paths: watchPaths, paths: watchPaths,
options: opts, options: opts,
}); });
const unlisten = await w.listen<DebouncedEvent>( const unlisten = await w.listen<DebouncedEvent>(`watcher://debounced-event/${id}`, (event) => {
`watcher://debounced-event/${id}`,
(event) => {
cb(event.payload); cb(event.payload);
} });
);
return () => { return () => {
void unwatch(id); void unwatch(id);
@ -71,18 +71,14 @@ export async function watch(
}; };
} }
export async function watchImmediate( export async function watchImmediate(paths: string | string[], options: WatchOptions, cb: (event: RawEvent) => void): Promise<UnlistenFn> {
paths: string | string[],
options: WatchOptions,
cb: (event: RawEvent) => void
): Promise<UnlistenFn> {
const opts = { const opts = {
recursive: false, recursive: false,
...options, ...options,
delayMs: null, delayMs: null,
}; };
let watchPaths; let watchPaths;
if (typeof paths === "string") { if (typeof paths === 'string') {
watchPaths = [paths]; watchPaths = [paths];
} else { } else {
watchPaths = paths; watchPaths = paths;
@ -90,18 +86,15 @@ export async function watchImmediate(
const id = window.crypto.getRandomValues(new Uint32Array(1))[0]; const id = window.crypto.getRandomValues(new Uint32Array(1))[0];
await invoke("plugin:fs-watch|watch", { await invoke('plugin:fs-watch|watch', {
id, id,
paths: watchPaths, paths: watchPaths,
options: opts, options: opts,
}); });
const unlisten = await w.listen<RawEvent>( const unlisten = await w.listen<RawEvent>(`watcher://raw-event/${id}`, (event) => {
`watcher://raw-event/${id}`,
(event) => {
cb(event.payload); cb(event.payload);
} });
);
return () => { return () => {
void unwatch(id); void unwatch(id);

@ -1,7 +1,5 @@
use notify::{ use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
raw_watcher, watcher, DebouncedEvent, Op, RawEvent, RecommendedWatcher, RecursiveMode, use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer};
Watcher as _,
};
use serde::{ser::Serializer, Deserialize, Serialize}; use serde::{ser::Serializer, Deserialize, Serialize};
use tauri::{ use tauri::{
command, command,
@ -39,72 +37,33 @@ impl Serialize for Error {
} }
#[derive(Default)] #[derive(Default)]
struct WatcherCollection(Mutex<HashMap<Id, (RecommendedWatcher, Vec<PathBuf>)>>); struct WatcherCollection(Mutex<HashMap<Id, (WatcherKind, Vec<PathBuf>)>>);
#[derive(Clone, Serialize)]
struct RawEventWrapper {
path: Option<PathBuf>,
operation: u32,
cookie: Option<u32>,
}
#[derive(Clone, Serialize)] enum WatcherKind {
#[serde(tag = "type", content = "payload")] Debouncer(Debouncer<RecommendedWatcher>),
enum DebouncedEventWrapper { Watcher(RecommendedWatcher),
NoticeWrite(PathBuf),
NoticeRemove(PathBuf),
Create(PathBuf),
Write(PathBuf),
Chmod(PathBuf),
Remove(PathBuf),
Rename(PathBuf, PathBuf),
Rescan,
Error {
error: String,
path: Option<PathBuf>,
},
}
impl From<DebouncedEvent> 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<R: Runtime>(window: Window<R>, rx: Receiver<RawEvent>, id: Id) { fn watch_raw<R: Runtime>(window: Window<R>, rx: Receiver<notify::Result<Event>>, id: Id) {
spawn(move || { spawn(move || {
let event_name = format!("watcher://raw-event/{}", id); let event_name = format!("watcher://raw-event/{}", id);
while let Ok(event) = rx.recv() { while let Ok(event) = rx.recv() {
let _ = window.emit( if let Ok(event) = event {
&event_name, // TODO: Should errors be emitted too?
RawEventWrapper { let _ = window.emit(&event_name, event);
path: event.path, }
operation: event.op.unwrap_or_else(|_| Op::empty()).bits(),
cookie: event.cookie,
},
);
} }
}); });
} }
fn watch_debounced<R: Runtime>(window: Window<R>, rx: Receiver<DebouncedEvent>, id: Id) { fn watch_debounced<R: Runtime>(window: Window<R>, rx: Receiver<DebounceEventResult>, id: Id) {
spawn(move || { spawn(move || {
let event_name = format!("watcher://debounced-event/{}", id); let event_name = format!("watcher://debounced-event/{}", id);
while let Ok(event) = rx.recv() { 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<R: Runtime>(
let watcher = if let Some(delay) = options.delay_ms { let watcher = if let Some(delay) = options.delay_ms {
let (tx, rx) = channel(); 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 { for path in &paths {
watcher.watch(path, mode)?; watcher.watch(path, mode)?;
} }
watch_debounced(window, rx, id); watch_debounced(window, rx, id);
watcher WatcherKind::Debouncer(debouncer)
} else { } else {
let (tx, rx) = channel(); let (tx, rx) = channel();
let mut watcher = raw_watcher(tx)?; let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
for path in &paths { for path in &paths {
watcher.watch(path, mode)?; watcher.watch(path, mode)?;
} }
watch_raw(window, rx, id); watch_raw(window, rx, id);
watcher WatcherKind::Watcher(watcher)
}; };
watchers.0.lock().unwrap().insert(id, (watcher, paths)); watchers.0.lock().unwrap().insert(id, (watcher, paths));
@ -155,11 +115,20 @@ async fn watch<R: Runtime>(
#[command] #[command]
async fn unwatch(watchers: State<'_, WatcherCollection>, id: Id) -> Result<()> { async fn unwatch(watchers: State<'_, WatcherCollection>, id: Id) -> Result<()> {
if let Some((mut watcher, paths)) = watchers.0.lock().unwrap().remove(&id) { 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 { for path in paths {
watcher.unwatch(path)?; watcher.unwatch(&path)?
} }
} }
};
}
Ok(()) Ok(())
} }

Loading…
Cancel
Save