use std::collections::HashMap;
use std::io::{self, Cursor, Read};
use std::sync::OnceLock;
use thiserror::Error;
use zip::ZipArchive;
static BUNDLE_ZIP_URL: &str = "https://bootmii.org/hackmii_installer_v1.2.zip";
#[derive(Debug, Error)]
pub enum BundleError {
#[error("network error: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("zip decoding error: {0}")]
Zip(#[from] zip::result::ZipError),
#[error("io error: {0}")]
Io(#[from] io::Error),
}
pub fn bundle() -> &'static Result<HashMap<String, Vec<u8>>, BundleError> {
static BUNDLE: OnceLock<Result<HashMap<String, Vec<u8>>, BundleError>> = OnceLock::new();
BUNDLE.get_or_init(|| {
let prefix = BUNDLE_ZIP_URL
.split('/')
.next_back()
.unwrap_or("")
.split(".zip")
.next()
.unwrap_or("")
.to_owned()
+ "/";
let resp = reqwest::blocking::get(BUNDLE_ZIP_URL)?;
let bytes = resp.bytes()?;
let reader = Cursor::new(bytes);
let mut zip = ZipArchive::new(reader)?;
let mut map = HashMap::new();
for idx in 0..zip.len() {
let mut file = zip.by_index(idx)?;
let file_name = file.name().to_owned();
if file_name.ends_with('/') {
continue;
}
let mut buf = Vec::with_capacity(file.size() as usize);
file.read_to_end(&mut buf)?;
map.insert(
file_name
.strip_prefix(&prefix)
.unwrap_or(file_name.as_str())
.to_string(),
buf,
);
}
Ok(map)
})
}
#[cfg(test)]
mod tests {
use super::bundle;
#[test]
fn test_bundle() {
let bundle = bundle().as_ref().ok();
assert!(&bundle.unwrap().len() > &0);
for (name, data) in bundle.unwrap() {
println!("in bundle: {:<26}: {:>11} bytes", name, data.len());
}
}
}