use anyhow::Result;
use std::io::{self, Read};
pub(super) fn to_human_readable_size(size: u64) -> String {
let prefix = ["", "k", "M", "G", "T", "P", "E"];
let exp = 1024.0;
let log_value = if size > 0 {
(size as f32).log(exp) as usize
} else {
0
};
format!(
"{:.prec$} {}B",
size as f32 / exp.powi(log_value as i32),
prefix[log_value],
prec = if log_value > 0 { 2 } else { 0 }
)
}
pub(crate) fn to_hex_string(bytes: &[u8]) -> String {
use std::fmt::Write as _;
bytes.iter().fold(String::new(), |mut output, b| {
let _ = write!(output, "{b:02x}");
output
})
}
pub trait Progress {
fn set_length(&mut self, len: u64);
fn inc(&mut self, delta: u64);
fn finish(&mut self);
}
pub(super) struct ProgressReader<R: Read, C: FnMut(usize) -> Result<()>> {
reader: R,
cb: C,
}
impl<R: Read, C: FnMut(usize) -> Result<()>> ProgressReader<R, C> {
pub(super) fn new(reader: R, cb: C) -> Self {
Self { reader, cb }
}
}
impl<R: Read, C: FnMut(usize) -> Result<()>> Read for ProgressReader<R, C> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let read = self.reader.read(buf)?;
(self.cb)(read).map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{e:?}")))?;
Ok(read)
}
}
#[cfg(test)]
mod tests {
#[test]
fn to_human_readable_size() {
let sizes = [
(0u64, "0 B"),
(1023u64, "1023 B"),
(9253u64, "9.04 kB"),
(3771286u64, "3.60 MB"),
(8363220129, "7.79 GB"),
(7856731783569, "7.15 TB"),
(4799178968842384, "4.26 PB"),
(3424799178968842384, "2.97 EB"),
];
for (size, expected) in sizes {
assert_eq!(super::to_human_readable_size(size), expected);
}
}
#[test]
fn to_hex_string() {
assert_eq!(
&super::to_hex_string(b"chucknorris"),
"636875636b6e6f72726973"
);
}
}