zip_extensions/deflate/
preserve_symlinks_handler.rs

1use crate::default_entry_handler::DefaultEntryHandler;
2use crate::entry_handler::EntryHandler;
3use crate::file_utils::make_relative_path;
4use std::io;
5use std::io::Write;
6use std::path::PathBuf;
7use zip::ZipWriter;
8use zip::result::ZipResult;
9use zip::write::{FileOptionExtension, FileOptions};
10
11/// An `EntryHandler` wrapper that preserves symlinks when zipping.
12///
13/// If the current entry is a symbolic link, it writes a symlink entry to the ZIP. Otherwise, it
14/// delegates to the wrapped `inner` handler. By default, the inner handler
15/// is `DefaultEntryHandler`, but you can compose multiple behaviors by wrapping another handler
16/// instead.
17pub struct PreserveSymlinksHandler<H = DefaultEntryHandler> {
18    inner: H,
19}
20
21impl PreserveSymlinksHandler<DefaultEntryHandler> {
22    pub fn new() -> Self {
23        Self {
24            inner: DefaultEntryHandler,
25        }
26    }
27
28    pub fn with_default_inner() -> Self {
29        Self::new()
30    }
31}
32
33impl<H> PreserveSymlinksHandler<H> {
34    pub fn with_inner(inner: H) -> Self {
35        Self { inner }
36    }
37}
38
39impl<T: FileOptionExtension, H> EntryHandler<T> for PreserveSymlinksHandler<H>
40where
41    H: EntryHandler<T>,
42{
43    fn handle_entry<W: Write + io::Seek>(
44        &self,
45        writer: &mut ZipWriter<W>,
46        root: &PathBuf,
47        entry_path: &PathBuf,
48        file_options: FileOptions<T>,
49        buffer: &mut Vec<u8>,
50    ) -> ZipResult<()> {
51        let symlink_metadata = std::fs::symlink_metadata(entry_path)?;
52        let relative = make_relative_path(root, entry_path);
53
54        if symlink_metadata.is_symlink() {
55            let target = std::fs::read_link(entry_path)?;
56            writer.add_symlink(
57                relative.to_str().unwrap(),
58                target.to_str().unwrap(),
59                file_options,
60            )?;
61            return Ok(());
62        }
63
64        self.inner
65            .handle_entry(writer, root, entry_path, file_options, buffer)
66    }
67}