use std::io::{Read, Seek};
use zarust::{ArchiveReader, EntryKind, NodeHandle};
use crate::cli::{
Failure,
ui::{counted, format_bytes},
};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Summary {
pub files: u64,
pub directories: u64,
pub bytes: u64,
}
impl Summary {
pub fn of<R: Read + Seek>(
reader: &ArchiveReader<R>,
root: NodeHandle,
) -> Result<Self, Failure> {
let mut summary = Self::default();
summary.accumulate(reader, root)?;
Ok(summary)
}
fn accumulate<R: Read + Seek>(
&mut self,
reader: &ArchiveReader<R>,
directory: NodeHandle,
) -> Result<(), Failure> {
for entry in reader.directory_entries(directory)? {
match entry.kind {
EntryKind::Directory => {
self.directories += 1;
self.accumulate(reader, entry.handle)?;
}
EntryKind::File => self.file(entry.size),
}
}
Ok(())
}
pub fn file(&mut self, size: u64) {
self.files += 1;
self.bytes = self.bytes.saturating_add(size);
}
pub fn directory(&mut self) {
self.directories += 1;
}
pub fn describe(&self) -> String {
format!(
"{}, {} · {}",
counted(self.files, "file", "files"),
counted(self.directories, "directory", "directories"),
format_bytes(self.bytes as f64),
)
}
}
pub fn ratio(part: u64, whole: u64) -> f64 {
if whole == 0 {
100.0
} else {
part as f64 * 100.0 / whole as f64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn describes_counts_with_matching_plurals() {
let one = Summary {
files: 1,
directories: 1,
bytes: 1024,
};
assert_eq!(one.describe(), "1 file, 1 directory · 1.0 KiB");
let many = Summary {
files: 0,
directories: 2,
bytes: 0,
};
assert_eq!(many.describe(), "0 files, 2 directories · 0 B");
}
#[test]
fn ratio_treats_an_empty_whole_as_complete() {
assert_eq!(ratio(0, 0), 100.0);
assert_eq!(ratio(1, 4), 25.0);
}
}