sett 0.3.0

Rust port of sett (data compression, encryption and transfer tool).
Documentation
//! Miscellaneous utilities

use anyhow::Result;
use std::io::{self, Read};

/// Formats sizes in a human-readable format.
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 }
    )
}

/// Converts bytes into a hexadecimal value.
pub(crate) fn to_hex_string(bytes: &[u8]) -> String {
    use std::fmt::Write as _;
    bytes.iter().fold(String::new(), |mut output, b| {
        // `write!` to a String can never fail so it's safe to ignore the result.
        let _ = write!(output, "{b:02x}");
        output
    })
}

/// Keep track of a current progress.
pub trait Progress {
    /// Sets the length of the progress bar.
    fn set_length(&mut self, len: u64);
    /// Increments the current progress completion.
    fn inc(&mut self, delta: u64);
    /// Finalizes the progress bar.
    fn finish(&mut self);
}

/// Runs a callback function while reading through a reader to
/// report the current progress.
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"
        );
    }
}