use super::layers::traits::InnerWriterTrait;
use super::{ArchiveFileBlock, ArchiveFileID, ArchiveReader, ArchiveWriter, Error};
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::io::{self, Read, Seek, SeekFrom, Write};
pub fn linear_extract<W1: InnerWriterTrait, R: Read + Seek, S: BuildHasher>(
archive: &mut ArchiveReader<R>,
export: &mut HashMap<&String, W1, S>,
) -> Result<(), Error> {
archive.src.seek(SeekFrom::Start(0))?;
let mut src = io::BufReader::new(&mut archive.src);
let mut id2filename: HashMap<ArchiveFileID, String> = HashMap::new();
'read_block: loop {
match ArchiveFileBlock::from(&mut src)? {
ArchiveFileBlock::FileStart { filename, id } => {
if export.contains_key(&filename) {
id2filename.insert(id, filename.clone());
}
}
ArchiveFileBlock::EndOfFile { id, .. } => {
id2filename.remove(&id);
}
ArchiveFileBlock::FileContent { length, id, .. } => {
let copy_src = &mut (&mut src).take(length);
let mut extracted: bool = false;
if let Some(fname) = id2filename.get(&id) {
if let Some(writer) = export.get_mut(fname) {
io::copy(copy_src, writer)?;
extracted = true;
}
};
if !extracted {
io::copy(copy_src, &mut io::sink())?;
}
}
ArchiveFileBlock::EndOfArchiveData {} => {
break 'read_block;
}
}
}
Ok(())
}
pub struct StreamWriter<'a, 'b, W: InnerWriterTrait> {
archive: &'b mut ArchiveWriter<'a, W>,
file_id: ArchiveFileID,
}
impl<'a, 'b, W: InnerWriterTrait> StreamWriter<'a, 'b, W> {
pub fn new(archive: &'b mut ArchiveWriter<'a, W>, file_id: ArchiveFileID) -> Self {
Self { archive, file_id }
}
}
impl<'a, 'b, W: InnerWriterTrait> Write for StreamWriter<'a, 'b, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.archive
.append_file_content(self.file_id, buf.len() as u64, buf)?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
self.archive.flush()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::build_archive;
use crate::*;
use std::io::Cursor;
#[test]
fn full_linear_extract() {
let (mla, key, files) = build_archive(None, false);
let dest = Cursor::new(mla.into_raw());
let mut config = ArchiveReaderConfig::new();
config.add_private_keys(std::slice::from_ref(&key));
let mut mla_read = ArchiveReader::from_config(dest, config).unwrap();
let file_list: Vec<String> = mla_read
.list_files()
.expect("reader.list_files")
.cloned()
.collect();
let mut export: HashMap<&String, Vec<u8>> =
file_list.iter().map(|fname| (fname, Vec::new())).collect();
linear_extract(&mut mla_read, &mut export).expect("Extract error");
for (fname, content) in files.iter() {
assert_eq!(export.get(fname).unwrap(), content);
}
}
#[test]
fn one_linear_extract() {
let (mla, key, files) = build_archive(None, false);
let dest = Cursor::new(mla.into_raw());
let mut config = ArchiveReaderConfig::new();
config.add_private_keys(std::slice::from_ref(&key));
let mut mla_read = ArchiveReader::from_config(dest, config).unwrap();
let mut export: HashMap<&String, Vec<u8>> = HashMap::new();
export.insert(&files[0].0, Vec::new());
linear_extract(&mut mla_read, &mut export).expect("Extract error");
assert_eq!(export.get(&files[0].0).unwrap(), &files[0].1);
}
#[test]
fn stream_writer() {
let file = Vec::new();
let mut mla = ArchiveWriter::from_config(file, ArchiveWriterConfig::new())
.expect("Writer init failed");
let fake_file = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let id = mla.start_file("my_file").unwrap();
let mut sw = StreamWriter::new(&mut mla, id);
sw.write_all(&fake_file[..5]).unwrap();
sw.write_all(&fake_file[5..]).unwrap();
mla.end_file(id).unwrap();
let id = mla.start_file("my_file2").unwrap();
let mut sw = StreamWriter::new(&mut mla, id);
assert_eq!(
io::copy(&mut fake_file.as_slice(), &mut sw).unwrap(),
fake_file.len() as u64
);
mla.end_file(id).unwrap();
mla.finalize().unwrap();
let dest = mla.into_raw();
let buf = Cursor::new(dest.as_slice());
let mut mla_read = ArchiveReader::from_config(buf, ArchiveReaderConfig::new()).unwrap();
let mut content1 = Vec::new();
mla_read
.get_file("my_file".to_string())
.unwrap()
.unwrap()
.data
.read_to_end(&mut content1)
.unwrap();
assert_eq!(content1.as_slice(), fake_file.as_slice());
let mut content2 = Vec::new();
mla_read
.get_file("my_file2".to_string())
.unwrap()
.unwrap()
.data
.read_to_end(&mut content2)
.unwrap();
assert_eq!(content2.as_slice(), fake_file.as_slice());
}
}