#![doc = include_str!("../README.md")] use std::{ collections::HashMap, fs, io::{BufWriter, Write}, sync::Mutex, }; #[cfg(feature = "parallel")] use { rayon::prelude::{IntoParallelRefIterator, ParallelIterator}, std::{ops::DerefMut, sync::mpsc::Sender}, }; mod checksum; mod error; mod file; mod filetype; pub(crate) mod nix; mod node; mod special; mod stream; pub use { checksum::{Algorithm, Checksum}, error::Error, file::File, filetype::FileType, node::Node, special::Special, stream::Stream }; #[cfg(feature = "parallel")] pub use stream::Message as StreamMessage; pub static MAGIC: [u8; 7] = [0x89, b'h', b'a', b'g', b'g', b'i', b's' ]; /// Creates a haggis archive from a list of files pub fn create_archive(path: &str, files: Vec, algorithm: Algorithm) -> Result<(), Error> { let fd = fs::OpenOptions::new() .create(true) .truncate(true) .open(path)?; let mut writer = BufWriter::new(fd); stream_archive(&mut writer, files, algorithm)?; Ok(()) } /// Streams a haggis archive over something which implements `Write` pub fn stream_archive( mut writer: W, files: Vec, algorithm: Algorithm, ) -> Result<(), Error> { let links = Mutex::new(HashMap::new()); writer.write_all(&MAGIC)?; for f in &files { let node = Node::from_path(f, algorithm, &links)?; node.write(&mut writer)?; } writer.write_all(&[0; 8])?; Ok(()) } #[cfg(feature = "parallel")] #[derive(Debug)] pub enum Message { NodeCreated(String), NodeSaved { name: String, size: u64 }, Err(Error), Eof, } /// Creates a Haggis archive from a list of files, processing each file in parallel #[cfg(feature = "parallel")] pub fn par_create_archive( path: &str, files: Vec, algorithm: Algorithm, sender: &Sender, ) -> Result<(), Error> { let fd = fs::File::create(path)?; let writer = BufWriter::new(fd); par_stream_archive(writer, files, algorithm, sender)?; Ok(()) } /// Streams a Haggis archive from a list of files, processing each file in parallel #[cfg(feature = "parallel")] pub fn par_stream_archive( mut writer: W, files: Vec, algorithm: Algorithm, sender: &Sender, ) -> Result<(), Error> { writer.write_all(&MAGIC)?; let links = Mutex::new(HashMap::::new()); let writer = Mutex::new(writer); let s = sender.clone(); files.par_iter().try_for_each_with(s, |s, f| { let node = match Node::from_path(f, algorithm, &links) { Ok(n) => n, Err(e) => { s.send(Message::Err(e)).map_err(|_| Error::SenderError)?; return Ok(()); } }; if let Ok(mut writer) = writer.lock() { let mut writer = writer.deref_mut(); if let Err(e) = node.write(&mut writer) { s.send(Message::Err(e)).map_err(|_| Error::SenderError)?; } match node.filetype { FileType::Normal(n) => s .send(Message::NodeSaved { name: node.name.clone(), size: n.len, }) .map_err(|_| Error::SenderError)?, _ => s .send(Message::NodeCreated(node.name.clone())) .map_err(|_| Error::SenderError)?, } Ok(()) } else { Err(Error::MutexError) } })?; let mut writer = writer.into_inner().map_err(|_| Error::MutexError)?; writer.write_all(&[0; 8])?; sender.send(Message::Eof).map_err(|_| Error::SenderError)?; Ok(()) }