pub mod fs;
pub mod zip;
use std::sync::Arc;
use crate::{cfg, errors::ErrorCollector, fop::{FileOp, TypeBlacklist}, ProgressState};
use bytes::Bytes;
pub use fs::{FSEntryReader, FSEntrySaver};
pub use zip::{ZipEntryReader, ZipEntrySaver};
pub trait EntryReader {
type RE<'a>: ReadEntry where Self: 'a;
fn read_next(&mut self) -> Option<Self::RE<'_>>;
fn read_len(&self) -> usize;
fn read_iter(&mut self) -> ReadEntryIter<Self> where Self: Sized { ReadEntryIter(self) }
fn read_entries(
mut self,
mut tx: impl FnMut(NamedEntry) -> crate::Result_<()>,
blacklist: &TypeBlacklist
) -> crate::Result_<()> where Self: Sized {
for re in self.read_iter() {
let Some(ne) = read_entry::<Self>(re, blacklist)? else { continue };
tx(ne)?;
}
Ok(())
}
}
pub fn read_entry<R: EntryReader>(re: R::RE<'_>, blacklist: &TypeBlacklist) -> crate::Result_<Option<NamedEntry>> {
let (is_dir, name) = re.meta();
let Some(is_dir) = is_dir else { return Ok(None) };
let et = if is_dir {
NamedEntry::dir(name)
} else {
let fop = FileOp::by_name(&name, blacklist);
if let FileOp::Ignore(_) = fop {
return Ok(None);
}
NamedEntry::file(name, re.data()?, fop)
};
Ok(Some(et))
}
#[repr(transparent)]
pub struct ReadEntryIter<'a, R: EntryReader>(&'a mut R);
impl <'a, R: EntryReader + 'a> Iterator for ReadEntryIter<'a, R> {
type Item = R::RE<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe { std::mem::transmute(self.0.read_next()) }
}
}
pub trait ReadEntry {
fn meta(&self) -> (Option<bool>, Box<str>);
fn data(self) -> crate::Result_<Bytes>;
}
pub trait EntrySaver {
fn save(&mut self, name: &str, entry: SavingEntry) -> crate::Result_<()>;
fn save_entries(
mut self,
rx: impl IntoIterator<Item = NamedEntry>,
ev: &mut ErrorCollector,
cfgmap: &cfg::ConfigMap,
mut ps: impl FnMut(ProgressState) -> crate::Result_<()>,
) -> crate::Result_<()> where Self: Sized {
let mut cv = Vec::new();
for (n, ne) in rx.into_iter().enumerate() {
ps(ProgressState::Push(n, ne.0.clone()))?;
if let Some(se) = process_entry(&mut cv, &ne, ev, cfgmap) {
self.save(&ne.0, se)?;
}
}
ps(ProgressState::Finish)
}
}
pub fn process_entry<'a>(
cbuf: &'a mut Vec<u8>,
NamedEntry(name, et): &'a NamedEntry,
ev: &mut ErrorCollector,
cfgmap: &cfg::ConfigMap,
) -> Option<SavingEntry<'a>> {
let se = match et {
EntryType::Directory => SavingEntry::Directory,
EntryType::File(buf, fop) => {
match fop {
FileOp::Ignore(e) => {
ev.collect(name.clone(), e.clone().into());
return None;
}
FileOp::Minify(m) => {
let buf: &[u8] = match m.minify(cfgmap, buf, cbuf) {
Ok(()) => cbuf,
Err(e) => {
ev.collect(name.clone(), e);
buf
}
};
SavingEntry::File(buf, m.compress_min())
}
FileOp::Recompress(x) => SavingEntry::File(buf, *x as u16),
FileOp::Pass => SavingEntry::File(buf, 24)
}
}
};
Some(se)
}
pub struct NamedEntry(pub Arc<str>, pub EntryType);
impl NamedEntry {
#[inline]
pub fn dir(name: impl Into<Arc<str>>) -> Self {
Self(name.into(), EntryType::Directory)
}
#[inline]
pub fn file(name: impl Into<Arc<str>>, data: impl Into<Bytes>, fop: FileOp) -> Self {
Self(name.into(), EntryType::File(data.into(), fop))
}
}
#[derive(Clone)]
pub enum EntryType {
Directory,
File(Bytes, FileOp)
}
pub enum SavingEntry<'a> {
Directory,
File(&'a [u8], u16)
}