diff options
| -rw-r--r-- | .vscode/settings.json | 7 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/archive.rs | 20 | ||||
| -rw-r--r-- | src/driver/driver.rs | 25 | ||||
| -rw-r--r-- | src/driver/file.rs | 1 | ||||
| -rw-r--r-- | src/driver/mod.rs | 5 | ||||
| -rw-r--r-- | src/error.rs | 35 | ||||
| -rw-r--r-- | src/lib.rs | 10 | ||||
| -rw-r--r-- | src/structs/de.rs | 0 | ||||
| -rw-r--r-- | src/structs/mod.rs | 2 | ||||
| -rw-r--r-- | src/structs/ser.rs | 6 | ||||
| -rw-r--r-- | src/zip/driver.rs | 143 | ||||
| -rw-r--r-- | src/zip/error.rs | 58 | ||||
| -rw-r--r-- | src/zip/file.rs | 45 | ||||
| -rw-r--r-- | src/zip/mod.rs | 7 | ||||
| -rw-r--r-- | src/zip/structs.rs | 52 | ||||
| -rw-r--r-- | tests/usage.rs | 19 |
17 files changed, 434 insertions, 3 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index c36ecad..193c892 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json | |||
| @@ -1,11 +1,12 @@ | |||
| 1 | { | 1 | { |
| 2 | "cSpell.words": [ | 2 | "cSpell.words": [ |
| 3 | "archivator", | 3 | "archivator", |
| 4 | "bincode", | ||
| 4 | "chrono", | 5 | "chrono", |
| 5 | "datatypes", | 6 | "datatypes", |
| 6 | "eocd", | 7 | "datetime", |
| 7 | "LZMA", | 8 | "eocdr", |
| 8 | "Zstandard" | 9 | "rposition" |
| 9 | ], | 10 | ], |
| 10 | "rust-analyzer.linkedProjects": ["./Cargo.toml", "./Cargo.toml"] | 11 | "rust-analyzer.linkedProjects": ["./Cargo.toml", "./Cargo.toml"] |
| 11 | } | 12 | } |
| @@ -12,4 +12,6 @@ categories = ["compression", "filesystem"] | |||
| 12 | exclude = [".vscode/"] | 12 | exclude = [".vscode/"] |
| 13 | 13 | ||
| 14 | [dependencies] | 14 | [dependencies] |
| 15 | bincode = "1.3.3" | ||
| 15 | chrono = "0.4.29" | 16 | chrono = "0.4.29" |
| 17 | serde = { version = "1.0.203", features = ["derive"] } | ||
diff --git a/src/archive.rs b/src/archive.rs new file mode 100644 index 0000000..a422f9e --- /dev/null +++ b/src/archive.rs | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; | ||
| 2 | use crate::ArchiveResult; | ||
| 3 | use std::io::{Read, Write}; | ||
| 4 | |||
| 5 | pub struct Archive<D: Driver> { | ||
| 6 | pub(crate) driver: D, | ||
| 7 | } | ||
| 8 | |||
| 9 | impl<D: ArchiveRead> Archive<D> | ||
| 10 | where | ||
| 11 | D::IO: std::io::Read, | ||
| 12 | { | ||
| 13 | pub fn new(io: D::IO) -> ArchiveResult<Self, D::Error> { | ||
| 14 | Ok(Self { | ||
| 15 | driver: D::read(io)?, | ||
| 16 | }) | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<D: ArchiveWrite> Archive<D> where D::IO: Read + Write {} | ||
diff --git a/src/driver/driver.rs b/src/driver/driver.rs new file mode 100644 index 0000000..3a8ed16 --- /dev/null +++ b/src/driver/driver.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | use crate::driver::ArchiveFile; | ||
| 2 | use crate::ArchiveResult; | ||
| 3 | use std::error::Error; | ||
| 4 | use std::io::{Read, Write}; | ||
| 5 | |||
| 6 | pub trait Driver: Sized { | ||
| 7 | type Error: Error; | ||
| 8 | |||
| 9 | type IO; | ||
| 10 | type File: ArchiveFile; | ||
| 11 | } | ||
| 12 | |||
| 13 | pub trait ArchiveRead: Driver | ||
| 14 | where | ||
| 15 | Self::IO: Read, | ||
| 16 | { | ||
| 17 | // Create driver instance | ||
| 18 | fn read(io: Self::IO) -> ArchiveResult<Self, Self::Error>; | ||
| 19 | } | ||
| 20 | |||
| 21 | pub trait ArchiveWrite: ArchiveRead | ||
| 22 | where | ||
| 23 | Self::IO: Read + Write, | ||
| 24 | { | ||
| 25 | } | ||
diff --git a/src/driver/file.rs b/src/driver/file.rs new file mode 100644 index 0000000..a4974f3 --- /dev/null +++ b/src/driver/file.rs | |||
| @@ -0,0 +1 @@ | |||
| pub trait ArchiveFile {} | |||
diff --git a/src/driver/mod.rs b/src/driver/mod.rs new file mode 100644 index 0000000..36ee6b5 --- /dev/null +++ b/src/driver/mod.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | mod driver; | ||
| 2 | mod file; | ||
| 3 | |||
| 4 | pub use driver::{ArchiveRead, ArchiveWrite, Driver}; | ||
| 5 | pub use file::ArchiveFile; | ||
diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..6d7aba4 --- /dev/null +++ b/src/error.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | use std::error::Error; | ||
| 2 | use std::fmt::Display; | ||
| 3 | use std::io; | ||
| 4 | |||
| 5 | pub type ArchiveResult<R, E> = Result<R, ArchiveError<E>>; | ||
| 6 | |||
| 7 | #[derive(Debug)] | ||
| 8 | pub enum ArchiveError<E: Error> { | ||
| 9 | IO(io::Error), | ||
| 10 | Driver { name: &'static str, error: E }, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl<E: Error> From<io::Error> for ArchiveError<E> { | ||
| 14 | fn from(value: io::Error) -> Self { | ||
| 15 | Self::IO(value) | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<E: Error> Display for ArchiveError<E> { | ||
| 20 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| 21 | match self { | ||
| 22 | ArchiveError::IO(error) => write!(f, "{error}"), | ||
| 23 | ArchiveError::Driver { name, error } => write!(f, "{name}: {error}"), | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<E: Error> Error for ArchiveError<E> { | ||
| 29 | fn source(&self) -> Option<&(dyn Error + 'static)> { | ||
| 30 | match self { | ||
| 31 | Self::IO(error) => Some(error), | ||
| 32 | _ => None, | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | mod archive; | ||
| 2 | mod error; | ||
| 3 | mod structs; | ||
| 4 | |||
| 5 | pub mod driver; | ||
| 6 | pub mod zip; | ||
| 7 | |||
| 8 | pub use archive::Archive; | ||
| 9 | pub use error::{ArchiveError, ArchiveResult}; | ||
| 10 | pub use zip::Zip; | ||
diff --git a/src/structs/de.rs b/src/structs/de.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/structs/de.rs | |||
diff --git a/src/structs/mod.rs b/src/structs/mod.rs new file mode 100644 index 0000000..d7e9daa --- /dev/null +++ b/src/structs/mod.rs | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | mod de; | ||
| 2 | mod ser; | ||
diff --git a/src/structs/ser.rs b/src/structs/ser.rs new file mode 100644 index 0000000..a3fe291 --- /dev/null +++ b/src/structs/ser.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | use crate::ArchiveError; | ||
| 2 | use serde::{ser, Serialize}; | ||
| 3 | |||
| 4 | pub struct ArchiveSerializer { | ||
| 5 | bin: Vec<u8>, | ||
| 6 | } | ||
diff --git a/src/zip/driver.rs b/src/zip/driver.rs new file mode 100644 index 0000000..733d44b --- /dev/null +++ b/src/zip/driver.rs | |||
| @@ -0,0 +1,143 @@ | |||
| 1 | use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; | ||
| 2 | use crate::zip::error::{ZipError, ZipResult}; | ||
| 3 | use crate::zip::structs::{EOCDR64Locator, CDR, EOCDR, EOCDR64}; | ||
| 4 | use crate::zip::ZipFile; | ||
| 5 | use std::collections::HashMap as Map; | ||
| 6 | use std::fs::File; | ||
| 7 | use std::io::{Read, Seek, SeekFrom, Write}; | ||
| 8 | |||
| 9 | pub struct Zip<IO = File> { | ||
| 10 | io: IO, | ||
| 11 | |||
| 12 | files: Map<String, ZipFile>, | ||
| 13 | comment: String, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl<IO> Driver for Zip<IO> { | ||
| 17 | type Error = ZipError; | ||
| 18 | |||
| 19 | type IO = IO; | ||
| 20 | type File = ZipFile; | ||
| 21 | } | ||
| 22 | |||
| 23 | impl<IO: Read + Seek> ArchiveRead for Zip<IO> { | ||
| 24 | fn read(mut io: Self::IO) -> ZipResult<Self> { | ||
| 25 | // Search eocdr | ||
| 26 | let limit = 65557.min(io.seek(SeekFrom::End(0))?) as i64; | ||
| 27 | let start = io.seek(SeekFrom::End(-limit))?; | ||
| 28 | let pos = start + { | ||
| 29 | let mut buf = vec![0; limit as usize]; | ||
| 30 | io.read(&mut buf)?; | ||
| 31 | buf[..buf.len() - 18] | ||
| 32 | .windows(4) | ||
| 33 | .rposition(|v| u32::from_le_bytes(v.try_into().unwrap()) == 0x06054b50) | ||
| 34 | .ok_or(ZipError::EOCDRNotFound)? as u64 | ||
| 35 | }; | ||
| 36 | |||
| 37 | // Read eocdr | ||
| 38 | io.seek(SeekFrom::Start(pos + 4))?; | ||
| 39 | let buf = { | ||
| 40 | let mut buf = [0; 18]; | ||
| 41 | io.read(&mut buf)?; | ||
| 42 | buf | ||
| 43 | }; | ||
| 44 | let eocdr: EOCDR = bincode::deserialize(&buf).map_err(|_| ZipError::InvalidEOCDR)?; | ||
| 45 | let comment = { | ||
| 46 | let mut buf = vec![0; eocdr.comment_len as usize]; | ||
| 47 | io.read(&mut buf)?; | ||
| 48 | String::from_utf8(buf).map_err(|_| ZipError::InvalidArchiveComment)? | ||
| 49 | }; | ||
| 50 | |||
| 51 | // Try to find eocdr64locator | ||
| 52 | io.seek(SeekFrom::Start(pos - 20))?; | ||
| 53 | let buf = { | ||
| 54 | let mut buf = [0; 20]; | ||
| 55 | io.read(&mut buf)?; | ||
| 56 | buf | ||
| 57 | }; | ||
| 58 | let (cd_pointer, cd_size, cd_records) = | ||
| 59 | if u32::from_le_bytes(buf[0..4].try_into().unwrap()) == 0x07064b50 { | ||
| 60 | let eocdr64locator: EOCDR64Locator = | ||
| 61 | bincode::deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64Locator)?; | ||
| 62 | |||
| 63 | io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; | ||
| 64 | let buf = { | ||
| 65 | let mut buf = [0; 56]; | ||
| 66 | io.read(&mut buf)?; | ||
| 67 | buf | ||
| 68 | }; | ||
| 69 | if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 { | ||
| 70 | return Err(ZipError::InvalidEOCDR64Signature.into()); | ||
| 71 | } | ||
| 72 | let eocdr64: EOCDR64 = | ||
| 73 | bincode::deserialize(&buf[4..]).map_err(|_| ZipError::InvalidEOCDR64)?; | ||
| 74 | |||
| 75 | (eocdr64.cd_pointer, eocdr64.cd_size, eocdr64.cd_records) | ||
| 76 | } else { | ||
| 77 | ( | ||
| 78 | eocdr.cd_pointer as u64, | ||
| 79 | eocdr.cd_size as u64, | ||
| 80 | eocdr.cd_records as u64, | ||
| 81 | ) | ||
| 82 | }; | ||
| 83 | |||
| 84 | // Read cd records | ||
| 85 | let mut files = Map::with_capacity(cd_records as usize); | ||
| 86 | io.seek(SeekFrom::Start(cd_pointer))?; | ||
| 87 | let buf = { | ||
| 88 | let mut buf = vec![0; cd_size as usize]; | ||
| 89 | io.read(&mut buf)?; | ||
| 90 | buf | ||
| 91 | }; | ||
| 92 | let mut records = buf.as_slice(); | ||
| 93 | |||
| 94 | for _ in 0..cd_records { | ||
| 95 | let buf = { | ||
| 96 | let mut buf = [0; 46]; | ||
| 97 | records.read(&mut buf)?; | ||
| 98 | buf | ||
| 99 | }; | ||
| 100 | |||
| 101 | if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x02014b50 { | ||
| 102 | return Err(ZipError::InvalidCDRSignature.into()); | ||
| 103 | } | ||
| 104 | let cdr: CDR = bincode::deserialize(&buf[4..]).map_err(|_| ZipError::InvalidCDR)?; | ||
| 105 | let name = { | ||
| 106 | let mut buf = vec![0; cdr.name_len as usize]; | ||
| 107 | records.read(&mut buf)?; | ||
| 108 | String::from_utf8(buf).map_err(|_| ZipError::InvalidFileName)? | ||
| 109 | }; | ||
| 110 | let extra_fields = { | ||
| 111 | let mut buf = vec![0; cdr.extra_field_len as usize]; | ||
| 112 | records.read(&mut buf)?; | ||
| 113 | buf | ||
| 114 | }; | ||
| 115 | let comment = { | ||
| 116 | let mut buf = vec![0; cdr.comment_len as usize]; | ||
| 117 | records.read(&mut buf)?; | ||
| 118 | String::from_utf8(buf).map_err(|_| ZipError::InvalidFileComment)? | ||
| 119 | }; | ||
| 120 | |||
| 121 | files.insert( | ||
| 122 | name.clone(), | ||
| 123 | ZipFile::new( | ||
| 124 | name, | ||
| 125 | cdr.dos_date, | ||
| 126 | cdr.dos_time, | ||
| 127 | cdr.compression_method, | ||
| 128 | cdr.compressed_size as u64, | ||
| 129 | cdr.size as u64, | ||
| 130 | comment, | ||
| 131 | ), | ||
| 132 | ); | ||
| 133 | } | ||
| 134 | |||
| 135 | Ok(Self { io, files, comment }) | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | impl<IO: Read + Seek> Zip<IO> {} | ||
| 140 | |||
| 141 | impl<IO: Read + Write + Seek> ArchiveWrite for Zip<IO> {} | ||
| 142 | |||
| 143 | impl<IO: Read + Write> Zip<IO> {} | ||
diff --git a/src/zip/error.rs b/src/zip/error.rs new file mode 100644 index 0000000..ad1989a --- /dev/null +++ b/src/zip/error.rs | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | use crate::{ArchiveError, ArchiveResult}; | ||
| 2 | use std::error::Error; | ||
| 3 | use std::fmt::Display; | ||
| 4 | |||
| 5 | pub type ZipResult<R> = ArchiveResult<R, ZipError>; | ||
| 6 | |||
| 7 | #[derive(Debug)] | ||
| 8 | pub enum ZipError { | ||
| 9 | EOCDRNotFound, | ||
| 10 | InvalidEOCDR, | ||
| 11 | InvalidArchiveComment, | ||
| 12 | |||
| 13 | InvalidEOCDR64Locator, | ||
| 14 | InvalidEOCDR64Signature, | ||
| 15 | InvalidEOCDR64, | ||
| 16 | |||
| 17 | InvalidCDRSignature, | ||
| 18 | InvalidCDR, | ||
| 19 | InvalidFileName, | ||
| 20 | InvalidFileComment, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl From<ZipError> for ArchiveError<ZipError> { | ||
| 24 | fn from(value: ZipError) -> Self { | ||
| 25 | return ArchiveError::Driver { | ||
| 26 | name: "Zip", | ||
| 27 | error: value, | ||
| 28 | }; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | impl Display for ZipError { | ||
| 33 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| 34 | match self { | ||
| 35 | ZipError::EOCDRNotFound => write!(f, "End of central directory record not found"), | ||
| 36 | ZipError::InvalidEOCDR => write!(f, "Invalid end of central directory record"), | ||
| 37 | ZipError::InvalidArchiveComment => write!(f, "Invalid archive comment"), | ||
| 38 | ZipError::InvalidEOCDR64Locator => { | ||
| 39 | write!(f, "Invalid zip64 end of central directory locator") | ||
| 40 | } | ||
| 41 | ZipError::InvalidEOCDR64Signature => { | ||
| 42 | write!( | ||
| 43 | f, | ||
| 44 | "Invalid signature of zip64 end of central directory record" | ||
| 45 | ) | ||
| 46 | } | ||
| 47 | ZipError::InvalidEOCDR64 => write!(f, "Invalid zip64 end of central directory record"), | ||
| 48 | ZipError::InvalidCDRSignature => { | ||
| 49 | write!(f, "Invalid signature of central directory record") | ||
| 50 | } | ||
| 51 | ZipError::InvalidCDR => write!(f, "Invalid central directory record"), | ||
| 52 | ZipError::InvalidFileName => write!(f, "Invalid file name"), | ||
| 53 | ZipError::InvalidFileComment => write!(f, "Invalid file comment"), | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | impl Error for ZipError {} | ||
diff --git a/src/zip/file.rs b/src/zip/file.rs new file mode 100644 index 0000000..3b63c2a --- /dev/null +++ b/src/zip/file.rs | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | use crate::driver::ArchiveFile; | ||
| 2 | use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; | ||
| 3 | |||
| 4 | pub struct ZipFile { | ||
| 5 | pub name: String, | ||
| 6 | pub datetime: NaiveDateTime, | ||
| 7 | pub compression_method: u16, | ||
| 8 | pub compressed_size: u64, | ||
| 9 | pub size: u64, | ||
| 10 | pub comment: String, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl ArchiveFile for ZipFile {} | ||
| 14 | |||
| 15 | impl ZipFile { | ||
| 16 | pub fn new( | ||
| 17 | name: String, | ||
| 18 | dos_date: u16, | ||
| 19 | dos_time: u16, | ||
| 20 | compression_method: u16, | ||
| 21 | compressed_size: u64, | ||
| 22 | size: u64, | ||
| 23 | comment: String, | ||
| 24 | ) -> Self { | ||
| 25 | let year = (dos_date >> 9 & 0x7F) + 1980; | ||
| 26 | let month = dos_date >> 5 & 0xF; | ||
| 27 | let day = dos_date & 0x1F; | ||
| 28 | |||
| 29 | let hour = (dos_time >> 11) & 0x1F; | ||
| 30 | let minute = (dos_time >> 5) & 0x3F; | ||
| 31 | let seconds = (dos_time & 0x1F) * 2; | ||
| 32 | |||
| 33 | Self { | ||
| 34 | name, | ||
| 35 | datetime: NaiveDateTime::new( | ||
| 36 | NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).unwrap(), | ||
| 37 | NaiveTime::from_hms_opt(hour as u32, minute as u32, seconds as u32).unwrap(), | ||
| 38 | ), | ||
| 39 | compression_method, | ||
| 40 | compressed_size, | ||
| 41 | size, | ||
| 42 | comment, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
diff --git a/src/zip/mod.rs b/src/zip/mod.rs new file mode 100644 index 0000000..612a946 --- /dev/null +++ b/src/zip/mod.rs | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | mod driver; | ||
| 2 | mod error; | ||
| 3 | mod file; | ||
| 4 | mod structs; | ||
| 5 | |||
| 6 | pub use driver::Zip; | ||
| 7 | pub use file::ZipFile; | ||
diff --git a/src/zip/structs.rs b/src/zip/structs.rs new file mode 100644 index 0000000..e38f9f0 --- /dev/null +++ b/src/zip/structs.rs | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | use serde::{Deserialize, Serialize}; | ||
| 2 | |||
| 3 | #[derive(Serialize, Deserialize)] | ||
| 4 | pub struct EOCDR { | ||
| 5 | pub eocdr_disk: u16, | ||
| 6 | pub cd_disk: u16, | ||
| 7 | pub cd_disk_records: u16, | ||
| 8 | pub cd_records: u16, | ||
| 9 | pub cd_size: u32, | ||
| 10 | pub cd_pointer: u32, | ||
| 11 | pub comment_len: u16, | ||
| 12 | } | ||
| 13 | |||
| 14 | #[derive(Serialize, Deserialize)] | ||
| 15 | pub struct EOCDR64Locator { | ||
| 16 | pub eocdr64_disk: u32, | ||
| 17 | pub eocdr64_pointer: u64, | ||
| 18 | pub disks: u32, | ||
| 19 | } | ||
| 20 | |||
| 21 | #[derive(Serialize, Deserialize)] | ||
| 22 | pub struct EOCDR64 { | ||
| 23 | pub eocdr64_size: u64, | ||
| 24 | pub version: u16, | ||
| 25 | pub version_needed: u16, | ||
| 26 | pub eocdr64_disk: u32, | ||
| 27 | pub cd_disk: u32, | ||
| 28 | pub cd_disk_records: u64, | ||
| 29 | pub cd_records: u64, | ||
| 30 | pub cd_size: u64, | ||
| 31 | pub cd_pointer: u64, | ||
| 32 | } | ||
| 33 | |||
| 34 | #[derive(Serialize, Deserialize)] | ||
| 35 | pub struct CDR { | ||
| 36 | pub version: u16, | ||
| 37 | pub version_needed: u16, | ||
| 38 | pub bit_flag: u16, | ||
| 39 | pub compression_method: u16, | ||
| 40 | pub dos_time: u16, | ||
| 41 | pub dos_date: u16, | ||
| 42 | pub crc32: u32, | ||
| 43 | pub compressed_size: u32, | ||
| 44 | pub size: u32, | ||
| 45 | pub name_len: u16, | ||
| 46 | pub extra_field_len: u16, | ||
| 47 | pub comment_len: u16, | ||
| 48 | pub disk: u16, | ||
| 49 | pub internal_attributes: u16, | ||
| 50 | pub external_attributes: u32, | ||
| 51 | pub header_pointer: u32, | ||
| 52 | } | ||
diff --git a/tests/usage.rs b/tests/usage.rs new file mode 100644 index 0000000..64f7050 --- /dev/null +++ b/tests/usage.rs | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | use archivator::{Archive, Zip}; | ||
| 2 | use std::fs::File; | ||
| 3 | use std::time::{SystemTime, UNIX_EPOCH}; | ||
| 4 | |||
| 5 | fn time() -> f64 { | ||
| 6 | SystemTime::now() | ||
| 7 | .duration_since(UNIX_EPOCH) | ||
| 8 | .unwrap() | ||
| 9 | .as_secs_f64() | ||
| 10 | } | ||
| 11 | |||
| 12 | #[test] | ||
| 13 | fn time_test() { | ||
| 14 | let file = File::open("tests/files/1M.zip").unwrap(); | ||
| 15 | |||
| 16 | let start = time(); | ||
| 17 | let archive = Archive::<Zip>::new(file).unwrap(); | ||
| 18 | println!("{}", time() - start); | ||
| 19 | } | ||
