tinymist_package/pack/
fs.rs

1use std::{fs::File, io::Write};
2
3use super::*;
4
5/// A package in the directory.
6#[derive(Clone)]
7pub struct DirPack<P> {
8    /// The patch storing the package.
9    pub path: P,
10}
11
12impl<P: AsRef<Path>> DirPack<P> {
13    /// Creates a new `DirPack` instance.
14    pub fn new(path: P) -> Self {
15        Self { path }
16    }
17}
18
19impl<P: AsRef<Path>> fmt::Debug for DirPack<P> {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        write!(f, "DirPack({})", self.path.as_ref().display())
22    }
23}
24
25impl<P: AsRef<Path>> PackFs for DirPack<P> {
26    fn read_all(
27        &mut self,
28        f: &mut (dyn FnMut(&str, PackFile) -> PackageResult<()> + Send + Sync),
29    ) -> PackageResult<()> {
30        self.filter(|_| true).read_all(f)
31    }
32}
33
34impl<P: AsRef<Path>> Pack for DirPack<P> {}
35impl<P: AsRef<Path>> PackExt for DirPack<P> {
36    fn filter(&mut self, f: impl Fn(&str) -> bool + Send + Sync) -> impl Pack
37    where
38        Self: std::marker::Sized,
39    {
40        FilterDirPack {
41            path: &self.path,
42            f,
43        }
44    }
45}
46
47impl<P: AsRef<Path>> CloneIntoPack for DirPack<P> {
48    fn clone_into_pack(&mut self, pack: &mut impl PackFs) -> std::io::Result<()> {
49        let base = self.path.as_ref();
50        pack.read_all(&mut |path, file| {
51            let path = base.join(path);
52            std::fs::create_dir_all(path.parent().unwrap()).map_err(other)?;
53            let mut dst = std::fs::File::create(path).map_err(other)?;
54            match file {
55                PackFile::Read(mut reader) => {
56                    std::io::copy(&mut reader, &mut dst).map_err(other)?;
57                }
58                PackFile::Data(data) => {
59                    dst.write_all(&data.into_inner()).map_err(other)?;
60                }
61            }
62
63            Ok(())
64        })
65        .map_err(other_io)?;
66        Ok(())
67    }
68}
69
70struct FilterDirPack<'a, P, F> {
71    /// The patch storing the package.
72    pub path: &'a P,
73    /// The filter function.
74    pub f: F,
75}
76
77impl<S: AsRef<Path>, F> fmt::Debug for FilterDirPack<'_, S, F> {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "FilterDirPack({:?}, ..)", self.path.as_ref())
80    }
81}
82impl<Src: AsRef<Path>, F: Fn(&str) -> bool + Send + Sync> PackFs for FilterDirPack<'_, Src, F> {
83    fn read_all(
84        &mut self,
85        f: &mut (dyn FnMut(&str, PackFile) -> PackageResult<()> + Send + Sync),
86    ) -> PackageResult<()> {
87        let w = walkdir::WalkDir::new(self.path.as_ref())
88            .follow_links(true)
89            .into_iter()
90            .filter_entry(|e| !e.file_name().to_string_lossy().starts_with('.'))
91            .filter_map(|e| e.ok())
92            .filter(|e| e.file_type().is_file());
93
94        for entry in w {
95            let path = entry.path();
96            let rel_path = path.strip_prefix(self.path.as_ref()).map_err(other)?;
97
98            let file_path = rel_path.to_string_lossy();
99            if !(self.f)(&file_path) {
100                continue;
101            }
102
103            let pack_file = PackFile::Read(Box::new(File::open(path).map_err(other)?));
104            f(&file_path, pack_file)?;
105        }
106
107        Ok(())
108    }
109}
110
111impl<Src: AsRef<Path>, F: Fn(&str) -> bool + Send + Sync> Pack for FilterDirPack<'_, Src, F> {}