#![doc = include_str!("../README.md")]
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher};
use std::{
fs::read,
mem::take,
ops::Deref,
path::PathBuf,
sync::{OnceLock, RwLock, RwLockReadGuard},
};
#[derive(Debug)]
pub struct Module {
path: &'static str,
current: RwLock<Vec<u8>>,
next: RwLock<Option<Vec<u8>>>,
watcher: OnceLock<RecommendedWatcher>,
}
#[derive(Debug)]
pub struct Guard(RwLockReadGuard<'static, Vec<u8>>);
impl Deref for Guard {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Module {
pub const fn new(path: &'static str) -> Self {
Self {
path,
current: RwLock::new(Vec::new()),
next: RwLock::new(None),
watcher: OnceLock::new(),
}
}
pub fn load(&'static self) -> Guard {
self.watcher.get_or_init(|| {
let mut lock = self.current.write().unwrap();
*lock = read(self.path).unwrap();
let mut watcher = RecommendedWatcher::new(
|result| {
if let Ok(Event { kind, .. }) = result
&& kind.is_modify()
&& let Ok(mut content) = self.next.write()
{
*content = Some(read(self.path).unwrap());
}
},
Default::default(),
)
.unwrap();
watcher
.watch(&PathBuf::from(self.path), RecursiveMode::NonRecursive)
.unwrap();
watcher
});
if let Ok(mut content) = self.next.try_write()
&& let Some(content) = take(&mut *content)
&& let Ok(mut current) = self.current.try_write()
{
*current = content;
}
Guard(self.current.read().unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_file() {
static FOO: Module = Module::new("test/foo.txt");
assert_eq!(&*FOO.load(), "foo\n".as_bytes());
}
}