use std::sync::Arc;
#[cfg(feature = "s3-sync")]
use vfs_sync_host::HostSyncManager;
pub trait SyncHooks: Send + Sync {
fn on_write(&self, path: &str);
fn on_delete(&self, path: &str);
fn on_truncate(&self, path: &str);
fn on_read(&self, path: &str);
fn on_open(&self, path: &str);
}
pub struct NoOpSyncHooks;
impl SyncHooks for NoOpSyncHooks {
fn on_write(&self, _path: &str) {}
fn on_delete(&self, _path: &str) {}
fn on_truncate(&self, _path: &str) {}
fn on_read(&self, _path: &str) {}
fn on_open(&self, _path: &str) {}
}
#[cfg(feature = "s3-sync")]
pub struct S3SyncHooks {
sync_manager: Arc<HostSyncManager>,
read_from_s3: bool,
metadata_sync: bool,
}
#[cfg(feature = "s3-sync")]
impl S3SyncHooks {
pub fn new(sync_manager: Arc<HostSyncManager>) -> Self {
Self {
sync_manager,
read_from_s3: false,
metadata_sync: false,
}
}
pub fn new_with_read_mode(sync_manager: Arc<HostSyncManager>, read_from_s3: bool) -> Self {
Self {
sync_manager,
read_from_s3,
metadata_sync: false,
}
}
pub fn new_with_options(
sync_manager: Arc<HostSyncManager>,
read_from_s3: bool,
metadata_sync: bool,
) -> Self {
Self {
sync_manager,
read_from_s3,
metadata_sync,
}
}
}
#[cfg(feature = "s3-sync")]
impl SyncHooks for S3SyncHooks {
fn on_write(&self, path: &str) {
if self.sync_manager.is_realtime() {
let sync = self.sync_manager.clone();
let path = path.to_string();
let handle = std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
if let Err(e) = sync.sync_file_now(&path).await {
log::error!("[sync] RealTime sync failed for {}: {}", path, e);
}
});
});
if let Err(e) = handle.join() {
log::error!("[sync] RealTime sync thread panicked: {:?}", e);
}
} else {
self.sync_manager.enqueue_upload(path.to_string());
}
}
fn on_delete(&self, path: &str) {
self.sync_manager.enqueue_delete(path.to_string());
}
fn on_truncate(&self, path: &str) {
if self.sync_manager.is_realtime() {
let sync = self.sync_manager.clone();
let path = path.to_string();
let handle = std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
if let Err(e) = sync.sync_file_now(&path).await {
log::error!("[sync] RealTime sync failed for {}: {}", path, e);
}
});
});
if let Err(e) = handle.join() {
log::error!("[sync] RealTime sync thread panicked: {:?}", e);
}
} else {
self.sync_manager.enqueue_upload(path.to_string());
}
}
fn on_read(&self, path: &str) {
if self.read_from_s3 {
let sync = self.sync_manager.clone();
let path = path.to_string();
let handle = std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
if let Err(e) = sync.refresh_file_from_s3(&path).await {
log::error!("[sync] S3 refresh failed for {}: {}", path, e);
}
});
});
if let Err(e) = handle.join() {
log::error!("[sync] S3 refresh thread panicked: {:?}", e);
}
}
}
fn on_open(&self, path: &str) {
if self.metadata_sync {
let sync = self.sync_manager.clone();
let path = path.to_string();
let handle = std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
match sync.check_and_refresh_from_s3(&path).await {
Ok(refreshed) => {
if refreshed {
log::debug!("[sync] File refreshed on open: {}", path);
}
}
Err(e) => {
log::error!("[sync] S3 metadata check failed for {}: {}", path, e);
}
}
});
});
if let Err(e) = handle.join() {
log::error!("[sync] S3 metadata check thread panicked: {:?}", e);
}
}
}
}