Files
claudia/src-tauri/src/file_watcher.rs
2025-08-10 21:44:48 +08:00

195 lines
6.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime};
use tauri::{AppHandle, Emitter};
#[derive(Debug, Clone, serde::Serialize)]
pub struct FileChangeEvent {
pub path: String,
pub change_type: String,
pub timestamp: u64,
}
pub struct FileWatcherManager {
watchers: Arc<Mutex<HashMap<String, RecommendedWatcher>>>,
app_handle: AppHandle,
// 用于去重,避免短时间内重复事件
last_events: Arc<Mutex<HashMap<PathBuf, SystemTime>>>,
}
impl FileWatcherManager {
pub fn new(app_handle: AppHandle) -> Self {
Self {
watchers: Arc::new(Mutex::new(HashMap::new())),
app_handle,
last_events: Arc::new(Mutex::new(HashMap::new())),
}
}
/// 监听指定路径(文件或目录)
pub fn watch_path(&self, path: &str, recursive: bool) -> Result<(), String> {
let path_buf = PathBuf::from(path);
// 检查路径是否存在
if !path_buf.exists() {
return Err(format!("Path does not exist: {}", path));
}
// 检查是否已经在监听
{
let watchers = self.watchers.lock().unwrap();
if watchers.contains_key(path) {
log::debug!("Already watching path: {}", path);
return Ok(());
}
}
let app_handle = self.app_handle.clone();
let last_events = self.last_events.clone();
let watch_path = path.to_string();
// 创建文件监听器
let mut watcher = RecommendedWatcher::new(
move |res: Result<Event, notify::Error>| {
match res {
Ok(event) => {
Self::handle_event(event, &app_handle, &last_events);
}
Err(e) => {
log::error!("Watch error: {:?}", e);
}
}
},
Config::default()
.with_poll_interval(Duration::from_secs(1))
.with_compare_contents(false),
).map_err(|e| format!("Failed to create watcher: {}", e))?;
// 开始监听
let mode = if recursive {
RecursiveMode::Recursive
} else {
RecursiveMode::NonRecursive
};
watcher
.watch(&path_buf, mode)
.map_err(|e| format!("Failed to watch path: {}", e))?;
// 存储监听器
let mut watchers = self.watchers.lock().unwrap();
watchers.insert(watch_path, watcher);
log::info!("Started watching path: {} (recursive: {})", path, recursive);
Ok(())
}
/// 停止监听指定路径
pub fn unwatch_path(&self, path: &str) -> Result<(), String> {
let mut watchers = self.watchers.lock().unwrap();
if watchers.remove(path).is_some() {
log::info!("Stopped watching path: {}", path);
Ok(())
} else {
Err(format!("Path not being watched: {}", path))
}
}
/// 停止所有监听
#[allow(dead_code)]
pub fn unwatch_all(&self) {
let mut watchers = self.watchers.lock().unwrap();
let count = watchers.len();
watchers.clear();
log::info!("Stopped watching {} paths", count);
}
/// 处理文件系统事件
fn handle_event(event: Event, app_handle: &AppHandle, last_events: &Arc<Mutex<HashMap<PathBuf, SystemTime>>>) {
// 过滤不需要的事件
let change_type = match event.kind {
EventKind::Create(_) => "created",
EventKind::Modify(_) => "modified",
EventKind::Remove(_) => "deleted",
_ => return, // 忽略其他事件(包括 Access 等)
};
// 处理每个受影响的路径
for path in event.paths {
// 去重:检查是否在短时间内已经发送过相同路径的事件
let now = SystemTime::now();
let should_emit = {
let mut last_events = last_events.lock().unwrap();
if let Some(last_time) = last_events.get(&path) {
// 如果距离上次事件不到500ms忽略
if now.duration_since(*last_time).unwrap_or(Duration::ZERO) < Duration::from_millis(500) {
false
} else {
last_events.insert(path.clone(), now);
true
}
} else {
last_events.insert(path.clone(), now);
true
}
};
if should_emit {
let change_event = FileChangeEvent {
path: path.to_string_lossy().to_string(),
change_type: change_type.to_string(),
timestamp: now
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs(),
};
// 发送事件到前端
if let Err(e) = app_handle.emit("file-system-change", &change_event) {
log::error!("Failed to emit file change event: {}", e);
} else {
log::debug!(
"Emitted file change event: {} ({})",
change_event.path,
change_event.change_type
);
}
}
}
}
/// 获取当前监听的路径列表
pub fn get_watched_paths(&self) -> Vec<String> {
let watchers = self.watchers.lock().unwrap();
watchers.keys().cloned().collect()
}
}
// 全局文件监听管理器状态
pub struct FileWatcherState(pub Arc<Mutex<Option<FileWatcherManager>>>);
impl FileWatcherState {
pub fn new() -> Self {
Self(Arc::new(Mutex::new(None)))
}
pub fn init(&self, app_handle: AppHandle) {
let mut state = self.0.lock().unwrap();
*state = Some(FileWatcherManager::new(app_handle));
}
pub fn with_manager<F, R>(&self, f: F) -> Result<R, String>
where
F: FnOnce(&FileWatcherManager) -> Result<R, String>,
{
let state = self.0.lock().unwrap();
match state.as_ref() {
Some(manager) => f(manager),
None => Err("File watcher manager not initialized".to_string()),
}
}
}