mod destination;
pub(crate) mod entries;
mod entry;
mod info;
mod options;
mod archive_open;
mod archive_query;
mod archive_test;
mod decompression;
mod extraction;
mod metadata;
mod multivolume;
mod path_safety;
mod solid_blocks;
pub use destination::{
ExtractDestination, FilesystemDestination, MemoryDestination, NullDestination,
};
#[cfg(feature = "regex")]
pub use entry::SelectByRegex;
pub use entry::{
Entry, EntrySelector, SelectAll, SelectByName, SelectByPredicate, SelectFilesOnly,
};
pub use info::{ArchiveInfo, EncryptionInfo, ExtractResult, TestResult};
pub use options::{
ExtractOptions, FilterPolicy, LinkPolicy, OverwritePolicy, PathSafety, PreserveMetadata,
TestOptions, Threads,
};
pub(crate) use archive_open::{ExtractionLimits, map_io_error};
use std::path::PathBuf;
#[cfg(feature = "aes")]
use crate::Password;
use crate::format::parser::ArchiveHeader;
#[derive(Debug, Clone)]
pub(crate) struct VolumeInfo {
pub count: u32,
pub paths: Vec<PathBuf>,
}
pub struct Archive<R> {
pub(crate) reader: R,
pub(crate) header: ArchiveHeader,
pub(crate) entries: Vec<Entry>,
pub(crate) info: ArchiveInfo,
#[cfg(feature = "aes")]
pub(crate) password: Option<Password>,
pub(crate) volume_info: Option<VolumeInfo>,
pub(crate) sfx_offset: u64,
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
fn make_empty_archive() -> Vec<u8> {
use crate::format::property_id;
let mut data = Vec::new();
data.extend_from_slice(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]);
data.extend_from_slice(&[0x00, 0x04]);
let start_header_crc_pos = data.len();
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
let header_data = vec![property_id::HEADER, property_id::END];
let header_size = header_data.len() as u64;
data.extend_from_slice(&header_size.to_le_bytes());
let header_crc = crc32fast::hash(&header_data);
data.extend_from_slice(&header_crc.to_le_bytes());
let start_header_crc = crc32fast::hash(&data[12..32]);
data[start_header_crc_pos..start_header_crc_pos + 4]
.copy_from_slice(&start_header_crc.to_le_bytes());
data.extend_from_slice(&header_data);
data
}
#[test]
fn test_archive_info_default() {
let info = ArchiveInfo::default();
assert_eq!(info.entry_count, 0);
assert!(!info.is_solid);
}
#[test]
fn test_extract_options_builder() {
let opts = ExtractOptions::new()
.overwrite(OverwritePolicy::Overwrite)
.path_safety(PathSafety::Strict);
assert_eq!(opts.overwrite, OverwritePolicy::Overwrite);
assert_eq!(opts.path_safety, PathSafety::Strict);
}
#[test]
fn test_open_empty_archive() {
let data = make_empty_archive();
let cursor = Cursor::new(data);
let archive = Archive::open(cursor).unwrap();
assert!(archive.is_empty());
assert_eq!(archive.len(), 0);
}
}