use std::{
collections::BTreeMap,
fs,
ops::Bound,
path::{Path, PathBuf},
time::SystemTime,
};
#[derive(Debug, Default)]
pub struct OverlayState {
entries: BTreeMap<String, OverlayEntry>,
}
impl OverlayState {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub(super) fn get(&self, relative_path: &str) -> Option<&OverlayEntry> {
self.entries.get(relative_path)
}
pub(super) fn get_mut(&mut self, relative_path: &str) -> Option<&mut OverlayEntry> {
self.entries.get_mut(relative_path)
}
pub(super) fn remove(&mut self, relative_path: &str) -> Option<OverlayEntry> {
self.entries.remove(relative_path)
}
pub(super) fn insert(&mut self, relative_path: String, entry: OverlayEntry) {
self.entries.insert(relative_path, entry);
}
pub(super) fn prefix_iter(&self, prefix: &str) -> impl Iterator<Item = (&str, &OverlayEntry)> {
debug_assert!(prefix.is_empty() || prefix.ends_with('/'));
let upper_storage;
let bounds: (Bound<&str>, Bound<&str>) = if prefix.is_empty() {
(Bound::Unbounded, Bound::Unbounded)
} else {
upper_storage = {
let mut upper = prefix.to_owned();
upper.pop();
upper.push('0');
upper
};
(Bound::Included(prefix), Bound::Excluded(upper_storage.as_str()))
};
self.entries
.range::<str, _>(bounds)
.map(|(key, value)| (key.as_str(), value))
}
}
#[derive(Debug)]
pub(super) enum OverlayEntry {
File(OverlayFile),
RealFileRef(OverlayFileRef),
Directory {
mtime: f64,
},
Deleted,
}
#[derive(Debug)]
pub(super) struct OverlayFile {
pub content: Vec<u8>,
pub mtime: f64,
}
#[derive(Debug)]
pub(super) struct OverlayFileRef {
pub host_path: PathBuf,
pub mtime: f64,
pub size: i64,
}
impl OverlayFileRef {
#[must_use]
pub fn from_host_path(path: &Path) -> Option<Self> {
let metadata = fs::metadata(path).ok()?;
let mtime = metadata
.modified()
.unwrap_or(SystemTime::UNIX_EPOCH)
.duration_since(SystemTime::UNIX_EPOCH)
.map_or(0.0, |duration| duration.as_secs_f64());
let size = i64::try_from(metadata.len()).unwrap_or(i64::MAX);
Some(Self {
host_path: path.to_path_buf(),
mtime,
size,
})
}
#[must_use]
pub fn from_lstat(path: &Path) -> Option<Self> {
let metadata = fs::symlink_metadata(path).ok()?;
let mtime = metadata
.modified()
.unwrap_or(SystemTime::UNIX_EPOCH)
.duration_since(SystemTime::UNIX_EPOCH)
.map_or(0.0, |duration| duration.as_secs_f64());
let size = i64::try_from(metadata.len()).unwrap_or(i64::MAX);
Some(Self {
host_path: path.to_path_buf(),
mtime,
size,
})
}
}