diff --git a/src/bootstrap.rs b/src/bootstrap.rs index 134facb..ecec03b 100644 --- a/src/bootstrap.rs +++ b/src/bootstrap.rs @@ -40,7 +40,7 @@ fn main() -> Result<(), Box> { let bs = Bootstrap::new("haggis", cli::haggis(), &outdir); bs.install(target_dir, 1)?; if matches.get_flag("meta") { - bs.docfiles(&["README.md", "LICENSE.md"], &Path::new("haggis"))?; + bs.docfiles(&["README.md", "LICENSE.md"], Path::new("haggis"))?; } Bootstrap::new("haggis-create", cli::create(), &outdir).manpage(1)?; Bootstrap::new("haggis-extract", cli::extract(), &outdir).manpage(1)?; diff --git a/src/haggis.rs b/src/haggis.rs index 2c00633..c20ecbd 100644 --- a/src/haggis.rs +++ b/src/haggis.rs @@ -25,6 +25,7 @@ mod cli; static TEMPLATE: &str = "[ {prefix:^30!} ] {wide_bar}{pos:>5.cyan}/{len:5.green}"; +#[allow(clippy::struct_excessive_bools)] struct ListingFlags { files: bool, color: bool, @@ -187,12 +188,26 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> { if flags.stdout { let stdout = io::stdout(); let mut writer = Encoder::new(stdout, level)?; - haggis::par_stream_archive(&mut writer, &files, flags.algorithm, &sender, flags.uid, flags.gid)?; + haggis::par_stream_archive( + &mut writer, + &files, + flags.algorithm, + &sender, + flags.uid, + flags.gid, + )?; let _fd = writer.finish(); } else if let Some(o) = output { let fd = File::create(o)?; let mut writer = Encoder::new(fd, level)?; - haggis::par_stream_archive(&mut writer, &files, flags.algorithm, &sender, flags.uid, flags.gid)?; + haggis::par_stream_archive( + &mut writer, + &files, + flags.algorithm, + &sender, + flags.uid, + flags.gid, + )?; let _fd = writer.finish()?; } else { unreachable!(); @@ -200,7 +215,14 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> { } else if flags.stdout { let stdout = io::stdout(); let mut writer = BufWriter::new(stdout); - haggis::par_stream_archive(&mut writer, &files, flags.algorithm, &sender, flags.uid, flags.gid)?; + haggis::par_stream_archive( + &mut writer, + &files, + flags.algorithm, + &sender, + flags.uid, + flags.gid, + )?; } else if let Some(o) = output { haggis::par_create_archive(o, &files, flags.algorithm, &sender, flags.uid, flags.gid)?; } else { @@ -303,7 +325,12 @@ fn extract(matches: &ArgMatches) -> Result<(), haggis::Error> { } } -fn progress(file: &str, receiver: &mpsc::Receiver, len: u64, matches: &ArgMatches) -> u64 { +fn progress( + file: &str, + receiver: &mpsc::Receiver, + len: u64, + matches: &ArgMatches, +) -> u64 { let mut total: u64 = 0; let pb = ProgressBar::new(len); pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap()); @@ -319,8 +346,7 @@ fn progress(file: &str, receiver: &mpsc::Receiver, len: u64, matc pb.inc(1); total += size; } - ListingKind::SoftLink(ref t) - | ListingKind::HardLink(ref t) => { + ListingKind::SoftLink(ref t) | ListingKind::HardLink(ref t) => { pb.set_prefix(format!("{name} -> {t}")); pb.inc(1); } @@ -328,8 +354,7 @@ fn progress(file: &str, receiver: &mpsc::Receiver, len: u64, matc pb.set_prefix(format!("mkdir {name}")); pb.inc(1); } - ListingKind::Block(_) - | ListingKind::Character(_) => { + ListingKind::Block(_) | ListingKind::Character(_) => { pb.set_prefix(format!("mknod {name}")); pb.inc(1); } @@ -357,7 +382,7 @@ fn progress(file: &str, receiver: &mpsc::Receiver, len: u64, matc } } } - return total; + total } fn print_listing(li: &Listing, matches: &ArgMatches) -> Result<(), haggis::Error> { diff --git a/src/lib.rs b/src/lib.rs index 597bb1d..beb9fc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,130 @@ pub(crate) fn load_string(reader: &mut R) -> Result { Ok(String::from_utf8(buf)?) } +pub struct Creator { + pub writer: W, + pub algorithm: Algorithm, + pub uid: Option, + pub gid: Option, +} + +impl Creator> { + pub fn new_for_path( + path: &str, + algorithm: Algorithm, + uid: Option, + gid: Option, + ) -> Result { + let fd = fs::OpenOptions::new() + .create(true) + .truncate(true) + .open(path)?; + let writer = BufWriter::new(fd); + Ok(Self { + writer, + algorithm, + uid, + gid, + }) + } +} + +impl Creator>> { + #[allow(clippy::must_use_candidate)] + pub fn new_for_stdout(algorithm: Algorithm, uid: Option, gid: Option) -> Self { + let stdout = io::stdout().lock(); + let writer = BufWriter::new(stdout); + Self { + writer, + algorithm, + uid, + gid, + } + } +} + +impl Creator { + /// Creates and streams a haggis archive over something which implements `Write` + /// # Errors + /// Returns `crate::Error` if io fails or several other error conditions + pub fn stream_archive(&mut self, files: &[String]) -> Result<(), Error> { + self.writer.write_all(&MAGIC)?; + let len = u32::try_from(files.len())?; + self.writer.write_all(&len.to_le_bytes())?; + let links = Mutex::new(HashMap::new()); + for f in files { + let mut node = Node::from_path(f, self.algorithm, &links)?; + if let Some(n) = self.uid { + node.uid = n; + } + if let Some(n) = self.gid { + node.gid = n; + } + node.write(&mut self.writer)?; + } + self.writer.write_all(&[0; 8])?; + Ok(()) + } +} + +#[cfg(feature = "parallel")] +impl Creator { + /// Creates and streams a Haggis archive from a list of files, processing each + /// file in parallel + /// # Errors + /// Returns `crate::Error` if io fails or several other error conditions + pub fn par_stream_archive( + mut self, + files: &[String], + sender: &Sender, + ) -> Result<(), Error> { + self.writer.write_all(&MAGIC)?; + let len = u32::try_from(files.len())?; + self.writer.write_all(&len.to_le_bytes())?; + let links = Mutex::new(HashMap::::new()); + let writer = Mutex::new(self.writer); + let s = sender.clone(); + files.par_iter().try_for_each_with(s, |s, f| { + let mut node = match Node::from_path(f, self.algorithm, &links) { + Ok(n) => n, + Err(error) => { + s.send(Message::Err { + name: f.to_string(), + error, + }) + .map_err(|_| Error::SenderError)?; + return Ok(()); + } + }; + if let Some(n) = self.uid { + node.uid = n; + } + if let Some(n) = self.gid { + node.gid = n; + } + if let Ok(mut writer) = writer.lock() { + let mut writer = &mut *writer; + if let Err(error) = node.write(&mut writer) { + s.send(Message::Err { + name: node.name.clone(), + error, + }) + .map_err(|_| Error::SenderError)?; + } + s.send(Message::NodeSaved(node.into())) + .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(()) + } +} + #[allow(clippy::similar_names)] /// Creates a haggis archive from a list of files /// # Errors @@ -231,7 +355,8 @@ pub fn par_stream_archive( }) .map_err(|_| Error::SenderError)?; } - s.send(Message::NodeSaved(node.into())).map_err(|_| Error::SenderError)?; + s.send(Message::NodeSaved(node.into())) + .map_err(|_| Error::SenderError)?; Ok(()) } else { Err(Error::MutexError) diff --git a/src/listing.rs b/src/listing.rs index c14fbf3..966a224 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -293,7 +293,7 @@ impl Listing { Kind::HardLink(ref tgt) => println!("{}=>{}", self.name, tgt), Kind::SoftLink(ref tgt) => println!("{}->{}", self.name, tgt), Kind::Character(ref sp) | Kind::Block(ref sp) => { - println!("{} {},{}", self.name, sp.major, sp.minor) + println!("{} {},{}", self.name, sp.major, sp.minor); } Kind::Eof => unreachable!(), } diff --git a/src/stream.rs b/src/stream.rs index 1bb1615..0caf5d3 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,11 +1,5 @@ #![allow(clippy::similar_names)] -#[cfg(feature = "parallel")] -use { - crate::{ FileType, Listing }, - rayon::{iter::ParallelBridge, prelude::ParallelIterator}, - std::sync::mpsc::Sender, -}; use { crate::{Error, Node, MAGIC}, std::{ @@ -15,6 +9,12 @@ use { os::fd::{AsRawFd, FromRawFd}, }, }; +#[cfg(feature = "parallel")] +use { + crate::{FileType, Listing}, + 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. @@ -152,7 +152,8 @@ impl Stream { s.send(Message::Eof).map_err(|_| Error::SenderError)?; } _ => { - s.send(Message::NodeExtracted(n.into())).map_err(|_| Error::SenderError)?; + s.send(Message::NodeExtracted(n.into())) + .map_err(|_| Error::SenderError)?; } } Ok::<(), Error>(()) @@ -187,14 +188,12 @@ impl Stream { .map_err(|_| Error::SenderError)?; return Err(error); } - match n.filetype { - FileType::Eof => { - s.send(Message::Eof).map_err(|_| Error::SenderError)?; - } - _ => { - f(&n, uid, gid); - s.send(Message::NodeExtracted(n.into())).map_err(|_| Error::SenderError)?; - } + if let FileType::Eof = n.filetype { + s.send(Message::Eof).map_err(|_| Error::SenderError)?; + } else { + f(&n, uid, gid); + s.send(Message::NodeExtracted(n.into())) + .map_err(|_| Error::SenderError)?; } Ok::<(), Error>(()) })?;