|
|
@ -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(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|