rpkg_rs/misc/
hash_path_list.rs

1use crate::misc::resource_id::ResourceID;
2use crate::resource::runtime_resource_id::RuntimeResourceID;
3use rayon::iter::ParallelIterator;
4use rayon::prelude::IntoParallelIterator;
5use std::collections::HashMap;
6use std::fs::read_to_string;
7use std::path::Path;
8use std::str::FromStr;
9use thiserror::Error;
10
11#[derive(Debug, Error)]
12pub enum PathListError {
13    #[error("{0}")]
14    IoError(#[from] std::io::Error),
15
16    #[error("Invalid RuntimeResourceID entry")]
17    InvalidRuntimeResourceID,
18}
19
20/// A rainbow table of hashed paths with associated paths.
21#[derive(Default, Debug)]
22pub struct PathList {
23    pub entries: HashMap<RuntimeResourceID, Option<ResourceID>>,
24}
25
26impl PathList {
27    /// Creates a new empty PathList.
28    pub fn new() -> Self {
29        Self::default()
30    }
31
32    /// Parses a file into the PathList.
33    ///
34    /// Example of an input file:
35    /// ```txt
36    /// #comments will be ingored!
37    /// 00546F0BD4E80484.GFXI,[assembly:/any/path/here/file.jpg].pc_gfx
38    /// 0023456789ABCDEF.WWEM, this_will_fail_the_md5_validation
39    /// 003456789ABCDEF0.FXAS,[assembly:/lorem/ipsum/dolor_sit/amet/ai/consectetur/adipisicing.animset].pc_animset
40    /// ....
41    /// ```
42    ///
43    /// # Arguments
44    ///
45    /// * `path` - The path to the file to parse.
46    pub fn parse_into<P: AsRef<Path>>(&mut self, path: P) -> Result<&Self, PathListError> {
47        let file_as_string = read_to_string(path).map_err(PathListError::IoError)?;
48        let lines: Vec<_> = file_as_string.lines().map(String::from).collect();
49
50        let lines_par = lines.into_par_iter();
51
52        self.entries = lines_par
53            .filter_map(|line_res| {
54                if line_res.starts_with('#') {
55                    return None;
56                };
57
58                let (hash, path) = match line_res.split_once(',') {
59                    Some((h, p)) => (h.split_once('.').unwrap().0, Some(p)),
60                    None => (line_res.as_str(), None),
61                };
62
63                if let Ok(id) = u64::from_str_radix(hash, 16) {
64                    if let Some(path) = path {
65                        if let Ok(rid) = ResourceID::from_str(path) {
66                            if rid.is_valid() {
67                                return Some((RuntimeResourceID::from(id), Some(rid)));
68                            }
69                        }
70                    }
71                    Some((RuntimeResourceID::from(id), None))
72                } else {
73                    None
74                }
75            })
76            .collect::<Vec<_>>()
77            .into_iter()
78            .collect();
79
80        Ok(self)
81    }
82
83    pub fn get(&self, key: &RuntimeResourceID) -> Option<&ResourceID> {
84        if let Some(value) = self.entries.get(key) {
85            if let Some(path) = value {
86                return Some(path);
87            }
88            return None;
89        }
90        None
91    }
92}