Compare commits
2 Commits
e3dddc3dd9
...
22faadc34e
Author | SHA1 | Date | |
---|---|---|---|
|
22faadc34e | ||
|
3bc8b9aa80 |
10
src/cli.rs
10
src/cli.rs
@ -27,6 +27,11 @@ pub fn extract() -> Command {
|
|||||||
.long("quiet")
|
.long("quiet")
|
||||||
.visible_alias("silent")
|
.visible_alias("silent")
|
||||||
.action(ArgAction::SetFalse),
|
.action(ArgAction::SetFalse),
|
||||||
|
Arg::new("verbose")
|
||||||
|
.help("List the extracted files")
|
||||||
|
.short('v')
|
||||||
|
.long("verbose")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
Arg::new("stdin")
|
Arg::new("stdin")
|
||||||
.help("Read archive from stdin")
|
.help("Read archive from stdin")
|
||||||
.short('i')
|
.short('i')
|
||||||
@ -99,6 +104,11 @@ pub fn create() -> Command {
|
|||||||
.long("quiet")
|
.long("quiet")
|
||||||
.visible_alias("silent")
|
.visible_alias("silent")
|
||||||
.action(ArgAction::SetFalse),
|
.action(ArgAction::SetFalse),
|
||||||
|
Arg::new("verbose")
|
||||||
|
.help("List the archived files")
|
||||||
|
.short('v')
|
||||||
|
.long("verbose")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
Arg::new("stdout")
|
Arg::new("stdout")
|
||||||
.help("Write archive to stdout")
|
.help("Write archive to stdout")
|
||||||
.short('o')
|
.short('o')
|
||||||
|
@ -95,13 +95,13 @@ fn create(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||||||
handle = Some(thread::spawn(move || {
|
handle = Some(thread::spawn(move || {
|
||||||
for msg in &receiver {
|
for msg in &receiver {
|
||||||
match msg {
|
match msg {
|
||||||
Message::NodeCreated(s) => {
|
Message::NodeSaved(n) => {
|
||||||
pb.set_prefix(s.split('/').last().unwrap().to_string());
|
let name = n.name.split('/').last().unwrap();
|
||||||
pb.inc(1);
|
if let ListingKind::Normal(size) = n.kind {
|
||||||
}
|
|
||||||
Message::NodeSaved { name, size } => {
|
|
||||||
let name = name.split('/').last().unwrap();
|
|
||||||
pb.set_prefix(format!("{name} added, {size} bytes"));
|
pb.set_prefix(format!("{name} added, {size} bytes"));
|
||||||
|
} else {
|
||||||
|
pb.set_prefix(format!("{name} added"));
|
||||||
|
}
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
}
|
}
|
||||||
Message::Eof => {
|
Message::Eof => {
|
||||||
@ -242,28 +242,38 @@ fn progress(file: &str, receiver: &mpsc::Receiver<StreamMessage>, len: u64) -> u
|
|||||||
pb.println(format!("Extracting archive {file}"));
|
pb.println(format!("Extracting archive {file}"));
|
||||||
for msg in receiver {
|
for msg in receiver {
|
||||||
match msg {
|
match msg {
|
||||||
StreamMessage::FileExtracted { name, size } => {
|
StreamMessage::NodeExtracted(n) => {
|
||||||
let name = name.split('/').last().unwrap();
|
let name = n.name.split('/').last().unwrap();
|
||||||
|
match n.kind {
|
||||||
|
ListingKind::Normal(size) => {
|
||||||
pb.set_prefix(format!("{name} extracted, {size} bytes"));
|
pb.set_prefix(format!("{name} extracted, {size} bytes"));
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
total += size;
|
total += size;
|
||||||
}
|
}
|
||||||
StreamMessage::LinkCreated { name, target } => {
|
ListingKind::SoftLink(t)
|
||||||
let name = name.split('/').last().unwrap();
|
| ListingKind::HardLink(t) => {
|
||||||
let target = target.split('/').last().unwrap();
|
pb.set_prefix(format!("{name} -> {t}"));
|
||||||
pb.set_prefix(format!("{name} -> {target}"));
|
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
}
|
}
|
||||||
StreamMessage::DirectoryCreated { name } => {
|
ListingKind::Directory => {
|
||||||
let name = name.split('/').last().unwrap();
|
|
||||||
pb.set_prefix(format!("mkdir {name}"));
|
pb.set_prefix(format!("mkdir {name}"));
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
}
|
}
|
||||||
StreamMessage::DeviceCreated { name } => {
|
ListingKind::Block(_d)
|
||||||
let name = name.split('/').last().unwrap();
|
| ListingKind::Character(_d) => {
|
||||||
pb.set_prefix(format!("mknod {name}"));
|
pb.set_prefix(format!("mknod {name}"));
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
}
|
}
|
||||||
|
ListingKind::Fifo => {
|
||||||
|
pb.set_prefix(format!("mkfifo {name}"));
|
||||||
|
pb.inc(1);
|
||||||
|
}
|
||||||
|
ListingKind::Eof => {
|
||||||
|
pb.finish_and_clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
StreamMessage::Eof => {
|
StreamMessage::Eof => {
|
||||||
pb.finish_and_clear();
|
pb.finish_and_clear();
|
||||||
break;
|
break;
|
||||||
|
24
src/lib.rs
24
src/lib.rs
@ -137,18 +137,8 @@ pub fn stream_archive<W: Write>(
|
|||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
/// A `Node` has successfully been created from this path
|
|
||||||
NodeCreated(
|
|
||||||
/// The path of the created `Node`
|
|
||||||
String,
|
|
||||||
),
|
|
||||||
/// The `Node` has successfully been written into the writer
|
/// The `Node` has successfully been written into the writer
|
||||||
NodeSaved {
|
NodeSaved(Listing),
|
||||||
/// The path of the saved `Node`
|
|
||||||
name: String,
|
|
||||||
/// The size in bytes of the created `Node`
|
|
||||||
size: u64,
|
|
||||||
},
|
|
||||||
/// An error occurred creating or writing out the node
|
/// An error occurred creating or writing out the node
|
||||||
Err {
|
Err {
|
||||||
/// The pathname of the node
|
/// The pathname of the node
|
||||||
@ -241,17 +231,7 @@ pub fn par_stream_archive<W: Write + Send>(
|
|||||||
})
|
})
|
||||||
.map_err(|_| Error::SenderError)?;
|
.map_err(|_| Error::SenderError)?;
|
||||||
}
|
}
|
||||||
match node.filetype {
|
s.send(Message::NodeSaved(node.into())).map_err(|_| Error::SenderError)?;
|
||||||
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(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::MutexError)
|
Err(Error::MutexError)
|
||||||
|
107
src/stream.rs
107
src/stream.rs
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
use {
|
use {
|
||||||
crate::FileType,
|
crate::{ FileType, Listing },
|
||||||
rayon::{iter::ParallelBridge, prelude::ParallelIterator},
|
rayon::{iter::ParallelBridge, prelude::ParallelIterator},
|
||||||
std::sync::mpsc::Sender,
|
std::sync::mpsc::Sender,
|
||||||
};
|
};
|
||||||
@ -44,30 +44,8 @@ impl<R: Read> Iterator for Stream<R> {
|
|||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
/// A File node has been successfully extracted
|
/// A Node has been successfully extracted
|
||||||
FileExtracted {
|
NodeExtracted(Listing),
|
||||||
/// The path of the extracted `Node`
|
|
||||||
name: String,
|
|
||||||
/// The size in bytes of the created file
|
|
||||||
size: u64,
|
|
||||||
},
|
|
||||||
/// A link has been created from a `Node`
|
|
||||||
LinkCreated {
|
|
||||||
/// The path of the link
|
|
||||||
name: String,
|
|
||||||
/// The path to this link's target
|
|
||||||
target: String,
|
|
||||||
},
|
|
||||||
/// A directory has been created from this `Node`
|
|
||||||
DirectoryCreated {
|
|
||||||
/// The directory's path
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
/// A device file has been created from this `Node`
|
|
||||||
DeviceCreated {
|
|
||||||
/// The path of the device file
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
/// An error occurred while extracting this `Node`
|
/// An error occurred while extracting this `Node`
|
||||||
Err {
|
Err {
|
||||||
/// The pathame of the `Node` being extracted
|
/// The pathame of the `Node` being extracted
|
||||||
@ -135,12 +113,12 @@ impl<R: Read> Stream<R> {
|
|||||||
f: F,
|
f: F,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: FnOnce(Node, Option<u32>, Option<u32>) + Copy,
|
F: FnOnce(&Node, Option<u32>, Option<u32>) + Copy,
|
||||||
{
|
{
|
||||||
for node in self {
|
for node in self {
|
||||||
let node = node?;
|
let node = node?;
|
||||||
node.extract(prefix, uid, gid)?;
|
node.extract(prefix, uid, gid)?;
|
||||||
f(node, uid, gid);
|
f(&node, uid, gid);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -148,7 +126,7 @@ impl<R: Read> Stream<R> {
|
|||||||
|
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
impl<R: Read + Send> Stream<R> {
|
impl<R: Read + Send> Stream<R> {
|
||||||
/// Extracts and archive in parallel
|
/// Extracts an archive in parallel
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns `crate::Error` if io fails or several other error conditions
|
/// Returns `crate::Error` if io fails or several other error conditions
|
||||||
pub fn par_extract(
|
pub fn par_extract(
|
||||||
@ -170,35 +148,12 @@ impl<R: Read + Send> Stream<R> {
|
|||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
match n.filetype {
|
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 => {
|
FileType::Eof => {
|
||||||
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)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok::<(), Error>(())
|
Ok::<(), Error>(())
|
||||||
})?;
|
})?;
|
||||||
@ -206,7 +161,7 @@ impl<R: Read + Send> Stream<R> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts and archive in parallel and runs the passed in function for
|
/// Extracts an archive in parallel and runs the passed in function for
|
||||||
/// each `Node`
|
/// each `Node`
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns `crate::Error` if io fails or several other error conditions
|
/// Returns `crate::Error` if io fails or several other error conditions
|
||||||
@ -219,44 +174,28 @@ impl<R: Read + Send> Stream<R> {
|
|||||||
f: F,
|
f: F,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: FnOnce(Node, Option<u32>, Option<u32>) + Copy + Send + Sync,
|
F: FnOnce(&Node, Option<u32>, Option<u32>) + Copy + Send + Sync,
|
||||||
{
|
{
|
||||||
let s = sender.clone();
|
let s = sender.clone();
|
||||||
self.into_iter().par_bridge().try_for_each_with(s, |s, n| {
|
self.into_iter().par_bridge().try_for_each_with(s, |s, n| {
|
||||||
let n = n?;
|
let n = n?;
|
||||||
n.extract(prefix, uid, gid)?;
|
if let Err(error) = n.extract(prefix, uid, gid) {
|
||||||
|
s.send(Message::Err {
|
||||||
|
name: n.name.clone(),
|
||||||
|
error: error.clone(),
|
||||||
|
})
|
||||||
|
.map_err(|_| Error::SenderError)?;
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
match n.filetype {
|
match n.filetype {
|
||||||
FileType::Normal(ref f) => {
|
|
||||||
s.send(Message::FileExtracted {
|
|
||||||
name: n.name.clone(),
|
|
||||||
size: f.len,
|
|
||||||
})
|
|
||||||
.map_err(|_| Error::SenderError)?;
|
|
||||||
}
|
|
||||||
FileType::SoftLink(ref t) | FileType::HardLink(ref 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 => {
|
FileType::Eof => {
|
||||||
s.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
s.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
f(&n, uid, gid);
|
||||||
|
s.send(Message::NodeExtracted(n.into())).map_err(|_| Error::SenderError)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f(n, uid, gid);
|
|
||||||
Ok::<(), Error>(())
|
Ok::<(), Error>(())
|
||||||
})?;
|
})?;
|
||||||
sender.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
sender.send(Message::Eof).map_err(|_| Error::SenderError)?;
|
||||||
|
Loading…
Reference in New Issue
Block a user