use crate::store::{Access, Store, StoreError};
use std::cell::RefCell;
use std::path::{Path, PathBuf};
const LET_GO: bool = cfg!(windows);
pub(crate) struct HeldIndex {
path: PathBuf,
held: RefCell<Option<Held>>,
}
struct Held {
store: Store,
file: FileIdentity,
}
impl HeldIndex {
pub(crate) fn new(path: &Path) -> Self {
Self {
path: path.to_owned(),
held: RefCell::new(None),
}
}
pub(crate) fn with<R>(
&self,
read: impl FnOnce(&Store) -> Result<R, StoreError>,
) -> Result<R, StoreError> {
let file = FileIdentity::of(&self.path);
let mut held = self.held.take();
if held
.as_ref()
.is_some_and(|held| file.is_none() || held.file != file)
{
held = None;
}
let held = match held {
Some(held) => held,
None => Held {
store: Store::open(&self.path, Access::Read)?,
file,
},
};
let result = read(&held.store);
if result.is_ok() && !LET_GO {
self.held.replace(Some(held));
}
result
}
pub(crate) fn path(&self) -> &Path {
&self.path
}
pub(crate) fn adopt(&self, store: Store, file: FileIdentity) {
if !LET_GO {
self.held.replace(Some(Held { store, file }));
}
}
pub(crate) fn release(&self) {
self.held.take();
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct FileIdentity(Option<(u64, u64)>);
impl FileIdentity {
#[cfg(unix)]
pub(crate) fn of(path: &Path) -> Self {
use std::os::unix::fs::MetadataExt;
Self(
std::fs::metadata(path)
.ok()
.map(|metadata| (metadata.dev(), metadata.ino())),
)
}
#[cfg(not(unix))]
pub(crate) fn of(_path: &Path) -> Self {
Self(Some((0, 0)))
}
fn is_none(&self) -> bool {
self.0.is_none()
}
}