caveman/cave/src/live_file.rs

69 lines
2.4 KiB
Rust

use std::sync::Arc;
use futures::{Future, StreamExt};
use tokio::fs::File;
use tokio::sync::RwLock;
use inotify::{Inotify, WatchMask};
pub async fn load<F, R, T>(path: &str, f: F) -> Result<Arc<RwLock<T>>, tokio::io::Error>
where
F: Fn(File) -> R + Send + Sync + 'static,
R: Future<Output = T> + Send,
T: Send + Sync + 'static,
{
let f = Arc::new(f);
let t = f(File::open(path).await?).await;
let lock = Arc::new(RwLock::new(t));
let lock_ = lock.clone();
let path = std::env::current_dir()
.unwrap()
.join(path).clone();
let dir = path.parent().unwrap().to_path_buf();
let path = Arc::new(RwLock::new(path));
let dir = Arc::new(RwLock::new(dir));
tokio::spawn(async move {
let inotify = Inotify::init()
.unwrap();
inotify.watches().add(&*dir.read().await, WatchMask::MODIFY | WatchMask::CREATE | WatchMask::MOVED_TO)
.unwrap();
tracing::debug!("Watching directory {:?}", &dir);
inotify.into_event_stream([0; 1024])
.unwrap()
.for_each(|result| {
let path = path.clone();
let dir = dir.clone();
let f = f.clone();
let lock = lock_.clone();
async move {
match result {
Ok(e) => {
if let Some(name) = e.name {
let dir = dir.read().await;
let path = path.read().await;
if dir.join(name) == *path {
match File::open(&*path).await {
Ok(file) => {
let t = f(file).await;
*lock.write().await = t;
tracing::info!("reloaded {}", path.display());
}
Err(e) =>
tracing::error!("error opening {}: {}", path.display(), e),
}
}
}
}
Err(e) => {
tracing::error!("inotify: {}", e);
}
}
}
}).await;
});
Ok(lock)
}