mc_repack_core/entry/
mod.rs

1
2/// Entry reader and saver for a file system.
3pub mod fs;
4/// Entry reader and saver for ZIP archives.
5pub mod zip;
6
7use std::sync::Arc;
8use crate::{cfg, errors::ErrorCollector, fop::{FileOp, TypeBlacklist}, ProgressState};
9
10use bytes::Bytes;
11pub use fs::{FSEntryReader, FSEntrySaver};
12pub use zip::{ZipEntryReader, ZipEntrySaver};
13
14/// Reads entries from a file-based system. Typically used with `EntrySaver`.
15pub trait EntryReader {
16    /// A type for reading entries.
17    type RE<'a>: ReadEntry where Self: 'a;
18
19    /// Reads the next entry.
20    fn read_next(&mut self) -> Option<Self::RE<'_>>;
21
22    /// Returns the number of entries.
23    fn read_len(&self) -> usize;
24
25    /// Creates an iterator for reading entries.
26    fn read_iter(&mut self) -> ReadEntryIter<Self> where Self: Sized { ReadEntryIter(self) }
27
28    /// Reads entries, checks if they are not blacklisted and sends them via `tx`.
29    fn read_entries(
30        mut self,
31        mut tx: impl FnMut(NamedEntry) -> crate::Result_<()>,
32        blacklist: &TypeBlacklist
33    ) -> crate::Result_<()> where Self: Sized {
34        for re in self.read_iter() {
35            let Some(ne) = read_entry::<Self>(re, blacklist)? else { continue };
36            tx(ne)?;
37        }
38        Ok(())
39    }
40}
41
42/// Reads an entry from an [`EntryReader`].
43pub fn read_entry<R: EntryReader>(re: R::RE<'_>, blacklist: &TypeBlacklist) -> crate::Result_<Option<NamedEntry>> {
44    let (is_dir, name) = re.meta();
45    let Some(is_dir) = is_dir else { return Ok(None) };
46    let et = if is_dir {
47        NamedEntry::dir(name)
48    } else {
49        let fop = FileOp::by_name(&name, blacklist);
50        if let FileOp::Ignore(_) = fop {
51            return Ok(None);
52        }
53        NamedEntry::file(name, re.data()?, fop)
54    };
55    Ok(Some(et))
56}
57
58/// An iterator for reading entries from an entry reader.
59#[repr(transparent)]
60pub struct ReadEntryIter<'a, R: EntryReader>(&'a mut R);
61impl <'a, R: EntryReader + 'a> Iterator for ReadEntryIter<'a, R> {
62    type Item = R::RE<'a>;
63
64    #[inline]
65    fn next(&mut self) -> Option<Self::Item> {
66        // SAFETY: As long as R has a lifetime, it is safe to call next() on it.
67        unsafe { std::mem::transmute(self.0.read_next()) }
68    }
69}
70
71/// A trait for reading entries data. While the [`ReadEntry::data()`] is called, this entry is consumed.
72pub trait ReadEntry {
73    /// Returns the entry metadata.
74    fn meta(&self) -> (Option<bool>, Box<str>);
75
76    /// Reads the entry data.
77    fn data(self) -> crate::Result_<Bytes>;
78}
79
80/// Saves entries in a file-based system. Typically used with `EntryReader`.
81pub trait EntrySaver {
82    /// Saves an entry.
83    fn save(&mut self, name: &str, entry: SavingEntry) -> crate::Result_<()>;
84
85    /// Receives entries from `rx`, optimizes, sends progress (via `ps`), and saves them.
86    /// Errors are collected with entry names.
87    fn save_entries(
88        mut self,
89        rx: impl IntoIterator<Item = NamedEntry>,
90        ev: &mut ErrorCollector,
91        cfgmap: &cfg::ConfigMap,
92        mut ps: impl FnMut(ProgressState) -> crate::Result_<()>,
93    ) -> crate::Result_<()> where Self: Sized {
94        let mut cv = Vec::new();
95        for (n, ne) in rx.into_iter().enumerate() {
96            ps(ProgressState::Push(n, ne.0.clone()))?;
97            if let Some(se) = process_entry(&mut cv, &ne, ev, cfgmap) {
98                self.save(&ne.0, se)?;
99            }
100        }
101        ps(ProgressState::Finish)
102    }
103}
104
105/// Saves an entry with the [`EntrySaver`].
106pub fn process_entry<'a>(
107    cbuf: &'a mut Vec<u8>,
108    NamedEntry(name, et): &'a NamedEntry, 
109    ev: &mut ErrorCollector,
110    cfgmap: &cfg::ConfigMap,
111) -> Option<SavingEntry<'a>> {
112    let se = match et {
113        EntryType::Directory => SavingEntry::Directory,
114        EntryType::File(buf, fop) => {
115            match fop {
116                FileOp::Ignore(e) => {
117                    ev.collect(name.clone(), e.clone().into());
118                    return None;
119                }
120                FileOp::Minify(m) => {
121                    let buf: &[u8] = match m.minify(cfgmap, buf, cbuf) {
122                        Ok(()) => cbuf,
123                        Err(e) => {
124                            ev.collect(name.clone(), e);
125                            buf
126                        }
127                    };
128                    SavingEntry::File(buf, m.compress_min())
129                }
130                FileOp::Recompress(x) => SavingEntry::File(buf, *x as u16),
131                FileOp::Pass => SavingEntry::File(buf, 24)
132            }
133        }
134    };
135    Some(se)
136    //saver.save(&name, se)?;
137    //Ok(())
138}
139
140/// An entry with its name and type.
141pub struct NamedEntry(pub Arc<str>, pub EntryType);
142impl NamedEntry {
143    /// A shorthand function for creating a directory entry
144    #[inline]
145    pub fn dir(name: impl Into<Arc<str>>) -> Self {
146        Self(name.into(), EntryType::Directory)
147    }
148
149    /// A shorthand function for creating a file entry
150    #[inline]
151    pub fn file(name: impl Into<Arc<str>>, data: impl Into<Bytes>, fop: FileOp) -> Self {
152        Self(name.into(), EntryType::File(data.into(), fop))
153    }
154}
155
156/// An entry type based on extracted data from an archive
157#[derive(Clone)]
158pub enum EntryType {
159    /// A directory with its path
160    Directory,
161    /// A file with its path, data and file operation
162    File(Bytes, FileOp)
163}
164
165/// A type for saving entries.
166pub enum SavingEntry<'a> {
167    /// A directory
168    Directory,
169    /// A file with data and a minimum compression constraint
170    File(&'a [u8], u16)
171}