use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::time::SystemTime;
pub struct MtimeCache<T> {
inner: Mutex<Option<MtimeCacheInner<T>>>,
}
pub struct MtimeCacheBuilder {
file_mtimes: Vec<(PathBuf, Option<SystemTime>)>,
}
struct MtimeCacheInner<T> {
builder: MtimeCacheBuilder,
cached: T,
}
impl<T: Clone> MtimeCache<T> {
pub fn new() -> Self {
MtimeCache {
inner: Mutex::new(None),
}
}
pub fn get_or_try_init<E>(
&self,
init: impl FnOnce(&mut MtimeCacheBuilder) -> Result<T, E>,
) -> Result<T, E> {
let mut inner = self.inner.lock().unwrap_or_else(|e| {
let mut locked = e.into_inner();
*locked = None;
locked
});
if let Some(inner) = &*inner {
if !inner.builder.any_modified() {
return Ok(inner.cached.clone());
}
}
let mut builder = MtimeCacheBuilder::new();
let value = init(&mut builder)?;
*inner = Some(MtimeCacheInner {
builder,
cached: value.clone(),
});
Ok(value)
}
}
impl MtimeCacheBuilder {
fn new() -> Self {
MtimeCacheBuilder {
file_mtimes: Vec::new(),
}
}
pub fn add_path(&mut self, path: PathBuf) {
let mtime = get_mtime(&path);
#[cfg(any(sqlx_macros_unstable, procmacro2_semver_exempt))]
{
proc_macro::tracked_path::path(&path);
}
self.file_mtimes.push((path, mtime));
}
fn any_modified(&self) -> bool {
for (path, expected_mtime) in &self.file_mtimes {
let actual_mtime = get_mtime(path);
if expected_mtime != &actual_mtime {
return true;
}
}
false
}
}
fn get_mtime(path: &Path) -> Option<SystemTime> {
std::fs::metadata(path)
.and_then(|metadata| metadata.modified())
.ok()
}