use crate::MAGIC; use { crate::{Error, Node}, std::{ io::{ErrorKind, Read}, iter::Iterator, }, }; #[cfg(feature = "parallel")] use { crate::FileType, rayon::{iter::ParallelBridge, prelude::ParallelIterator}, std::sync::mpsc::Sender, }; /// An iterator over a series of archive `Node`'s. This struct is generic over any /// type which implements `Read`, such as a file or a network stream. #[derive(Debug)] pub struct Stream { pub length: u32, reader: R, } impl Iterator for Stream { type Item = Result; fn next(&mut self) -> Option { match Node::read(&mut self.reader) { Err(Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => None, Ok(f) => match f.filetype { crate::FileType::Eof => None, _ => Some(Ok(f)), }, x => Some(x), } } } #[cfg(feature = "parallel")] #[derive(Debug)] pub enum Message { FileExtracted { name: String, size: u64 }, LinkCreated { name: String, target: String }, DirectoryCreated { name: String }, DeviceCreated { name: String }, Err(Error), Eof, } impl Stream { pub fn new(mut reader: R) -> Result { let mut buf = [0; 11]; reader.read_exact(&mut buf)?; let length = u32::from_le_bytes(buf[7..].try_into()?); if buf[0..7] == MAGIC { Ok(Self { length ,reader }) } else { Err(Error::InvalidMagic) } } pub fn extract(&mut self, prefix: Option<&str>) -> Result<(), Error> { for node in self { node?.extract(prefix)?; } Ok(()) } #[cfg(feature = "parallel")] pub fn par_extract( &mut self, prefix: Option<&str>, sender: &Sender, ) -> Result<(), Error> { let s = sender.clone(); self.into_iter().par_bridge().try_for_each_with(s, |s, n| { let n = n?; n.extract(prefix)?; match n.filetype { FileType::Normal(f) => { s.send(Message::FileExtracted { name: n.name.clone(), size: f.len }) .map_err(|_| Error::SenderError)?; }, FileType::SoftLink(t) | FileType::HardLink(t) => { s.send(Message::LinkCreated { name: n.name.clone(), target: t.clone() }) .map_err(|_| Error::SenderError)?; }, FileType::Directory => { s.send(Message::DirectoryCreated { name: n.name.clone() }) .map_err(|_| Error::SenderError)?; }, FileType::Block(_) | FileType::Character(_) | FileType::Fifo => { s.send(Message::DeviceCreated { name: n.name.clone() }) .map_err(|_| Error::SenderError)?; }, FileType::Eof => { s.send(Message::Eof).map_err(|_| Error::SenderError)?; }, } Ok::<(), Error>(()) })?; sender.send(Message::Eof).map_err(|_| Error::SenderError)?; Ok(()) } }