Skip to main content

lib3mf_core/archive/
zip_archive.rs

1use crate::archive::ArchiveReader;
2use crate::error::{Lib3mfError, Result};
3use std::io::{Read, Seek};
4use zip::ZipArchive;
5
6/// A ZIP-based archive reader for 3MF files.
7///
8/// Wraps a [`zip::ZipArchive`] and implements [`ArchiveReader`] for use with the 3MF parser.
9/// This is the standard entry point for reading `.3mf` files from disk or memory.
10#[derive(Debug)]
11pub struct ZipArchiver<R> {
12    archive: ZipArchive<R>,
13}
14
15impl<R: Read + Seek> ZipArchiver<R> {
16    /// Creates a new `ZipArchiver` by reading a ZIP archive from the given reader.
17    pub fn new(reader: R) -> Result<Self> {
18        Ok(Self {
19            archive: ZipArchive::new(reader).map_err(|e| Lib3mfError::Io(e.into()))?,
20        })
21    }
22}
23
24impl<R: Read + Seek> Read for ZipArchiver<R> {
25    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
26        // ZipArchiver itself doesn't implement Read in a meaningful way for the whole archive,
27        // but we need to satisfy the trait bounds if we passed it around.
28        // For now, this is a placeholder or we might remove Read from ArchiveReader if not strictly needed.
29        // However, ArchiveReader inherits Read + Seek to allow flexibility.
30        // A better design might be to separate the "Opener" from the "Reader".
31        // Let's implement dummy Read/Seek for the Archiver wrapper or rethink the trait.
32
33        // Actually, looking at the design, ArchiveReader requires Read+Seek.
34        // This implies the *underlying* reader has it, but the Archiver *is* the manager.
35        // Let's implement pass-through if we have access, or just return 0.
36        Ok(0)
37    }
38}
39
40impl<R: Read + Seek> Seek for ZipArchiver<R> {
41    fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
42        Ok(0)
43    }
44}
45
46impl<R: Read + Seek> ArchiveReader for ZipArchiver<R> {
47    fn read_entry(&mut self, name: &str) -> Result<Vec<u8>> {
48        let name = name.trim_start_matches('/');
49        let mut file = self.archive.by_name(name).map_err(|_| {
50            Lib3mfError::Io(std::io::Error::new(std::io::ErrorKind::NotFound, name))
51        })?;
52
53        let mut buffer = Vec::new();
54        file.read_to_end(&mut buffer)?;
55        Ok(buffer)
56    }
57
58    fn entry_exists(&mut self, name: &str) -> bool {
59        let name = name.trim_start_matches('/');
60        self.archive.by_name(name).is_ok()
61    }
62
63    fn list_entries(&mut self) -> Result<Vec<String>> {
64        Ok(self.archive.file_names().map(|s| s.to_string()).collect())
65    }
66}