use crate::GLOBAL_CONFIG;
use crate::data::paths::{
BasicDirEntryInfo,
PathData,
};
use crate::library::iter_extensions::HttmIter;
use crate::lookup::versions::{
ProximateDatasetAndOptAlts,
RelativePathAndSnapMounts,
};
use hashbrown::HashSet;
use std::ffi::{
OsStr,
OsString,
};
use std::fs::FileType;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeletedFiles {
inner: HashSet<BasicDirEntryInfo>,
}
impl Default for DeletedFiles {
fn default() -> Self {
Self {
inner: HashSet::new(),
}
}
}
impl DeletedFiles {
pub fn new(requested_dir: &Path) -> Self {
let Some(mut deleted_files) = Self::unique_pseudo_live_versions(requested_dir) else {
return Self::default();
};
if let Ok(read_dir) = std::fs::read_dir(requested_dir) {
let live_paths: HashSet<OsString> = unsafe {
read_dir
.flatten()
.map(|entry| entry.file_name())
.collect_set_known_unique()
};
if !live_paths.is_empty() {
deleted_files.retain(|v| !live_paths.contains(v.filename()))
}
};
Self {
inner: deleted_files,
}
}
#[inline(always)]
pub fn into_inner(self) -> HashSet<BasicDirEntryInfo> {
self.inner
}
#[inline(always)]
fn unique_pseudo_live_versions<'a>(
requested_dir: &'a Path,
) -> Option<HashSet<BasicDirEntryInfo>> {
let path_data = PathData::without_styling(requested_dir, None);
let prox_opt_alts = ProximateDatasetAndOptAlts::new(&GLOBAL_CONFIG, &path_data).ok()?;
let pseudo_live_versions: HashSet<BasicDirEntryInfo> = prox_opt_alts
.into_search_bundles()
.fold(HashSet::new(), |mut acc, search_bundle| {
let iter = Self::deleted_paths_for_directory(&requested_dir, &search_bundle);
if acc.is_empty() {
acc = iter.collect_set_no_update();
} else {
acc.extend(iter);
}
acc
});
if pseudo_live_versions.is_empty() {
return None;
}
Some(pseudo_live_versions)
}
#[inline(always)]
fn deleted_paths_for_directory<'a>(
pseudo_live_dir: &'a Path,
search_bundle: &'a RelativePathAndSnapMounts<'a>,
) -> impl Iterator<Item = BasicDirEntryInfo> {
search_bundle
.snap_mounts()
.iter()
.map(move |path| path.join(search_bundle.relative_path()))
.flat_map(std::fs::read_dir)
.flatten()
.flatten()
.filter_map(|dir_entry| {
dir_entry
.file_type()
.ok()
.map(|file_type| (dir_entry, file_type))
})
.map(|(dir_entry, file_type)| {
let file_name = dir_entry.file_name();
let basic_info =
Self::into_pseudo_live_version(&file_name, pseudo_live_dir, Some(file_type));
basic_info
})
}
#[inline(always)]
fn into_pseudo_live_version<'a>(
file_name: &OsStr,
pseudo_live_dir: &'a Path,
opt_filetype: Option<FileType>,
) -> BasicDirEntryInfo {
let path = pseudo_live_dir.join(file_name);
BasicDirEntryInfo::new(&path, opt_filetype)
}
}