diff options
| author | Igor Tolmachev <me@igorek.dev> | 2024-06-27 16:15:00 +0900 |
|---|---|---|
| committer | Igor Tolmachev <me@igorek.dev> | 2024-06-29 21:18:46 +0900 |
| commit | 51694e1f0b2730915e0a57ec6d8de503cf06ef9a (patch) | |
| tree | 165f3adbc29f96814b57aeba394fb4045af7c5f0 | |
| parent | a867677218c1d55dadfcac1ca5b8cd32a78a3c28 (diff) | |
| download | archivator-51694e1f0b2730915e0a57ec6d8de503cf06ef9a.tar.gz archivator-51694e1f0b2730915e0a57ec6d8de503cf06ef9a.zip | |
Create file driver and implement file reader
| -rw-r--r-- | src/archive.rs | 20 | ||||
| -rw-r--r-- | src/driver/driver.rs | 27 | ||||
| -rw-r--r-- | src/driver/file.rs | 9 | ||||
| -rw-r--r-- | src/driver/mod.rs | 2 | ||||
| -rw-r--r-- | src/error.rs | 6 | ||||
| -rw-r--r-- | src/file.rs | 38 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/zip/archive.rs | 4 | ||||
| -rw-r--r-- | src/zip/driver.rs | 38 | ||||
| -rw-r--r-- | src/zip/error.rs | 8 | ||||
| -rw-r--r-- | src/zip/file_driver.rs | 99 | ||||
| -rw-r--r-- | src/zip/file_info.rs (renamed from src/zip/file.rs) | 11 | ||||
| -rw-r--r-- | src/zip/mod.rs | 6 | ||||
| -rw-r--r-- | src/zip/tests.rs | 2 | ||||
| -rw-r--r-- | tests/files/zip.zip | bin | 427 -> 466 bytes | |||
| -rw-r--r-- | tests/zip.rs | 33 |
16 files changed, 256 insertions, 50 deletions
diff --git a/src/archive.rs b/src/archive.rs index e635007..4a70867 100644 --- a/src/archive.rs +++ b/src/archive.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; | 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; |
| 2 | use crate::ArchiveResult; | 2 | use crate::{ArchiveFile, ArchiveResult}; |
| 3 | use std::fs::File; | 3 | use std::fs::File; |
| 4 | use std::io::{Read, Write}; | 4 | use std::io::{Read, Write}; |
| 5 | use std::path::Path; | 5 | use std::path::Path; |
| @@ -10,9 +10,9 @@ pub struct Archive<D: Driver> { | |||
| 10 | 10 | ||
| 11 | impl<D: ArchiveRead> Archive<D> | 11 | impl<D: ArchiveRead> Archive<D> |
| 12 | where | 12 | where |
| 13 | D::IO: std::io::Read, | 13 | D::Io: std::io::Read, |
| 14 | { | 14 | { |
| 15 | pub fn read(io: D::IO) -> ArchiveResult<Self, D::Error> { | 15 | pub fn read(io: D::Io) -> ArchiveResult<Self, D::Error> { |
| 16 | Ok(Self { | 16 | Ok(Self { |
| 17 | driver: D::read(io)?, | 17 | driver: D::read(io)?, |
| 18 | }) | 18 | }) |
| @@ -20,18 +20,22 @@ where | |||
| 20 | 20 | ||
| 21 | pub fn read_from_file(path: impl AsRef<Path>) -> ArchiveResult<Self, D::Error> | 21 | pub fn read_from_file(path: impl AsRef<Path>) -> ArchiveResult<Self, D::Error> |
| 22 | where | 22 | where |
| 23 | D: ArchiveRead<IO = File>, | 23 | D: ArchiveRead<Io = File>, |
| 24 | { | 24 | { |
| 25 | Self::read(File::open(path)?) | 25 | Self::read(File::open(path)?) |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | pub fn files(&self) -> Vec<&D::File> { | 28 | pub fn files(&self) -> Vec<&D::FileInfo> { |
| 29 | self.driver.files() | 29 | self.driver.files() |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | pub fn get_file(&self, name: &str) -> Option<&D::File> { | 32 | pub fn get_file_info(&self, name: &str) -> Option<&D::FileInfo> { |
| 33 | self.driver.get_file(name) | 33 | self.driver.get_file_info(name) |
| 34 | } | ||
| 35 | |||
| 36 | pub fn get_file_reader<'d>(&'d mut self, name: &str) -> Option<ArchiveFile<D::FileDriver<'d>>> { | ||
| 37 | Some(ArchiveFile::new(self.driver.get_file_reader(name)?)) | ||
| 34 | } | 38 | } |
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | impl<D: ArchiveWrite> Archive<D> where D::IO: Read + Write {} | 41 | impl<D: ArchiveWrite> Archive<D> where D::Io: Read + Write {} |
diff --git a/src/driver/driver.rs b/src/driver/driver.rs index 9c18e1f..5bd2319 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use crate::driver::ArchiveFile; | 1 | use crate::driver::{ArchiveFileInfo, FileDriver}; |
| 2 | use crate::ArchiveResult; | 2 | use crate::ArchiveResult; |
| 3 | use std::error::Error; | 3 | use std::error::Error; |
| 4 | use std::io::{Read, Write}; | 4 | use std::io::{Read, Write}; |
| @@ -6,26 +6,33 @@ use std::io::{Read, Write}; | |||
| 6 | pub trait Driver: Sized { | 6 | pub trait Driver: Sized { |
| 7 | type Error: Error; | 7 | type Error: Error; |
| 8 | 8 | ||
| 9 | type IO; | 9 | type Io; |
| 10 | type File: ArchiveFile; | 10 | type FileInfo: ArchiveFileInfo; |
| 11 | type FileDriver<'d>: FileDriver | ||
| 12 | where | ||
| 13 | Self::FileInfo: 'd, | ||
| 14 | Self::Io: 'd; | ||
| 11 | } | 15 | } |
| 12 | 16 | ||
| 13 | pub trait ArchiveRead: Driver | 17 | pub trait ArchiveRead: Driver |
| 14 | where | 18 | where |
| 15 | Self::IO: Read, | 19 | Self::Io: Read, |
| 16 | { | 20 | { |
| 17 | // Create driver instance | 21 | // Create driver instance |
| 18 | fn read(io: Self::IO) -> ArchiveResult<Self, Self::Error>; | 22 | fn read(io: Self::Io) -> ArchiveResult<Self, Self::Error>; |
| 19 | 23 | ||
| 20 | // Return vec of files (sorted by name) | 24 | // Return vec of files (sorted by name) |
| 21 | fn files(&self) -> Vec<&Self::File>; | 25 | fn files(&self) -> Vec<&Self::FileInfo>; |
| 22 | 26 | ||
| 23 | // Return file by name | 27 | // Return file info by name |
| 24 | fn get_file(&self, name: &str) -> Option<&Self::File>; | 28 | fn get_file_info(&self, name: &str) -> Option<&Self::FileInfo>; |
| 29 | |||
| 30 | // Return file reader by name | ||
| 31 | fn get_file_reader<'d>(&'d mut self, name: &str) -> Option<Self::FileDriver<'d>>; | ||
| 25 | } | 32 | } |
| 26 | 33 | ||
| 27 | pub trait ArchiveWrite: ArchiveRead | 34 | pub trait ArchiveWrite: Driver |
| 28 | where | 35 | where |
| 29 | Self::IO: Read + Write, | 36 | Self::Io: Read + Write, |
| 30 | { | 37 | { |
| 31 | } | 38 | } |
diff --git a/src/driver/file.rs b/src/driver/file.rs index a4974f3..125c9c3 100644 --- a/src/driver/file.rs +++ b/src/driver/file.rs | |||
| @@ -1 +1,8 @@ | |||
| 1 | pub trait ArchiveFile {} | 1 | pub trait ArchiveFileInfo {} |
| 2 | |||
| 3 | pub trait FileDriver { | ||
| 4 | type Io; | ||
| 5 | type FileInfo: ArchiveFileInfo; | ||
| 6 | |||
| 7 | fn info(&self) -> &Self::FileInfo; | ||
| 8 | } | ||
diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 36ee6b5..b637a34 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs | |||
| @@ -2,4 +2,4 @@ mod driver; | |||
| 2 | mod file; | 2 | mod file; |
| 3 | 3 | ||
| 4 | pub use driver::{ArchiveRead, ArchiveWrite, Driver}; | 4 | pub use driver::{ArchiveRead, ArchiveWrite, Driver}; |
| 5 | pub use file::ArchiveFile; | 5 | pub use file::{ArchiveFileInfo, FileDriver}; |
diff --git a/src/error.rs b/src/error.rs index 7172d04..97a4e62 100644 --- a/src/error.rs +++ b/src/error.rs | |||
| @@ -6,21 +6,21 @@ pub type ArchiveResult<T, E> = Result<T, ArchiveError<E>>; | |||
| 6 | 6 | ||
| 7 | #[derive(Debug)] | 7 | #[derive(Debug)] |
| 8 | pub enum ArchiveError<E: Error> { | 8 | pub enum ArchiveError<E: Error> { |
| 9 | IO { error: io::Error }, | 9 | Io { error: io::Error }, |
| 10 | Serde { message: String }, | 10 | Serde { message: String }, |
| 11 | Archivator { module: String, error: E }, | 11 | Archivator { module: String, error: E }, |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | impl<E: Error> From<io::Error> for ArchiveError<E> { | 14 | impl<E: Error> From<io::Error> for ArchiveError<E> { |
| 15 | fn from(value: io::Error) -> Self { | 15 | fn from(value: io::Error) -> Self { |
| 16 | Self::IO { error: value } | 16 | Self::Io { error: value } |
| 17 | } | 17 | } |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | impl<E: Error> Display for ArchiveError<E> { | 20 | impl<E: Error> Display for ArchiveError<E> { |
| 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 22 | match self { | 22 | match self { |
| 23 | Self::IO { error } => writeln!(f, "IO: {error}"), | 23 | Self::Io { error } => writeln!(f, "IO: {error}"), |
| 24 | Self::Serde { message } => writeln!(f, "Serde: {message}"), | 24 | Self::Serde { message } => writeln!(f, "Serde: {message}"), |
| 25 | Self::Archivator { module, error } => writeln!(f, "{module}: {error}"), | 25 | Self::Archivator { module, error } => writeln!(f, "{module}: {error}"), |
| 26 | } | 26 | } |
diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..f284b98 --- /dev/null +++ b/src/file.rs | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | use crate::driver::FileDriver; | ||
| 2 | use std::io::{Read, Result as IoResult, Seek, Write}; | ||
| 3 | |||
| 4 | pub struct ArchiveFile<D: FileDriver> { | ||
| 5 | pub(crate) driver: D, | ||
| 6 | } | ||
| 7 | |||
| 8 | impl<D: FileDriver> ArchiveFile<D> { | ||
| 9 | pub fn new(driver: D) -> Self { | ||
| 10 | Self { driver } | ||
| 11 | } | ||
| 12 | |||
| 13 | pub fn info(&self) -> &D::FileInfo { | ||
| 14 | self.driver.info() | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<D: FileDriver + Read> Read for ArchiveFile<D> { | ||
| 19 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||
| 20 | self.driver.read(buf) | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<D: FileDriver + Write> Write for ArchiveFile<D> { | ||
| 25 | fn write(&mut self, buf: &[u8]) -> IoResult<usize> { | ||
| 26 | self.driver.write(buf) | ||
| 27 | } | ||
| 28 | |||
| 29 | fn flush(&mut self) -> IoResult<()> { | ||
| 30 | self.driver.flush() | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | impl<D: FileDriver + Seek> Seek for ArchiveFile<D> { | ||
| 35 | fn seek(&mut self, pos: std::io::SeekFrom) -> IoResult<u64> { | ||
| 36 | self.driver.seek(pos) | ||
| 37 | } | ||
| 38 | } | ||
| @@ -1,5 +1,6 @@ | |||
| 1 | mod archive; | 1 | mod archive; |
| 2 | mod error; | 2 | mod error; |
| 3 | mod file; | ||
| 3 | 4 | ||
| 4 | pub mod driver; | 5 | pub mod driver; |
| 5 | pub mod structs; | 6 | pub mod structs; |
| @@ -7,4 +8,6 @@ pub mod zip; | |||
| 7 | 8 | ||
| 8 | pub use archive::Archive; | 9 | pub use archive::Archive; |
| 9 | pub use error::{ArchiveError, ArchiveResult}; | 10 | pub use error::{ArchiveError, ArchiveResult}; |
| 11 | pub use file::ArchiveFile; | ||
| 12 | |||
| 10 | pub use zip::Zip; | 13 | pub use zip::Zip; |
diff --git a/src/zip/archive.rs b/src/zip/archive.rs index 9a244fc..79e8ca1 100644 --- a/src/zip/archive.rs +++ b/src/zip/archive.rs | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | use crate::{Archive, Zip}; | 1 | use crate::{Archive, Zip}; |
| 2 | use std::io::{Read, Seek, Write}; | 2 | use std::io::{Read, Seek, Write}; |
| 3 | 3 | ||
| 4 | impl<IO: Read + Seek> Archive<Zip<IO>> { | 4 | impl<Io: Read + Seek> Archive<Zip<Io>> { |
| 5 | pub fn comment(&self) -> &String { | 5 | pub fn comment(&self) -> &String { |
| 6 | self.driver.comment() | 6 | self.driver.comment() |
| 7 | } | 7 | } |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | impl<IO: Read + Write + Seek> Archive<Zip<IO>> {} | 10 | impl<Io: Read + Write + Seek> Archive<Zip<Io>> {} |
diff --git a/src/zip/driver.rs b/src/zip/driver.rs index 650344e..0db845d 100644 --- a/src/zip/driver.rs +++ b/src/zip/driver.rs | |||
| @@ -1,27 +1,27 @@ | |||
| 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; | 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; |
| 2 | use crate::zip::file::{BitFlag, CompressionMethod}; | ||
| 3 | use crate::zip::structs::{deserialize, EOCDR64Locator, ExtraHeader, CDR, EOCDR, EOCDR64}; | 2 | use crate::zip::structs::{deserialize, EOCDR64Locator, ExtraHeader, CDR, EOCDR, EOCDR64}; |
| 4 | use crate::zip::{ZipError, ZipFile, ZipResult}; | 3 | use crate::zip::{BitFlag, CompressionMethod, ZipError, ZipFile, ZipFileInfo, ZipResult}; |
| 5 | use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime}; | 4 | use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime}; |
| 6 | use std::collections::HashMap as Map; | 5 | use std::collections::HashMap as Map; |
| 7 | use std::io::{Read, Seek, SeekFrom, Write}; | 6 | use std::io::{Read, Seek, SeekFrom, Write}; |
| 8 | 7 | ||
| 9 | pub struct Zip<IO> { | 8 | pub struct Zip<Io> { |
| 10 | io: IO, | 9 | io: Io, |
| 11 | 10 | ||
| 12 | files: Map<String, ZipFile>, | 11 | files: Map<String, ZipFileInfo>, |
| 13 | comment: String, | 12 | comment: String, |
| 14 | } | 13 | } |
| 15 | 14 | ||
| 16 | impl<IO> Driver for Zip<IO> { | 15 | impl<Io> Driver for Zip<Io> { |
| 17 | type Error = ZipError; | 16 | type Error = ZipError; |
| 18 | 17 | ||
| 19 | type IO = IO; | 18 | type Io = Io; |
| 20 | type File = ZipFile; | 19 | type FileDriver<'d> = ZipFile<'d, Self::Io> where Self::Io: 'd; |
| 20 | type FileInfo = ZipFileInfo; | ||
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | impl<IO: Read + Seek> ArchiveRead for Zip<IO> { | 23 | impl<Io: Read + Seek> ArchiveRead for Zip<Io> { |
| 24 | fn read(mut io: Self::IO) -> ZipResult<Self> { | 24 | fn read(mut io: Self::Io) -> ZipResult<Self> { |
| 25 | // Search eocdr | 25 | // Search eocdr |
| 26 | let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; | 26 | let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; |
| 27 | let start = io.seek(SeekFrom::End(-limit))?; | 27 | let start = io.seek(SeekFrom::End(-limit))?; |
| @@ -137,7 +137,7 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> { | |||
| 137 | 137 | ||
| 138 | files.insert( | 138 | files.insert( |
| 139 | name.clone(), | 139 | name.clone(), |
| 140 | ZipFile::new( | 140 | ZipFileInfo::new( |
| 141 | CompressionMethod::from_struct_id(cdr.compression_method)?, | 141 | CompressionMethod::from_struct_id(cdr.compression_method)?, |
| 142 | BitFlag::new(cdr.bit_flag), | 142 | BitFlag::new(cdr.bit_flag), |
| 143 | NaiveDateTime::new( | 143 | NaiveDateTime::new( |
| @@ -169,23 +169,27 @@ impl<IO: Read + Seek> ArchiveRead for Zip<IO> { | |||
| 169 | Ok(Self { io, files, comment }) | 169 | Ok(Self { io, files, comment }) |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | fn files(&self) -> Vec<&Self::File> { | 172 | fn files(&self) -> Vec<&Self::FileInfo> { |
| 173 | let mut files: Vec<&Self::File> = self.files.values().collect(); | 173 | let mut files: Vec<&Self::FileInfo> = self.files.values().collect(); |
| 174 | files.sort_by_key(|f| &f.name); | 174 | files.sort_by_key(|f| &f.name); |
| 175 | files | 175 | files |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | fn get_file(&self, name: &str) -> Option<&Self::File> { | 178 | fn get_file_info(&self, name: &str) -> Option<&Self::FileInfo> { |
| 179 | self.files.get(name) | 179 | self.files.get(name) |
| 180 | } | 180 | } |
| 181 | |||
| 182 | fn get_file_reader<'d>(&'d mut self, name: &str) -> Option<Self::FileDriver<'d>> { | ||
| 183 | Some(ZipFile::new(&mut self.io, self.files.get(name)?).unwrap()) | ||
| 184 | } | ||
| 181 | } | 185 | } |
| 182 | 186 | ||
| 183 | impl<IO: Read + Seek> Zip<IO> { | 187 | impl<Io: Read + Seek> Zip<Io> { |
| 184 | pub fn comment(&self) -> &String { | 188 | pub fn comment(&self) -> &String { |
| 185 | &self.comment | 189 | &self.comment |
| 186 | } | 190 | } |
| 187 | } | 191 | } |
| 188 | 192 | ||
| 189 | impl<IO: Read + Write + Seek> ArchiveWrite for Zip<IO> {} | 193 | impl<Io: Read + Write + Seek> ArchiveWrite for Zip<Io> {} |
| 190 | 194 | ||
| 191 | impl<IO: Read + Write + Seek> Zip<IO> {} | 195 | impl<Io: Read + Write + Seek> Zip<Io> {} |
diff --git a/src/zip/error.rs b/src/zip/error.rs index 1c5527c..d82de78 100644 --- a/src/zip/error.rs +++ b/src/zip/error.rs | |||
| @@ -8,6 +8,7 @@ pub type ZipResult<T> = ArchiveResult<T, ZipError>; | |||
| 8 | pub enum ZipError { | 8 | pub enum ZipError { |
| 9 | EOCDRNotFound, | 9 | EOCDRNotFound, |
| 10 | InvalidEOCDR64Signature, | 10 | InvalidEOCDR64Signature, |
| 11 | InvalidFileHeaderSignature, | ||
| 11 | InvalidCDRSignature, | 12 | InvalidCDRSignature, |
| 12 | 13 | ||
| 13 | InvalidArchiveComment, | 14 | InvalidArchiveComment, |
| @@ -17,6 +18,8 @@ pub enum ZipError { | |||
| 17 | InvalidTime, | 18 | InvalidTime, |
| 18 | InvalidFileName, | 19 | InvalidFileName, |
| 19 | InvalidFileComment, | 20 | InvalidFileComment, |
| 21 | |||
| 22 | NegativeFileOffset, | ||
| 20 | } | 23 | } |
| 21 | 24 | ||
| 22 | impl From<ZipError> for ArchiveError<ZipError> { | 25 | impl From<ZipError> for ArchiveError<ZipError> { |
| @@ -38,6 +41,9 @@ impl Display for ZipError { | |||
| 38 | "Invalid signature of zip64 end of central directory record" | 41 | "Invalid signature of zip64 end of central directory record" |
| 39 | ) | 42 | ) |
| 40 | } | 43 | } |
| 44 | Self::InvalidFileHeaderSignature => { | ||
| 45 | write!(f, "Invalid file header signature") | ||
| 46 | } | ||
| 41 | Self::InvalidCDRSignature => { | 47 | Self::InvalidCDRSignature => { |
| 42 | write!(f, "Invalid signature of central directory record") | 48 | write!(f, "Invalid signature of central directory record") |
| 43 | } | 49 | } |
| @@ -49,6 +55,8 @@ impl Display for ZipError { | |||
| 49 | Self::InvalidTime => write!(f, "Invalid time"), | 55 | Self::InvalidTime => write!(f, "Invalid time"), |
| 50 | Self::InvalidFileName => write!(f, "Invalid file name"), | 56 | Self::InvalidFileName => write!(f, "Invalid file name"), |
| 51 | Self::InvalidFileComment => write!(f, "Invalid file comment"), | 57 | Self::InvalidFileComment => write!(f, "Invalid file comment"), |
| 58 | |||
| 59 | Self::NegativeFileOffset => write!(f, "Negative file offset"), | ||
| 52 | } | 60 | } |
| 53 | } | 61 | } |
| 54 | } | 62 | } |
diff --git a/src/zip/file_driver.rs b/src/zip/file_driver.rs new file mode 100644 index 0000000..47b4242 --- /dev/null +++ b/src/zip/file_driver.rs | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | use crate::driver::FileDriver; | ||
| 2 | use crate::zip::{ZipError, ZipFileInfo, ZipResult}; | ||
| 3 | use std::io::{ | ||
| 4 | Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, Take, | ||
| 5 | Write, | ||
| 6 | }; | ||
| 7 | |||
| 8 | pub struct ZipFile<'d, Io> { | ||
| 9 | io: &'d mut Io, | ||
| 10 | info: &'d ZipFileInfo, | ||
| 11 | |||
| 12 | bounds: (u64, u64), | ||
| 13 | cursor: u64, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl<'d, Io> FileDriver for ZipFile<'d, Io> { | ||
| 17 | type Io = Io; | ||
| 18 | type FileInfo = ZipFileInfo; | ||
| 19 | |||
| 20 | fn info(&self) -> &Self::FileInfo { | ||
| 21 | self.info | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl<'d, Io: Read + Seek> ZipFile<'d, Io> { | ||
| 26 | pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult<Self> { | ||
| 27 | io.seek(SeekFrom::Start(info.header_pointer))?; | ||
| 28 | let buf = { | ||
| 29 | let mut buf = [0; 30]; | ||
| 30 | io.read(&mut buf)?; | ||
| 31 | buf | ||
| 32 | }; | ||
| 33 | if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 { | ||
| 34 | return Err(ZipError::InvalidFileHeaderSignature.into()); | ||
| 35 | } | ||
| 36 | let data_pointer = info.header_pointer | ||
| 37 | + 30 | ||
| 38 | + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 | ||
| 39 | + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; | ||
| 40 | io.seek(SeekFrom::Start(data_pointer))?; | ||
| 41 | |||
| 42 | Ok(Self { | ||
| 43 | io, | ||
| 44 | info, | ||
| 45 | |||
| 46 | bounds: (data_pointer, data_pointer + info.compressed_size), | ||
| 47 | cursor: data_pointer, | ||
| 48 | }) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | impl<'d, Io: Read> Read for ZipFile<'d, Io> { | ||
| 53 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||
| 54 | let upper = buf.len().min((self.bounds.1 - self.cursor) as usize); | ||
| 55 | self.cursor += upper as u64; | ||
| 56 | self.io.read(&mut buf[..upper]) | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | impl<'d, Io: Write> Write for ZipFile<'d, Io> { | ||
| 61 | fn write(&mut self, buf: &[u8]) -> IoResult<usize> { | ||
| 62 | todo!() | ||
| 63 | } | ||
| 64 | |||
| 65 | fn flush(&mut self) -> IoResult<()> { | ||
| 66 | todo!() | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | impl<'d, Io: Seek> Seek for ZipFile<'d, Io> { | ||
| 71 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { | ||
| 72 | self.cursor = match pos { | ||
| 73 | SeekFrom::Start(offset) => self.bounds.0 + offset, | ||
| 74 | SeekFrom::End(offset) => { | ||
| 75 | let cursor = self.bounds.1.saturating_add_signed(offset); | ||
| 76 | if cursor < self.bounds.0 { | ||
| 77 | return Err(IoError::new( | ||
| 78 | IoErrorKind::InvalidInput, | ||
| 79 | ZipError::NegativeFileOffset, | ||
| 80 | )); | ||
| 81 | } | ||
| 82 | cursor | ||
| 83 | } | ||
| 84 | SeekFrom::Current(offset) => { | ||
| 85 | let cursor = self.cursor.saturating_add_signed(offset); | ||
| 86 | if cursor < self.bounds.0 { | ||
| 87 | return Err(IoError::new( | ||
| 88 | IoErrorKind::InvalidInput, | ||
| 89 | ZipError::NegativeFileOffset, | ||
| 90 | )); | ||
| 91 | } | ||
| 92 | cursor | ||
| 93 | } | ||
| 94 | } | ||
| 95 | .min(self.bounds.1); | ||
| 96 | |||
| 97 | Ok(self.io.seek(SeekFrom::Start(self.cursor))? - self.bounds.0) | ||
| 98 | } | ||
| 99 | } | ||
diff --git a/src/zip/file.rs b/src/zip/file_info.rs index 5b0723f..88322be 100644 --- a/src/zip/file.rs +++ b/src/zip/file_info.rs | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | use crate::driver::ArchiveFile; | 1 | use crate::driver::ArchiveFileInfo; |
| 2 | use crate::zip::{ZipError, ZipResult}; | 2 | use crate::zip::{ZipError, ZipResult}; |
| 3 | use chrono::{DateTime, Local}; | 3 | use chrono::{DateTime, Local}; |
| 4 | 4 | ||
| 5 | #[derive(Debug)] | ||
| 5 | pub enum CompressionMethod { | 6 | pub enum CompressionMethod { |
| 6 | Store, | 7 | Store, |
| 7 | Deflate, | 8 | Deflate, |
| @@ -28,6 +29,7 @@ impl CompressionMethod { | |||
| 28 | } | 29 | } |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 32 | #[derive(Debug)] | ||
| 31 | pub struct BitFlag { | 33 | pub struct BitFlag { |
| 32 | flag: u16, | 34 | flag: u16, |
| 33 | } | 35 | } |
| @@ -119,7 +121,8 @@ impl BitFlag { | |||
| 119 | } | 121 | } |
| 120 | } | 122 | } |
| 121 | 123 | ||
| 122 | pub struct ZipFile { | 124 | #[derive(Debug)] |
| 125 | pub struct ZipFileInfo { | ||
| 123 | pub compression_method: CompressionMethod, | 126 | pub compression_method: CompressionMethod, |
| 124 | pub bit_flag: BitFlag, | 127 | pub bit_flag: BitFlag, |
| 125 | pub datetime: DateTime<Local>, | 128 | pub datetime: DateTime<Local>, |
| @@ -131,7 +134,7 @@ pub struct ZipFile { | |||
| 131 | pub comment: String, | 134 | pub comment: String, |
| 132 | } | 135 | } |
| 133 | 136 | ||
| 134 | impl ZipFile { | 137 | impl ZipFileInfo { |
| 135 | pub fn new( | 138 | pub fn new( |
| 136 | compression_method: CompressionMethod, | 139 | compression_method: CompressionMethod, |
| 137 | bit_flag: BitFlag, | 140 | bit_flag: BitFlag, |
| @@ -157,4 +160,4 @@ impl ZipFile { | |||
| 157 | } | 160 | } |
| 158 | } | 161 | } |
| 159 | 162 | ||
| 160 | impl ArchiveFile for ZipFile {} | 163 | impl ArchiveFileInfo for ZipFileInfo {} |
diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 89d748b..3fe8384 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs | |||
| @@ -1,12 +1,14 @@ | |||
| 1 | mod archive; | 1 | mod archive; |
| 2 | mod driver; | 2 | mod driver; |
| 3 | mod error; | 3 | mod error; |
| 4 | mod file; | 4 | mod file_driver; |
| 5 | mod file_info; | ||
| 5 | mod structs; | 6 | mod structs; |
| 6 | 7 | ||
| 7 | pub use driver::Zip; | 8 | pub use driver::Zip; |
| 8 | pub use error::{ZipError, ZipResult}; | 9 | pub use error::{ZipError, ZipResult}; |
| 9 | pub use file::{bit, BitFlag, CompressionMethod, ZipFile}; | 10 | pub use file_driver::ZipFile; |
| 11 | pub use file_info::{bit, BitFlag, CompressionMethod, ZipFileInfo}; | ||
| 10 | 12 | ||
| 11 | #[cfg(test)] | 13 | #[cfg(test)] |
| 12 | mod tests; | 14 | mod tests; |
diff --git a/src/zip/tests.rs b/src/zip/tests.rs index d64e626..05e076d 100644 --- a/src/zip/tests.rs +++ b/src/zip/tests.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | use crate::zip::file::{bit::DeflateMode, BitFlag}; | 1 | use crate::zip::file_info::{bit::DeflateMode, BitFlag}; |
| 2 | 2 | ||
| 3 | #[test] | 3 | #[test] |
| 4 | fn test_bit_flag() { | 4 | fn test_bit_flag() { |
diff --git a/tests/files/zip.zip b/tests/files/zip.zip index e17bbb7..f3a090b 100644 --- a/tests/files/zip.zip +++ b/tests/files/zip.zip | |||
| Binary files differ | |||
diff --git a/tests/zip.rs b/tests/zip.rs index 65244c1..25f1f76 100644 --- a/tests/zip.rs +++ b/tests/zip.rs | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | use archivator::{Archive, Zip}; | 1 | use archivator::{Archive, Zip}; |
| 2 | use std::io::{Read, Seek, SeekFrom}; | ||
| 2 | 3 | ||
| 3 | #[test] | 4 | #[test] |
| 4 | fn test_zip() { | 5 | fn test_zip() { |
| 5 | let archive = Archive::<Zip<_>>::read_from_file("tests/files/zip.zip").unwrap(); | 6 | let mut archive = Archive::<Zip<_>>::read_from_file("tests/files/zip.zip").unwrap(); |
| 6 | 7 | ||
| 7 | assert_eq!(archive.comment(), "archive comment"); | 8 | assert_eq!(archive.comment(), "archive comment"); |
| 8 | assert_eq!( | 9 | assert_eq!( |
| @@ -13,4 +14,34 @@ fn test_zip() { | |||
| 13 | .collect::<Vec<&String>>(), | 14 | .collect::<Vec<&String>>(), |
| 14 | vec!["a", "b", "c"] | 15 | vec!["a", "b", "c"] |
| 15 | ); | 16 | ); |
| 17 | |||
| 18 | let mut f = archive.get_file_reader("a").unwrap(); | ||
| 19 | |||
| 20 | let mut data = String::new(); | ||
| 21 | f.read_to_string(&mut data).unwrap(); | ||
| 22 | assert_eq!(data, "file data \"a\""); | ||
| 23 | |||
| 24 | assert_eq!(f.seek(SeekFrom::Start(5)).unwrap(), 5); | ||
| 25 | let mut data = String::new(); | ||
| 26 | f.read_to_string(&mut data).unwrap(); | ||
| 27 | assert_eq!(data, "data \"a\""); | ||
| 28 | |||
| 29 | assert_eq!(f.seek(SeekFrom::Start(0)).unwrap(), 0); | ||
| 30 | let mut data = vec![0; 4]; | ||
| 31 | f.read(&mut data).unwrap(); | ||
| 32 | assert_eq!(String::from_utf8(data).unwrap(), "file"); | ||
| 33 | |||
| 34 | assert_eq!(f.seek(SeekFrom::Current(1)).unwrap(), 5); | ||
| 35 | let mut data = vec![0; 4]; | ||
| 36 | f.read(&mut data).unwrap(); | ||
| 37 | assert_eq!(String::from_utf8(data).unwrap(), "data"); | ||
| 38 | |||
| 39 | assert_eq!(f.seek(SeekFrom::End(-3)).unwrap(), 10); | ||
| 40 | let mut data = vec![0; 3]; | ||
| 41 | f.read(&mut data).unwrap(); | ||
| 42 | assert_eq!(String::from_utf8(data).unwrap(), "\"a\""); | ||
| 43 | |||
| 44 | assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 13); | ||
| 45 | f.seek(SeekFrom::End(-100)).unwrap_err(); | ||
| 46 | assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 13); | ||
| 16 | } | 47 | } |
