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