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