mc_repack_core/entry/
fs.rs

1use std::{fs, io, path::Path};
2use super::{EntryReader, EntrySaver, ReadEntry, SavingEntry};
3
4type FSEntry = io::Result<(Option<bool>, Box<Path>)>;
5
6/// An entry reader implementation for a file system. It reads a file tree from a provided directory.
7pub struct FSEntryReader<I: ExactSizeIterator<Item = FSEntry>> {
8    src_dir: Box<Path>,
9    iter: I
10}
11impl FSEntryReader<std::vec::IntoIter<FSEntry>> {
12    /// Creates an entry reader with a source directory path.
13    pub fn new(src_dir: Box<Path>) -> Self {
14        let files = walkdir::WalkDir::new(src_dir.clone()).into_iter().map(check_dir_entry).collect::<Vec<_>>();
15        Self::from_vec(src_dir, files)
16    }
17    /// Creates an entry reader with a source directory path with a list of files.
18    pub fn from_vec(src_dir: Box<Path>, files: Vec<FSEntry>) -> Self {
19        Self { src_dir, iter: files.into_iter() }
20    }
21}
22impl <I: ExactSizeIterator<Item = FSEntry>> FSEntryReader<I> {
23    /// Creates an entry reader with a source directory path and a custom iterator.
24    pub fn custom(src_dir: Box<Path>, iter: I) -> Self {
25        Self { src_dir, iter }
26    }
27}
28impl <I: ExactSizeIterator<Item = FSEntry>> EntryReader for FSEntryReader<I> {
29    type RE<'a> = ReadFileEntry<'a> where Self: 'a;
30    #[inline]
31    fn read_len(&self) -> usize {
32        self.iter.len()
33    }
34    #[inline]
35    fn read_next(&mut self) -> Option<Self::RE<'_>> {
36        self.iter.next().map(|cur| ReadFileEntry { src_dir: &self.src_dir, cur })
37    }
38}
39
40/// A read entry of a file system.
41pub struct ReadFileEntry<'a> {
42    src_dir: &'a Path,
43    cur: FSEntry
44}
45impl ReadEntry for ReadFileEntry<'_> {
46    fn meta(&self) -> (Option<bool>, Box<str>) {
47        let Ok((is_dir, p)) = &self.cur else { return (None, "".into()) };
48        let lname = if let Ok(p) = p.strip_prefix(self.src_dir) {
49            p.to_string_lossy().to_string()
50        } else {
51            return (None, "".into())
52        };
53        (*is_dir, lname.into_boxed_str())
54    }
55    fn data(self) -> crate::Result_<bytes::Bytes> {
56        match self.cur {
57            Ok((_, p)) => Ok(fs::read(&p)?.into()),
58            Err(e) => Err(e.into()),
59        }
60    }
61}
62
63/// An entry saver implementation for a file system. It writes files into a provided directory.
64pub struct FSEntrySaver {
65    dest_dir: Box<Path>
66}
67impl FSEntrySaver {
68    /// Creates an entry saver with a destination directory path.
69    pub const fn new(dest_dir: Box<Path>) -> Self {
70        Self { dest_dir }
71    }
72}
73impl EntrySaver for FSEntrySaver {
74    fn save(&mut self, name: &str, entry: SavingEntry) -> crate::Result_<()> {
75        let mut np = self.dest_dir.to_path_buf();
76        np.push(name);
77        match entry {
78            SavingEntry::Directory => fs::create_dir(np),
79            SavingEntry::File(buf, _) => fs::write(np, buf),
80        }?;
81        Ok(())
82    }
83}
84
85fn check_dir_entry(de: walkdir::Result<walkdir::DirEntry>) -> FSEntry {
86    match de {
87        Err(e) => Err(e.into()),
88        Ok(de) => {
89            let ft = de.file_type();
90        let p = de.into_path().into_boxed_path();
91        Ok((if ft.is_dir() {
92            Some(true)
93        } else if ft.is_file() {
94            Some(false)
95        } else {
96            None
97        }, p))
98        }
99    }
100}