hrdf_parser/
hrdf.rs

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    /// Loads and parses the data.<br>
24    /// If an URL is provided, the HRDF archive (ZIP file) is downloaded automatically. If a path is provided, it must absolutely point to an HRDF archive (ZIP file).<br>
25    /// The ZIP archive is automatically decompressed into the temp_dir of the OS folder.
26    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            // Loading from cache.
43            log::info!("Loading HRDF data from cache ({cache_path})...");
44
45            // If loading from cache fails, None is returned.
46            Hrdf::load_from_cache(&cache_path).ok()
47        } else {
48            // No loading from cache.
49            None
50        };
51
52        let hrdf = if let Some(hrdf) = hrdf {
53            // The cache has been loaded without error.
54            hrdf
55        } else {
56            // The cache must be built.
57            // If cache loading has failed, the cache must be rebuilt.
58            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                    // The data must be downloaded.
67                    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                // The data must be decompressed.
87                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    // Getters/Setters
112
113    pub fn data_storage(&self) -> &DataStorage {
114        &self.data_storage
115    }
116
117    // Functions
118
119    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}