Display sizes in human readable form
This commit is contained in:
parent
3e9a34af7e
commit
e3dddc3dd9
@ -153,4 +153,5 @@ to see Haggis implemented in other languages.
|
|||||||
- [x] Add path to error message when passing between threads
|
- [x] Add path to error message when passing between threads
|
||||||
- [x] Add ability to write archives to stdout
|
- [x] Add ability to write archives to stdout
|
||||||
- [x] Add ability to read archives from stdin
|
- [x] Add ability to read archives from stdin
|
||||||
- [ ] Add option to display total size to archive listings
|
- [x] Add option to display total size to archive listings
|
||||||
|
- [x] Optionally display sizes in human readable form
|
||||||
|
@ -164,6 +164,11 @@ pub fn list() -> Command {
|
|||||||
.short('t')
|
.short('t')
|
||||||
.long("total")
|
.long("total")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
|
Arg::new("human")
|
||||||
|
.help("Display sizes in human-readable form")
|
||||||
|
.short('H')
|
||||||
|
.long("human")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
Arg::new("archive")
|
Arg::new("archive")
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
.required(true)
|
.required(true)
|
||||||
|
@ -11,7 +11,10 @@ use {
|
|||||||
io::{self, BufReader, BufWriter},
|
io::{self, BufReader, BufWriter},
|
||||||
os::fd::{AsRawFd, FromRawFd},
|
os::fd::{AsRawFd, FromRawFd},
|
||||||
process,
|
process,
|
||||||
sync::{Arc, atomic::{AtomicU64, Ordering}, mpsc},
|
sync::{
|
||||||
|
atomic::{AtomicU64, Ordering},
|
||||||
|
mpsc, Arc,
|
||||||
|
},
|
||||||
thread,
|
thread,
|
||||||
},
|
},
|
||||||
walkdir::WalkDir,
|
walkdir::WalkDir,
|
||||||
@ -212,7 +215,10 @@ fn extract(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||||||
match handle.join() {
|
match handle.join() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if matches.get_flag("total") {
|
if matches.get_flag("total") {
|
||||||
println!("{} extracted", HumanSize::from(total.load(Ordering::Relaxed)));
|
println!(
|
||||||
|
"{} extracted",
|
||||||
|
HumanSize::from(total.load(Ordering::Relaxed))
|
||||||
|
);
|
||||||
} else if matches.get_flag("quiet") {
|
} else if matches.get_flag("quiet") {
|
||||||
println!("Archive extracted successfully");
|
println!("Archive extracted successfully");
|
||||||
}
|
}
|
||||||
@ -276,12 +282,12 @@ fn print_listing(li: &Listing, matches: &ArgMatches) -> Result<(), haggis::Error
|
|||||||
}
|
}
|
||||||
if matches.get_flag("color") {
|
if matches.get_flag("color") {
|
||||||
if matches.get_flag("long") {
|
if matches.get_flag("long") {
|
||||||
li.print_color()?;
|
li.print_color(matches.get_flag("human"))?;
|
||||||
} else {
|
} else {
|
||||||
li.print_color_simple()?;
|
li.print_color_simple()?;
|
||||||
}
|
}
|
||||||
} else if matches.get_flag("long") {
|
} else if matches.get_flag("long") {
|
||||||
println!("{li}");
|
li.print(matches.get_flag("human"));
|
||||||
} else {
|
} else {
|
||||||
println!("{}", li.name);
|
println!("{}", li.name);
|
||||||
}
|
}
|
||||||
@ -289,15 +295,22 @@ fn print_listing(li: &Listing, matches: &ArgMatches) -> Result<(), haggis::Error
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn list_unsorted(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
fn list_unsorted(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
||||||
|
let mut total: u64 = 0;
|
||||||
let file = matches.get_one::<String>("archive").unwrap();
|
let file = matches.get_one::<String>("archive").unwrap();
|
||||||
let fd = File::open(file)?;
|
let mut fd = File::open(file)?;
|
||||||
if matches.get_flag("zstd") {
|
let zst = matches.get_flag("zstd") || haggis::detect_zstd(&mut fd)?;
|
||||||
|
if zst {
|
||||||
let reader = Decoder::new(fd)?;
|
let reader = Decoder::new(fd)?;
|
||||||
let stream = NodeStream::new(reader)?;
|
let stream = NodeStream::new(reader)?;
|
||||||
for node in stream {
|
for node in stream {
|
||||||
let node = node?;
|
let node = node?;
|
||||||
let li = Listing::from(node);
|
let li = Listing::from(node);
|
||||||
print_listing(&li, matches)?;
|
print_listing(&li, matches)?;
|
||||||
|
if matches.get_flag("total") {
|
||||||
|
if let ListingKind::Normal(s) = li.kind {
|
||||||
|
total += s;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let reader = BufReader::new(fd);
|
let reader = BufReader::new(fd);
|
||||||
@ -305,8 +318,16 @@ fn list_unsorted(matches: &ArgMatches) -> Result<(), haggis::Error> {
|
|||||||
for li in stream {
|
for li in stream {
|
||||||
let li = li?;
|
let li = li?;
|
||||||
print_listing(&li, matches)?;
|
print_listing(&li, matches)?;
|
||||||
|
if matches.get_flag("total") {
|
||||||
|
if let ListingKind::Normal(s) = li.kind {
|
||||||
|
total += s;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if matches.get_flag("total") {
|
||||||
|
println!("Total: {}", HumanSize::from(total));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ pub enum HumanSize {
|
|||||||
impl fmt::Display for HumanSize {
|
impl fmt::Display for HumanSize {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Bytes(b) => write!(f, "{b}"),
|
Self::Bytes(b) => write!(f, "{b:>7}"),
|
||||||
Self::KiloBytes(k) => write!(f, "{k:.1}K"),
|
Self::KiloBytes(k) => write!(f, "{k:>6.1}K"),
|
||||||
Self::MegaBytes(m) => write!(f, "{m:.1}M"),
|
Self::MegaBytes(m) => write!(f, "{m:>6.1}M"),
|
||||||
Self::GigaBytes(g) => write!(f, "{g:.1}G"),
|
Self::GigaBytes(g) => write!(f, "{g:>6.1}G"),
|
||||||
Self::TeraBytes(t) => write!(f, "{t:.1}T"),
|
Self::TeraBytes(t) => write!(f, "{t:>6.1}T"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
185
src/listing.rs
185
src/listing.rs
@ -5,11 +5,10 @@ use {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{filetype::Flag, Error, FileType, Node, Special},
|
crate::{filetype::Flag, Error, FileType, HumanSize, Node, Special},
|
||||||
chrono::NaiveDateTime,
|
chrono::NaiveDateTime,
|
||||||
std::{
|
std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
fmt,
|
|
||||||
io::{Read, Seek, SeekFrom},
|
io::{Read, Seek, SeekFrom},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -172,84 +171,6 @@ impl Ord for Listing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Listing {
|
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}{}{}{}{}{}{}{}{}{} {:>4}:{:<4} ",
|
|
||||||
match self.kind {
|
|
||||||
Kind::Normal(_) => "-",
|
|
||||||
Kind::HardLink(_) => "L",
|
|
||||||
Kind::SoftLink(_) => "l",
|
|
||||||
Kind::Directory => "d",
|
|
||||||
Kind::Character(_) => "c",
|
|
||||||
Kind::Block(_) => "b",
|
|
||||||
Kind::Fifo => "p",
|
|
||||||
Kind::Eof => return Ok(()),
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o400 != 0 => "r",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o200 != 0 => "w",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o4000 != 0 => "S",
|
|
||||||
m if m & 0o100 != 0 => "x",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o40 != 0 => "r",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o20 != 0 => "w",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o2000 != 0 => "S",
|
|
||||||
m if m & 0o10 != 0 => "x",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o4 != 0 => "r",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o2 != 0 => "w",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
match self.mode {
|
|
||||||
m if m & 0o1000 != 0 => "t",
|
|
||||||
m if m & 0o1 != 0 => "x",
|
|
||||||
_ => "-",
|
|
||||||
},
|
|
||||||
self.uid,
|
|
||||||
self.gid,
|
|
||||||
)?;
|
|
||||||
match self.kind {
|
|
||||||
Kind::Normal(s) => write!(f, "{s:>10} "),
|
|
||||||
_ => write!(f, "{:>10}", "-"),
|
|
||||||
}?;
|
|
||||||
match NaiveDateTime::from_timestamp_opt(self.mtime as i64, 0) {
|
|
||||||
Some(dt) => write!(f, "{dt} ")?,
|
|
||||||
_ => write!(f, "{:>19} ", self.mtime)?,
|
|
||||||
}
|
|
||||||
match self.kind {
|
|
||||||
Kind::Directory | Kind::Fifo | Kind::Normal(_) => write!(f, "{}", self.name),
|
|
||||||
Kind::HardLink(ref tgt) => write!(f, "{}=>{}", self.name, tgt),
|
|
||||||
Kind::SoftLink(ref tgt) => write!(f, "{}->{}", self.name, tgt),
|
|
||||||
Kind::Character(ref sp) | Kind::Block(ref sp) => {
|
|
||||||
write!(f, "{} {},{}", self.name, sp.major, sp.minor)
|
|
||||||
}
|
|
||||||
Kind::Eof => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Listing {
|
impl Listing {
|
||||||
/// Reads all metadata from a haggis Node without reading the file's actual
|
/// Reads all metadata from a haggis Node without reading the file's actual
|
||||||
/// data.
|
/// data.
|
||||||
@ -292,6 +213,92 @@ impl Listing {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print(&self, human: bool) {
|
||||||
|
print!(
|
||||||
|
"{}{}{}{}{}{}{}{}{}{} {:>4}:{:<4} ",
|
||||||
|
match self.kind {
|
||||||
|
Kind::Normal(_) => "-",
|
||||||
|
Kind::HardLink(_) => "L",
|
||||||
|
Kind::SoftLink(_) => "l",
|
||||||
|
Kind::Directory => "d",
|
||||||
|
Kind::Character(_) => "c",
|
||||||
|
Kind::Block(_) => "b",
|
||||||
|
Kind::Fifo => "p",
|
||||||
|
Kind::Eof => return,
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o400 != 0 => "r",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o200 != 0 => "w",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o4000 != 0 => "S",
|
||||||
|
m if m & 0o100 != 0 => "x",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o40 != 0 => "r",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o20 != 0 => "w",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o2000 != 0 => "S",
|
||||||
|
m if m & 0o10 != 0 => "x",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o4 != 0 => "r",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o2 != 0 => "w",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
match self.mode {
|
||||||
|
m if m & 0o1000 != 0 => "t",
|
||||||
|
m if m & 0o1 != 0 => "x",
|
||||||
|
_ => "-",
|
||||||
|
},
|
||||||
|
self.uid,
|
||||||
|
self.gid,
|
||||||
|
);
|
||||||
|
match self.kind {
|
||||||
|
Kind::Normal(s) => {
|
||||||
|
if human {
|
||||||
|
print!("{} ", HumanSize::from(s));
|
||||||
|
} else {
|
||||||
|
print!("{s:>10} ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if human {
|
||||||
|
print!("{:>7} ", "-");
|
||||||
|
} else {
|
||||||
|
print!("{:>10} ", "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match NaiveDateTime::from_timestamp_opt(self.mtime as i64, 0) {
|
||||||
|
Some(dt) => print!("{dt} "),
|
||||||
|
_ => print!("{:>19} ", self.mtime),
|
||||||
|
}
|
||||||
|
match self.kind {
|
||||||
|
Kind::Directory | Kind::Fifo | Kind::Normal(_) => println!("{}", self.name),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
Kind::Eof => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Prints a line with relavent metadata included,
|
/// Prints a line with relavent metadata included,
|
||||||
/// colorizing the filename based on the filetype.
|
/// colorizing the filename based on the filetype.
|
||||||
/// Similar to GNU `ls --long --color auto`
|
/// Similar to GNU `ls --long --color auto`
|
||||||
@ -299,7 +306,7 @@ impl Listing {
|
|||||||
/// Can return an `Error` if stdout is unavailable
|
/// Can return an `Error` if stdout is unavailable
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
#[cfg(feature = "color")]
|
#[cfg(feature = "color")]
|
||||||
pub fn print_color(&self) -> Result<(), Error> {
|
pub fn print_color(&self, human: bool) -> Result<(), Error> {
|
||||||
print!(
|
print!(
|
||||||
"{}{}{}{}{}{}{}{}{}{} {:>4}:{:<4} ",
|
"{}{}{}{}{}{}{}{}{}{} {:>4}:{:<4} ",
|
||||||
match self.kind {
|
match self.kind {
|
||||||
@ -355,8 +362,20 @@ impl Listing {
|
|||||||
self.gid,
|
self.gid,
|
||||||
);
|
);
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Kind::Normal(s) => print!("{s:>10} "),
|
Kind::Normal(s) => {
|
||||||
_ => print!("{:>10} ", "-"),
|
if human {
|
||||||
|
print!("{:>10} ", HumanSize::from(s));
|
||||||
|
} else {
|
||||||
|
print!("{s:>10} ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if human {
|
||||||
|
print!("{:>7} ", "-");
|
||||||
|
} else {
|
||||||
|
print!("{:>10} ", "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
match NaiveDateTime::from_timestamp_opt(self.mtime as i64, 0) {
|
match NaiveDateTime::from_timestamp_opt(self.mtime as i64, 0) {
|
||||||
Some(dt) => print!("{dt} "),
|
Some(dt) => print!("{dt} "),
|
||||||
|
Loading…
Reference in New Issue
Block a user