use std::{fs, io, path::Path};
use super::{EntryReader, EntrySaver, ReadEntry, SavingEntry};
type FSEntry = io::Result<(Option<bool>, Box<Path>)>;
pub struct FSEntryReader<I: ExactSizeIterator<Item = FSEntry>> {
src_dir: Box<Path>,
iter: I
}
impl FSEntryReader<std::vec::IntoIter<FSEntry>> {
pub fn new(src_dir: Box<Path>) -> Self {
let files = walkdir::WalkDir::new(src_dir.clone()).into_iter().map(check_dir_entry).collect::<Vec<_>>();
Self::from_vec(src_dir, files)
}
pub fn from_vec(src_dir: Box<Path>, files: Vec<FSEntry>) -> Self {
Self { src_dir, iter: files.into_iter() }
}
}
impl <I: ExactSizeIterator<Item = FSEntry>> FSEntryReader<I> {
pub fn custom(src_dir: Box<Path>, iter: I) -> Self {
Self { src_dir, iter }
}
}
impl <I: ExactSizeIterator<Item = FSEntry>> EntryReader for FSEntryReader<I> {
type RE<'a> = ReadFileEntry<'a> where Self: 'a;
#[inline]
fn read_len(&self) -> usize {
self.iter.len()
}
#[inline]
fn read_next(&mut self) -> Option<Self::RE<'_>> {
self.iter.next().map(|cur| ReadFileEntry { src_dir: &self.src_dir, cur })
}
}
pub struct ReadFileEntry<'a> {
src_dir: &'a Path,
cur: FSEntry
}
impl ReadEntry for ReadFileEntry<'_> {
fn meta(&self) -> (Option<bool>, Box<str>) {
let Ok((is_dir, p)) = &self.cur else { return (None, "".into()) };
let lname = if let Ok(p) = p.strip_prefix(self.src_dir) {
p.to_string_lossy().to_string()
} else {
return (None, "".into())
};
(*is_dir, lname.into_boxed_str())
}
fn data(self) -> crate::Result_<bytes::Bytes> {
match self.cur {
Ok((_, p)) => Ok(fs::read(&p)?.into()),
Err(e) => Err(e.into()),
}
}
}
pub struct FSEntrySaver {
dest_dir: Box<Path>
}
impl FSEntrySaver {
pub const fn new(dest_dir: Box<Path>) -> Self {
Self { dest_dir }
}
}
impl EntrySaver for FSEntrySaver {
fn save(&mut self, name: &str, entry: SavingEntry) -> crate::Result_<()> {
let mut np = self.dest_dir.to_path_buf();
np.push(name);
match entry {
SavingEntry::Directory => fs::create_dir(np),
SavingEntry::File(buf, _) => fs::write(np, buf),
}?;
Ok(())
}
}
fn check_dir_entry(de: walkdir::Result<walkdir::DirEntry>) -> FSEntry {
match de {
Err(e) => Err(e.into()),
Ok(de) => {
let ft = de.file_type();
let p = de.into_path().into_boxed_path();
Ok((if ft.is_dir() {
Some(true)
} else if ft.is_file() {
Some(false)
} else {
None
}, p))
}
}
}