ezcache 0.2.0

Easy and flexible cache library for Rust
Documentation
use std::{fmt::Display, ops::DivAssign, time::Instant};

use ezcache::{prelude::*, TryCacheStoreErrorMap};
use rand::Rng;
use sha2::{Digest, Sha256};

const SOURCES: &[(&str, &str)] = &[
    (
        "javalsai/lidm latest zip",
        "https://github.com/javalsai/lidm/archive/refs/heads/master.zip",
    ),
    (
        "wikipedia article on rust",
        "https://en.wikipedia.org/wiki/Rust_(programming_language)",
    ),
    (
        "rust-lang/rust latest zip",
        "https://github.com/rust-lang/rust/archive/refs/heads/master.zip",
    ),
];

#[derive(Debug)]
pub struct E(pub reqwest::Error);
impl From<std::convert::Infallible> for E {
    fn from(_: std::convert::Infallible) -> Self {
        unreachable!()
    }
}
impl From<reqwest::Error> for E {
    fn from(value: reqwest::Error) -> Self {
        Self(value)
    }
}

fn main() {
    let args: Vec<_> = std::env::args().collect();
    let n: usize = args.get(1).map_or((SOURCES.len() * 5).div_ceil(2), |a| {
        a.parse().expect("argument was not a valid number")
    });

    let store = MemoryStore::new();
    let store: TryCacheStoreErrorMap<_, _, _, E, _> = TryCacheStoreErrorMap::from_store(store);
    let mut store = TryGenCacheStoreWrapper::new(
        store,
        |k: &&str, (client,): (&reqwest::blocking::Client,)| {
            Ok(client.get(*k).send()?.error_for_status()?.bytes()?.to_vec())
        },
    );

    let client = reqwest::blocking::Client::new();
    let mut rng = rand::thread_rng();

    for i in 0..n {
        let (name, url) = SOURCES[rng.gen::<usize>() % SOURCES.len()];
        println!(
            "\x1b[1;33m{}\x1b[0m: downloading \x1b[36m{name}\x1b[0m - \x1b[35m{url}\x1b[0m",
            i + 1
        );

        let a = Instant::now();
        let value = store
            .try_get_or_new(url, (&client,))
            .expect("unknown error downloading");
        let b = Instant::now();

        let hash = Sha256::new()
            .chain_update(&value)
            .finalize()
            .into_iter()
            .fold(String::new(), |acc, b| acc + &format!("{b:X}"));
        #[allow(clippy::cast_precision_loss)]
        let size = normalize_len(value.len() as f32);

        println!(
            "fetched \x1b[35m{size}\x1b[0m in \x1b[35m{:?}\x1b[0m (sha256 \x1b[1;4;30m{hash}\x1b[0m)\n",
            b - a
        );
    }
}

pub static MAGNITUDE_PREFIX_BINARY: &[&str] = &["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"];
fn normalize_len<T: DivAssign + PartialOrd + Copy + From<u16> + Display>(mut amount: T) -> String {
    let max_idx = MAGNITUDE_PREFIX_BINARY.len() - 1;
    let mut unit_idx = 0;
    let radix = T::from(1024);

    while amount >= radix && unit_idx < max_idx {
        amount /= radix;
        unit_idx += 1;
    }

    format!("{amount:.2}{}B", MAGNITUDE_PREFIX_BINARY[unit_idx])
}