1use std::{
2 env,
3 fs::{self, File},
4 io::{BufReader, Cursor},
5 path::{Path, PathBuf},
6 time::Instant,
7};
8
9use crate::{error::HResult, models::Version, storage::DataStorage};
10use bincode::config;
11use serde::{Deserialize, Serialize};
12use sha2::{Digest, Sha256};
13use url::Url;
14use zip::ZipArchive;
15
16#[derive(Debug, Serialize, Deserialize)]
17pub struct Hrdf {
18 data_storage: DataStorage,
19}
20
21impl Hrdf {
22 pub async fn new(
26 version: Version,
27 url_or_path: &str,
28 force_rebuild_cache: bool,
29 cache_prefix: Option<String>,
30 ) -> HResult<Self> {
31 let now = Instant::now();
32
33 let unique_filename = format!("{:x}", Sha256::digest(url_or_path.as_bytes()));
34 let cache_path = PathBuf::from(&cache_prefix.unwrap_or(String::from("./")))
35 .join(format!("{unique_filename}.cache"));
36
37 let hrdf = if cache_path.exists() && !force_rebuild_cache {
38 log::info!("Loading HRDF data from cache ({cache_path:?})...");
40
41 Hrdf::load_from_cache(&cache_path).ok()
43 } else {
44 None
46 };
47
48 let hrdf = if let Some(hrdf) = hrdf {
49 hrdf
51 } else {
52 let compressed_data_path = if Url::parse(url_or_path).is_ok() {
55 let compressed_data_path = env::temp_dir().join(format!("{unique_filename}.zip"));
56
57 if !compressed_data_path.exists() {
58 log::info!("Downloading HRDF data to {compressed_data_path:?}...");
60 let response = reqwest::get(url_or_path).await?.error_for_status()?;
61 let mut file = std::fs::File::create(&compressed_data_path)?;
62 let mut content = Cursor::new(response.bytes().await?);
63 std::io::copy(&mut content, &mut file)?;
64 }
65
66 compressed_data_path
67 } else {
68 PathBuf::from(url_or_path)
69 };
70
71 let decompressed_data_path = env::temp_dir().join(unique_filename);
72
73 if !decompressed_data_path.exists() {
74 log::info!("Unzipping HRDF archive into {decompressed_data_path:?}...");
76 let file = File::open(&compressed_data_path)?;
77 let mut archive = ZipArchive::new(BufReader::new(file))?;
78 archive.extract(&decompressed_data_path)?;
79 }
80
81 log::info!("Parsing HRDF data from {decompressed_data_path:?}...");
82
83 let hrdf = Self {
84 data_storage: DataStorage::new(version, &decompressed_data_path)?,
85 };
86
87 log::info!("Building cache...");
88 hrdf.build_cache(&cache_path)?;
89 hrdf
90 };
91
92 let elapsed = now.elapsed();
93
94 log::info!("HRDF data loaded in {elapsed:.2?}!");
95
96 Ok(hrdf)
97 }
98
99 pub fn data_storage(&self) -> &DataStorage {
102 &self.data_storage
103 }
104
105 pub fn build_cache(&self, path: &Path) -> HResult<()> {
108 let data = bincode::serde::encode_to_vec(self, config::standard())?;
109 fs::write(path, data)?;
110 Ok(())
111 }
112
113 pub fn load_from_cache(path: &Path) -> HResult<Self> {
114 let data = fs::read(path)?;
115 let (hrdf, _) = bincode::serde::decode_from_slice(&data, config::standard())?;
116 Ok(hrdf)
117 }
118}