Slight refactor for clippy lints; Add Creator
type
This commit is contained in:
parent
dd8daa26ed
commit
dcc571c6d2
@ -40,7 +40,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
let bs = Bootstrap::new("haggis", cli::haggis(), &outdir);
|
let bs = Bootstrap::new("haggis", cli::haggis(), &outdir);
|
||||||
bs.install(target_dir, 1)?;
|
bs.install(target_dir, 1)?;
|
||||||
if matches.get_flag("meta") {
|
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-create", cli::create(), &outdir).manpage(1)?;
|
||||||
Bootstrap::new("haggis-extract", cli::extract(), &outdir).manpage(1)?;
|
Bootstrap::new("haggis-extract", cli::extract(), &outdir).manpage(1)?;
|
||||||
|
@ -25,6 +25,7 @@ mod cli;
|
|||||||
|
|
||||||
static TEMPLATE: &str = "[ {prefix:^30!} ] {wide_bar}{pos:>5.cyan}/{len:5.green}";
|
static TEMPLATE: &str = "[ {prefix:^30!} ] {wide_bar}{pos:>5.cyan}/{len:5.green}";
|
||||||
|
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
struct ListingFlags {
|
struct ListingFlags {
|
||||||
files: bool,
|
files: bool,
|
||||||
color: bool,
|
color: bool,
|
||||||
@ -187,12 +188,26 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||||||
if flags.stdout {
|
if flags.stdout {
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
let mut writer = Encoder::new(stdout, level)?;
|
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();
|
let _fd = writer.finish();
|
||||||
} else if let Some(o) = output {
|
} else if let Some(o) = output {
|
||||||
let fd = File::create(o)?;
|
let fd = File::create(o)?;
|
||||||
let mut writer = Encoder::new(fd, level)?;
|
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()?;
|
let _fd = writer.finish()?;
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@ -200,7 +215,14 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||||||
} else if flags.stdout {
|
} else if flags.stdout {
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
let mut writer = BufWriter::new(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 {
|
} else if let Some(o) = output {
|
||||||
haggis::par_create_archive(o, &files, flags.algorithm, &sender, flags.uid, flags.gid)?;
|
haggis::par_create_archive(o, &files, flags.algorithm, &sender, flags.uid, flags.gid)?;
|
||||||
} else {
|
} 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 mut total: u64 = 0;
|
||||||
let pb = ProgressBar::new(len);
|
let pb = ProgressBar::new(len);
|
||||||
pb.set_style(ProgressStyle::with_template(TEMPLATE).unwrap());
|
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);
|
pb.inc(1);
|
||||||
total += size;
|
total += size;
|
||||||
}
|
}
|
||||||
ListingKind::SoftLink(ref t)
|
ListingKind::SoftLink(ref t) | ListingKind::HardLink(ref t) => {
|
||||||
| ListingKind::HardLink(ref t) => {
|
|
||||||
pb.set_prefix(format!("{name} -> {t}"));
|
pb.set_prefix(format!("{name} -> {t}"));
|
||||||
pb.inc(1);
|
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.set_prefix(format!("mkdir {name}"));
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
}
|
}
|
||||||
ListingKind::Block(_)
|
ListingKind::Block(_) | ListingKind::Character(_) => {
|
||||||
| ListingKind::Character(_) => {
|
|
||||||
pb.set_prefix(format!("mknod {name}"));
|
pb.set_prefix(format!("mknod {name}"));
|
||||||
pb.inc(1);
|
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> {
|
fn print_listing(li: &Listing, matches: &ArgMatches) -> Result<(), haggis::Error> {
|
||||||
|
127
src/lib.rs
127
src/lib.rs
@ -66,6 +66,130 @@ pub(crate) fn load_string<R: Read>(reader: &mut R) -> Result<String, Error> {
|
|||||||
Ok(String::from_utf8(buf)?)
|
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)]
|
#[allow(clippy::similar_names)]
|
||||||
/// Creates a haggis archive from a list of files
|
/// Creates a haggis archive from a list of files
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@ -231,7 +355,8 @@ pub fn par_stream_archive<W: Write + Send>(
|
|||||||
})
|
})
|
||||||
.map_err(|_| Error::SenderError)?;
|
.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(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::MutexError)
|
Err(Error::MutexError)
|
||||||
|
@ -293,7 +293,7 @@ impl Listing {
|
|||||||
Kind::HardLink(ref tgt) => println!("{}=>{}", self.name, tgt),
|
Kind::HardLink(ref tgt) => println!("{}=>{}", self.name, tgt),
|
||||||
Kind::SoftLink(ref tgt) => println!("{}->{}", self.name, tgt),
|
Kind::SoftLink(ref tgt) => println!("{}->{}", self.name, tgt),
|
||||||
Kind::Character(ref sp) | Kind::Block(ref sp) => {
|
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!(),
|
Kind::Eof => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
#![allow(clippy::similar_names)]
|
#![allow(clippy::similar_names)]
|
||||||
|
|
||||||
#[cfg(feature = "parallel")]
|
|
||||||
use {
|
|
||||||
crate::{ FileType, Listing },
|
|
||||||
rayon::{iter::ParallelBridge, prelude::ParallelIterator},
|
|
||||||
std::sync::mpsc::Sender,
|
|
||||||
};
|
|
||||||
use {
|
use {
|
||||||
crate::{Error, Node, MAGIC},
|
crate::{Error, Node, MAGIC},
|
||||||
std::{
|
std::{
|
||||||
@ -15,6 +9,12 @@ use {
|
|||||||
os::fd::{AsRawFd, FromRawFd},
|
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
|
/// 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.
|
/// 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::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>(())
|
Ok::<(), Error>(())
|
||||||
@ -187,14 +188,12 @@ impl<R: Read + Send> Stream<R> {
|
|||||||
.map_err(|_| Error::SenderError)?;
|
.map_err(|_| Error::SenderError)?;
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
match n.filetype {
|
if let FileType::Eof = n.filetype {
|
||||||
FileType::Eof => {
|
|
||||||
s.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
s.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||||
}
|
} else {
|
||||||
_ => {
|
|
||||||
f(&n, uid, gid);
|
f(&n, uid, gid);
|
||||||
s.send(Message::NodeExtracted(n.into())).map_err(|_| Error::SenderError)?;
|
s.send(Message::NodeExtracted(n.into()))
|
||||||
}
|
.map_err(|_| Error::SenderError)?;
|
||||||
}
|
}
|
||||||
Ok::<(), Error>(())
|
Ok::<(), Error>(())
|
||||||
})?;
|
})?;
|
||||||
|
Loading…
Reference in New Issue
Block a user