1use anyhow::Result;
4use std::io::{self, Read};
5
6pub(super) fn to_human_readable_size(size: u64) -> String {
8 let prefix = ["", "k", "M", "G", "T", "P", "E"];
9 let exp = 1024.0;
10 let log_value = if size > 0 {
11 (size as f32).log(exp) as usize
12 } else {
13 0
14 };
15 format!(
16 "{:.prec$} {}B",
17 size as f32 / exp.powi(log_value as i32),
18 prefix[log_value],
19 prec = if log_value > 0 { 2 } else { 0 }
20 )
21}
22
23pub(crate) fn to_hex_string(bytes: &[u8]) -> String {
25 use std::fmt::Write as _;
26 bytes.iter().fold(String::new(), |mut output, b| {
27 let _ = write!(output, "{b:02x}");
29 output
30 })
31}
32
33pub trait Progress {
35 fn set_length(&mut self, len: u64);
37 fn inc(&mut self, delta: u64);
39 fn finish(&mut self);
41}
42
43pub(super) struct ProgressReader<R: Read, C: FnMut(usize) -> Result<()>> {
46 reader: R,
47 cb: C,
48}
49
50impl<R: Read, C: FnMut(usize) -> Result<()>> ProgressReader<R, C> {
51 pub(super) fn new(reader: R, cb: C) -> Self {
52 Self { reader, cb }
53 }
54}
55
56impl<R: Read, C: FnMut(usize) -> Result<()>> Read for ProgressReader<R, C> {
57 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
58 let read = self.reader.read(buf)?;
59 (self.cb)(read).map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{e:?}")))?;
60 Ok(read)
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 #[test]
67 fn to_human_readable_size() {
68 let sizes = [
69 (0u64, "0 B"),
70 (1023u64, "1023 B"),
71 (9253u64, "9.04 kB"),
72 (3771286u64, "3.60 MB"),
73 (8363220129, "7.79 GB"),
74 (7856731783569, "7.15 TB"),
75 (4799178968842384, "4.26 PB"),
76 (3424799178968842384, "2.97 EB"),
77 ];
78 for (size, expected) in sizes {
79 assert_eq!(super::to_human_readable_size(size), expected);
80 }
81 }
82
83 #[test]
84 fn to_hex_string() {
85 assert_eq!(
86 &super::to_hex_string(b"chucknorris"),
87 "636875636b6e6f72726973"
88 );
89 }
90}