hrdf_parser/
hrdf.rs

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    /// 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 /tmp 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    ) -> 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            // Loading from cache.
42            log::info!("Loading HRDF data from cache ({cache_path})...");
43
44            // If loading from cache fails, None is returned.
45            Hrdf::load_from_cache(&cache_path).ok()
46        } else {
47            // No loading from cache.
48            None
49        };
50
51        let hrdf = if let Some(hrdf) = hrdf {
52            // The cache has been loaded without error.
53            hrdf
54        } else {
55            // The cache must be built.
56            // If cache loading has failed, the cache must be rebuilt.
57            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                    // The data must be downloaded.
62                    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                // The data must be decompressed.
78                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    // Getters/Setters
103
104    pub fn data_storage(&self) -> &DataStorage {
105        &self.data_storage
106    }
107
108    // Functions
109
110    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}