use std::{
error::Error,
fs::{self, File},
io::{BufReader, Cursor},
path::Path,
time::Instant,
};
use crate::{models::Version, storage::DataStorage};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use url::Url;
use zip::ZipArchive;
#[derive(Debug, Serialize, Deserialize)]
pub struct Hrdf {
data_storage: DataStorage,
}
impl Hrdf {
pub async fn new(
version: Version,
url_or_path: &str,
force_rebuild_cache: bool,
) -> Result<Self, Box<dyn Error>> {
let now = Instant::now();
let unique_filename = format!("{:x}", Sha256::digest(url_or_path.as_bytes()));
let cache_path = format!("{unique_filename}.cache");
let hrdf = if Path::new(&cache_path).exists() && !force_rebuild_cache {
log::info!("Loading HRDF data from cache ({cache_path})...");
Hrdf::load_from_cache(&cache_path).ok()
} else {
None
};
let hrdf = if let Some(hrdf) = hrdf {
hrdf
} else {
let compressed_data_path = if Url::parse(url_or_path).is_ok() {
let compressed_data_path = format!("/tmp/{unique_filename}.zip");
if !Path::new(&compressed_data_path).exists() {
log::info!("Downloading HRDF data to {compressed_data_path}...");
let response = reqwest::get(url_or_path).await?;
let mut file = std::fs::File::create(&compressed_data_path)?;
let mut content = Cursor::new(response.bytes().await?);
std::io::copy(&mut content, &mut file)?;
}
compressed_data_path
} else {
url_or_path.to_string()
};
let decompressed_data_path = format!("/tmp/{unique_filename}");
if !Path::new(&decompressed_data_path).exists() {
log::info!("Unzipping HRDF archive into {decompressed_data_path}...");
let file = File::open(&compressed_data_path)?;
let mut archive = ZipArchive::new(BufReader::new(file))?;
archive.extract(&decompressed_data_path)?;
}
log::info!("Parsing HRDF data from {decompressed_data_path}...");
let hrdf = Self {
data_storage: DataStorage::new(version, &decompressed_data_path)?,
};
log::info!("Building cache...");
hrdf.build_cache(&cache_path)?;
hrdf
};
let elapsed = now.elapsed();
log::info!("HRDF data loaded in {:.2?}!", elapsed);
Ok(hrdf)
}
pub fn data_storage(&self) -> &DataStorage {
&self.data_storage
}
pub fn build_cache(&self, path: &str) -> Result<(), Box<dyn Error>> {
let data = bincode::serialize(&self)?;
fs::write(path, data)?;
Ok(())
}
pub fn load_from_cache(path: &str) -> Result<Self, Box<dyn Error>> {
let data = fs::read(path)?;
let hrdf: Self = bincode::deserialize(&data)?;
Ok(hrdf)
}
}