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 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 ) -> Result<Self, Box<dyn Error>> {
31 let now = Instant::now();
32
33 let unique_filename = format!("{:x}", Sha256::digest(url_or_path.as_bytes()));
34 let cache_path = format!(
35 "{}/{unique_filename}.cache",
36 cache_prefix.unwrap_or(String::from("./"))
37 )
38 .replace("//", "/");
39
40 let hrdf = if Path::new(&cache_path).exists() && !force_rebuild_cache {
41 log::info!("Loading HRDF data from cache ({cache_path})...");
43
44 Hrdf::load_from_cache(&cache_path).ok()
46 } else {
47 None
49 };
50
51 let hrdf = if let Some(hrdf) = hrdf {
52 hrdf
54 } else {
55 let compressed_data_path = if Url::parse(url_or_path).is_ok() {
58 let compressed_data_path = format!("/tmp/{unique_filename}.zip");
59
60 if !Path::new(&compressed_data_path).exists() {
61 log::info!("Downloading HRDF data to {compressed_data_path}...");
63 let response = reqwest::get(url_or_path).await?;
64 let mut file = std::fs::File::create(&compressed_data_path)?;
65 let mut content = Cursor::new(response.bytes().await?);
66 std::io::copy(&mut content, &mut file)?;
67 }
68
69 compressed_data_path
70 } else {
71 url_or_path.to_string()
72 };
73
74 let decompressed_data_path = format!("/tmp/{unique_filename}");
75
76 if !Path::new(&decompressed_data_path).exists() {
77 log::info!("Unzipping HRDF archive into {decompressed_data_path}...");
79 let file = File::open(&compressed_data_path)?;
80 let mut archive = ZipArchive::new(BufReader::new(file))?;
81 archive.extract(&decompressed_data_path)?;
82 }
83
84 log::info!("Parsing HRDF data from {decompressed_data_path}...");
85
86 let hrdf = Self {
87 data_storage: DataStorage::new(version, &decompressed_data_path)?,
88 };
89
90 log::info!("Building cache...");
91 hrdf.build_cache(&cache_path)?;
92 hrdf
93 };
94
95 let elapsed = now.elapsed();
96
97 log::info!("HRDF data loaded in {:.2?}!", elapsed);
98
99 Ok(hrdf)
100 }
101
102 pub fn data_storage(&self) -> &DataStorage {
105 &self.data_storage
106 }
107
108 pub fn build_cache(&self, path: &str) -> Result<(), Box<dyn Error>> {
111 let data = bincode::serde::encode_to_vec(self, config::standard())?;
112 fs::write(path, data)?;
113 Ok(())
114 }
115
116 pub fn load_from_cache(path: &str) -> Result<Self, Box<dyn Error>> {
117 let data = fs::read(path)?;
118 let (hrdf, _) = bincode::serde::decode_from_slice(&data, config::standard())?;
119 Ok(hrdf)
120 }
121}