Slight refactor for clippy lints; Add Creator type

This commit is contained in:
Nathan Fisher 2024-12-15 02:52:53 -05:00
parent dd8daa26ed
commit dcc571c6d2
5 changed files with 176 additions and 27 deletions

View File

@ -40,7 +40,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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)?;

View File

@ -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<StreamMessage>, len: u64, matches: &ArgMatches) -> u64 {
fn progress(
file: &str,
receiver: &mpsc::Receiver<StreamMessage>,
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<StreamMessage>, 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<StreamMessage>, 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<StreamMessage>, len: u64, matc
}
}
}
return total;
total
}
fn print_listing(li: &Listing, matches: &ArgMatches) -> Result<(), haggis::Error> {

View File

@ -66,6 +66,130 @@ pub(crate) fn load_string<R: Read>(reader: &mut R) -> Result<String, Error> {
Ok(String::from_utf8(buf)?)
}
pub struct Creator<W: Write> {
pub writer: W,
pub algorithm: Algorithm,
pub uid: Option<u32>,
pub gid: Option<u32>,
}
impl Creator<BufWriter<std::fs::File>> {
pub fn new_for_path(
path: &str,
algorithm: Algorithm,
uid: Option<u32>,
gid: Option<u32>,
) -> Result<Self, Error> {
let fd = fs::OpenOptions::new()
.create(true)
.truncate(true)
.open(path)?;
let writer = BufWriter::new(fd);
Ok(Self {
writer,
algorithm,
uid,
gid,
})
}
}
impl Creator<BufWriter<std::io::StdoutLock<'_>>> {
#[allow(clippy::must_use_candidate)]
pub fn new_for_stdout(algorithm: Algorithm, uid: Option<u32>, gid: Option<u32>) -> Self {
let stdout = io::stdout().lock();
let writer = BufWriter::new(stdout);
Self {
writer,
algorithm,
uid,
gid,
}
}
}
impl<W: Write> Creator<W> {
/// 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<W: Write + Send> Creator<W> {
/// 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<Message>,
) -> 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::<u64, String>::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<W: Write + Send>(
})
.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)

View File

@ -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!(),
}

View File

@ -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<R: Read + Send> Stream<R> {
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<R: Read + Send> Stream<R> {
.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>(())
})?;