yazi/yazi-watcher/src/watcher.rs
三咲雅 misaki masa 58f1013348
Some checks failed
Cachix / Publish Flake (push) Has been cancelled
Check / clippy (push) Has been cancelled
Check / rustfmt (push) Has been cancelled
Check / stylua (push) Has been cancelled
Draft / build-unix (gcc-aarch64-linux-gnu, ubuntu-latest, aarch64-unknown-linux-gnu) (push) Has been cancelled
Draft / build-unix (gcc-i686-linux-gnu, ubuntu-latest, i686-unknown-linux-gnu) (push) Has been cancelled
Draft / build-unix (gcc-riscv64-linux-gnu, ubuntu-latest, riscv64gc-unknown-linux-gnu) (push) Has been cancelled
Draft / build-unix (gcc-sparc64-linux-gnu, ubuntu-latest, sparc64-unknown-linux-gnu) (push) Has been cancelled
Draft / build-unix (macos-latest, aarch64-apple-darwin) (push) Has been cancelled
Draft / build-unix (macos-latest, x86_64-apple-darwin) (push) Has been cancelled
Draft / build-unix (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Has been cancelled
Draft / build-windows (windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
Draft / build-windows (windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
Draft / build-musl (aarch64-unknown-linux-musl) (push) Has been cancelled
Draft / build-musl (x86_64-unknown-linux-musl) (push) Has been cancelled
Draft / build-snap (amd64, ubuntu-latest) (push) Has been cancelled
Draft / build-snap (arm64, ubuntu-24.04-arm) (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Draft / snap (push) Has been cancelled
Draft / draft (push) Has been cancelled
Draft / nightly (push) Has been cancelled
feat: trash bin (#4144)
2026-07-23 01:59:39 +08:00

103 lines
2.6 KiB
Rust

use futures::{StreamExt, stream};
use indexmap::IndexSet;
use tracing::error;
use yazi_fs::{FsUrl, file::File};
use yazi_shared::{LastValue, url::{UrlBuf, UrlCow, UrlLike}};
use crate::{Refresher, Reporter, WATCHED, Watchee, backend::Backend};
pub struct Watcher {
last: LastValue<IndexSet<UrlBuf>>,
reporter: Reporter,
pub refresher: Refresher,
}
impl Watcher {
pub fn serve() -> Self {
let last = LastValue::default();
let refresher = Refresher::serve();
let backend = Backend::serve();
let reporter = backend.reporter.clone();
tokio::spawn(Self::run(last.clone(), backend));
Self { last, reporter, refresher }
}
pub fn watch<I>(&mut self, files: I)
where
I: IntoIterator,
I::Item: Into<File>,
{
let it = files.into_iter();
let mut urls = IndexSet::with_capacity(it.size_hint().0);
let mut files = IndexSet::with_capacity(it.size_hint().0);
for file in it.map(Into::into) {
if !file.url.is_absolute() {
continue;
} else if let Some(cache) = file.url.cache_bucket() {
urls.insert(cache.into());
}
urls.insert(file.url.clone());
files.insert(file.into());
}
self.last.set(urls);
self.refresher.sync(files);
}
pub fn report<'a, I>(&self, urls: I)
where
I: IntoIterator,
I::Item: Into<UrlCow<'a>>,
{
self.reporter.report(urls);
}
async fn run(last: LastValue<IndexSet<UrlBuf>>, mut backend: Backend) {
loop {
let (to_unwatch, to_watch) = Self::diff(last.get().await).await;
if !to_unwatch.is_empty() || !to_watch.is_empty() {
backend = Self::sync(backend, to_unwatch, to_watch).await;
backend = backend.sync().await;
}
}
}
async fn diff(urls: IndexSet<UrlBuf>) -> (Vec<Watchee<'static>>, IndexSet<Watchee<'static>>) {
let mut new: IndexSet<_> = stream::iter(urls).then(Watchee::new).collect().await;
let old = WATCHED.read();
let to_unwatch = old.difference(&new).map(Watchee::to_static).collect();
new.retain(|watchee| !old.contains(watchee));
(to_unwatch, new)
}
async fn sync(
mut backend: Backend,
to_unwatch: Vec<Watchee<'static>>,
to_watch: IndexSet<Watchee<'static>>,
) -> Backend {
tokio::task::spawn_blocking(move || {
for watchee in to_unwatch {
match backend.unwatch(&watchee) {
Ok(()) => _ = WATCHED.write().remove(&watchee),
Err(e) => error!("Unwatch failed: {e:?}"),
}
}
for mut watchee in to_watch {
match backend.watch(&mut watchee) {
Ok(()) => WATCHED.write().insert(watchee),
Err(e) if matches!(e.kind, notify::ErrorKind::PathNotFound) => {}
Err(e) => error!("Watch failed: {e:?}"),
}
}
backend
})
.await
.unwrap()
}
}