hrdf_parser/
hrdf.rs

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    /// Loads and parses the data.<br>
23    /// 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>
24    /// The ZIP archive is automatically decompressed into the temp_dir of the OS folder.
25    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            // Loading from cache.
39            log::info!("Loading HRDF data from cache ({cache_path:?})...");
40
41            // If loading from cache fails, None is returned.
42            Hrdf::load_from_cache(&cache_path).ok()
43        } else {
44            // No loading from cache.
45            None
46        };
47
48        let hrdf = if let Some(hrdf) = hrdf {
49            // The cache has been loaded without error.
50            hrdf
51        } else {
52            // The cache must be built.
53            // If cache loading has failed, the cache must be rebuilt.
54            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                    // The data must be downloaded.
59                    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                // The data must be decompressed.
75                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    // Getters/Setters
100
101    pub fn data_storage(&self) -> &DataStorage {
102        &self.data_storage
103    }
104
105    // Functions
106
107    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}