refactor(fs): reduce overhead of watch (#2613)

This commit is contained in:
Tony
2025-04-19 19:35:14 +08:00
committed by GitHub
parent 5e78988f72
commit dac4d53724
12 changed files with 102 additions and 162 deletions
-5
View File
@@ -150,11 +150,6 @@ pub fn open<R: Runtime>(
Ok(rid)
}
#[tauri::command]
pub fn close<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> CommandResult<()> {
webview.resources_table().close(rid).map_err(Into::into)
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CopyFileOptions {
-3
View File
@@ -397,7 +397,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
commands::create,
commands::open,
commands::copy_file,
commands::close,
commands::mkdir,
commands::read_dir,
commands::read,
@@ -420,8 +419,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
commands::size,
#[cfg(feature = "watch")]
watcher::watch,
#[cfg(feature = "watch")]
watcher::unwatch
])
.setup(|app, api| {
let scope = Scope {
+43 -98
View File
@@ -2,8 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
use notify_debouncer_full::{new_debouncer, DebounceEventResult, Debouncer, RecommendedCache};
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
use notify_debouncer_full::{new_debouncer, DebouncedEvent, Debouncer, RecommendedCache};
use serde::Deserialize;
use tauri::{
ipc::{Channel, CommandScope, GlobalScope},
@@ -11,15 +11,7 @@ use tauri::{
Manager, Resource, ResourceId, Runtime, Webview,
};
use std::{
path::PathBuf,
sync::{
mpsc::{channel, Receiver},
Mutex,
},
thread::spawn,
time::Duration,
};
use std::time::Duration;
use crate::{
commands::{resolve_path, CommandResult},
@@ -27,79 +19,44 @@ use crate::{
SafeFilePath,
};
struct InnerWatcher {
pub kind: WatcherKind,
paths: Vec<PathBuf>,
}
pub struct WatcherResource(Mutex<InnerWatcher>);
impl WatcherResource {
fn new(kind: WatcherKind, paths: Vec<PathBuf>) -> Self {
Self(Mutex::new(InnerWatcher { kind, paths }))
}
fn with_lock<R, F: FnMut(&mut InnerWatcher) -> R>(&self, mut f: F) -> R {
let mut watcher = self.0.lock().unwrap();
f(&mut watcher)
}
}
impl Resource for WatcherResource {}
#[allow(unused)]
enum WatcherKind {
Debouncer(Debouncer<RecommendedWatcher, RecommendedCache>),
Watcher(RecommendedWatcher),
}
fn watch_raw(on_event: Channel<Event>, rx: Receiver<notify::Result<Event>>) {
spawn(move || {
while let Ok(event) = rx.recv() {
if let Ok(event) = event {
// TODO: Should errors be emitted too?
let _ = on_event.send(event);
}
}
});
}
fn watch_debounced(on_event: Channel<Event>, rx: Receiver<DebounceEventResult>) {
spawn(move || {
while let Ok(Ok(events)) = rx.recv() {
for event in events {
// TODO: Should errors be emitted too?
let _ = on_event.send(event.event);
}
}
});
}
impl Resource for WatcherKind {}
#[derive(Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WatchOptions {
base_dir: Option<BaseDirectory>,
#[serde(default)]
recursive: bool,
delay_ms: Option<u64>,
}
#[tauri::command]
pub async fn watch<R: Runtime>(
pub fn watch<R: Runtime>(
webview: Webview<R>,
paths: Vec<SafeFilePath>,
options: WatchOptions,
on_event: Channel<Event>,
on_event: Channel<notify::Event>,
global_scope: GlobalScope<Entry>,
command_scope: CommandScope<Entry>,
) -> CommandResult<ResourceId> {
let mut resolved_paths = Vec::with_capacity(paths.capacity());
for path in paths {
resolved_paths.push(resolve_path(
&webview,
&global_scope,
&command_scope,
path,
options.base_dir,
)?);
}
let resolved_paths = paths
.into_iter()
.map(|path| {
resolve_path(
&webview,
&global_scope,
&command_scope,
path,
options.base_dir,
)
})
.collect::<CommandResult<Vec<_>>>()?;
let recursive_mode = if options.recursive {
RecursiveMode::Recursive
@@ -107,52 +64,40 @@ pub async fn watch<R: Runtime>(
RecursiveMode::NonRecursive
};
let kind = if let Some(delay) = options.delay_ms {
let (tx, rx) = channel();
let mut debouncer = new_debouncer(Duration::from_millis(delay), None, tx)?;
let watcher_kind = if let Some(delay) = options.delay_ms {
let mut debouncer = new_debouncer(
Duration::from_millis(delay),
None,
move |events: Result<Vec<DebouncedEvent>, Vec<notify::Error>>| {
if let Ok(events) = events {
for event in events {
// TODO: Should errors be emitted too?
let _ = on_event.send(event.event);
}
}
},
)?;
for path in &resolved_paths {
debouncer.watch(path, recursive_mode)?;
}
watch_debounced(on_event, rx);
WatcherKind::Debouncer(debouncer)
} else {
let (tx, rx) = channel();
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
let mut watcher = RecommendedWatcher::new(
move |event| {
if let Ok(event) = event {
// TODO: Should errors be emitted too?
let _ = on_event.send(event);
}
},
Config::default(),
)?;
for path in &resolved_paths {
watcher.watch(path, recursive_mode)?;
}
watch_raw(on_event, rx);
WatcherKind::Watcher(watcher)
};
let rid = webview
.resources_table()
.add(WatcherResource::new(kind, resolved_paths));
let rid = webview.resources_table().add(watcher_kind);
Ok(rid)
}
#[tauri::command]
pub async fn unwatch<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> CommandResult<()> {
let watcher = webview.resources_table().take::<WatcherResource>(rid)?;
WatcherResource::with_lock(&watcher, |watcher| {
match &mut watcher.kind {
WatcherKind::Debouncer(ref mut debouncer) => {
for path in &watcher.paths {
debouncer.unwatch(path).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
})?;
}
}
WatcherKind::Watcher(ref mut w) => {
for path in &watcher.paths {
w.unwatch(path).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
})?;
}
}
}
Ok(())
})
}