use indexmap::map::IndexMap;
use std::convert::TryFrom;
use std::fmt;
use std::fs::Metadata;
use std::io::Error;
use std::io::Result;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::path::PathBuf;
use std::time::SystemTime;
mod extract;
mod iter;
pub(crate) use extract::ExtractStale;
pub use iter::IntoIter;
pub use iter::IntoIterWithMeta;
pub use iter::Iter;
pub use iter::IterMut;
pub use iter::IterWithMeta;
pub use iter::IterWithMetaMut;
pub use iter::Paths;
#[derive(Clone)]
pub struct DirIndex<T = ()> {
pub(crate) entries: IndexMap<PathBuf, IndexedFile<T>>,
}
#[derive(Clone, Debug)]
pub(crate) struct IndexedFile<T> {
pub(crate) meta: IndexedMeta,
pub(crate) data: T,
pub(crate) stale: bool,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct IndexedMeta {
pub size: u64,
pub modified: SystemTime,
}
impl<T> DirIndex<T> {
#[inline]
pub fn new() -> Self {
Self {
entries: IndexMap::new(),
}
}
#[inline]
pub fn get<P: AsRef<Path>>(&self, path: P) -> Option<&T> {
self.entries.get(path.as_ref()).map(|entry| &entry.data)
}
#[inline]
pub fn get_mut<P: AsRef<Path>>(&mut self, path: P) -> Option<&mut T> {
self.entries
.get_mut(path.as_ref())
.map(|entry| &mut entry.data)
}
#[inline]
pub fn get_with_meta<P: AsRef<Path>>(&self, path: P) -> Option<(&IndexedMeta, &T)> {
self.entries
.get(path.as_ref())
.map(|entry| (&entry.meta, &entry.data))
}
#[inline]
pub fn get_with_meta_mut<P: AsRef<Path>>(
&mut self,
path: P,
) -> Option<(&mut IndexedMeta, &mut T)> {
self.entries
.get_mut(path.as_ref())
.map(|entry| (&mut entry.meta, &mut entry.data))
}
#[inline]
pub fn insert(
&mut self,
path: PathBuf,
meta: IndexedMeta,
data: T,
) -> Option<(IndexedMeta, T)> {
self.entries
.insert(
path,
IndexedFile {
meta,
data,
stale: false,
},
)
.map(|entry| (entry.meta, entry.data))
}
#[inline]
pub fn remove<P: AsRef<Path>>(&mut self, path: P) -> Option<T> {
self.entries.remove(path.as_ref()).map(|entry| entry.data)
}
#[inline]
pub fn remove_with_meta<P: AsRef<Path>>(&mut self, path: P) -> Option<(IndexedMeta, T)> {
self.entries
.remove(path.as_ref())
.map(|entry| (entry.meta, entry.data))
}
#[inline]
pub fn len(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[inline]
pub fn iter(&self) -> Iter<'_, T> {
Iter::new(self)
}
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
IterMut::new(self)
}
#[inline]
pub fn iter_with_meta(&self) -> IterWithMeta<'_, T> {
IterWithMeta::new(self)
}
#[inline]
pub fn iter_with_meta_mut(&mut self) -> IterWithMetaMut<'_, T> {
IterWithMetaMut::new(self)
}
#[inline]
pub fn into_iter_with_meta(self) -> IntoIterWithMeta<T> {
IntoIterWithMeta::new(self)
}
#[inline]
pub fn paths(&self) -> Paths<'_, T> {
Paths::new(self)
}
#[inline]
pub fn sort(&mut self) {
self.entries.sort_keys();
}
pub(crate) fn mark_stale(&mut self) {
for value in self.entries.values_mut() {
value.stale = true;
}
}
#[inline]
pub(crate) fn extract_stale(&self) -> ExtractStale<T> {
ExtractStale::new()
}
#[inline]
pub(crate) fn touch<P: AsRef<Path>>(&mut self, path: P) {
if let Some(entry) = self.entries.get_mut(path.as_ref()) {
entry.stale = false;
}
}
}
impl<T> Default for DirIndex<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> IntoIterator for DirIndex<T> {
type Item = (PathBuf, T);
type IntoIter = IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter::new(self)
}
}
impl<'i, T> IntoIterator for &'i DirIndex<T> {
type Item = (&'i Path, &'i T);
type IntoIter = Iter<'i, T>;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter::new(self)
}
}
impl<'i, T> IntoIterator for &'i mut DirIndex<T> {
type Item = (&'i Path, &'i mut T);
type IntoIter = IterMut<'i, T>;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter::new(self)
}
}
impl<T> fmt::Debug for DirIndex<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct Entries(usize);
impl fmt::Debug for Entries {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 == 1 {
write!(f, "{} entry", self.0)
} else {
write!(f, "{} entries", self.0)
}
}
}
f.debug_struct("DirIndex")
.field("entries", &Entries(self.len()))
.finish()
}
}
impl TryFrom<Metadata> for IndexedMeta {
type Error = Error;
fn try_from(md: Metadata) -> Result<Self> {
Ok(IndexedMeta {
size: md.size(),
modified: md.modified()?,
})
}
}
impl TryFrom<&Metadata> for IndexedMeta {
type Error = Error;
fn try_from(md: &Metadata) -> Result<Self> {
Ok(IndexedMeta {
size: md.size(),
modified: md.modified()?,
})
}
}