Skip to main content

steamengine_renderer_util/resources/
mod.rs

1use fs_extra::dir::get_dir_content;
2use hashbrown::hash_map::HashMap;
3use rayon::prelude::*;
4use std::fs;
5use tracing::*;
6
7/// Implementation of resource loader for model
8#[cfg(feature = "model-resource-manager")]
9pub mod model;
10
11/// Implementation of resource loader for texture
12#[cfg(feature = "texture-resource-manager")]
13pub mod texture;
14
15/// Identifier for the resources
16#[derive(PartialEq, Eq, Hash, Debug, Clone)]
17pub struct Identifier {
18    /// Root folder of the resource
19    /// First folder to search
20    /// Example 'assets' or 'resources'
21    pub root: String,
22    /// Group of the resource
23    /// Folder inside root, 'textures' or 'models'
24    pub group: String,
25    /// Id of the resource
26    /// File inside the group folder, or alone file inside the root folder
27    pub id: String,
28}
29impl Identifier {
30    /// Convert a path into a Indentifier
31    /// Example
32    /// ```rust
33    /// let identifier = Indentifier::parse_from_str("assets/textures/tree.png");
34    /// assert_eq!(identifier.root, "assets");
35    /// assert_eq!(identifier.group, "textures");
36    /// assert_eq!(identifier.id, "tree.png");
37    /// ```
38    /// If the path doesnt has a group
39    /// ```rust
40    /// let identifier = Identifier::parse_from_str("assets/cube.obj");
41    /// assert_eq!(identifier.root, "assets");
42    /// assert_eq!(identifier.group, "");
43    /// assert_eq!(identifier.id, "tree.png")
44    /// ```
45    ///
46    pub fn parse_from_str(id: &str) -> Self {
47        let seg: Vec<&str> = id.splitn(3, "/").collect();
48        if seg.len() < 3 {
49            let root = seg[0].to_owned();
50            let id = seg[1].to_owned();
51            return Self {
52                root,
53                group: String::new(),
54                id,
55            };
56        }
57        let root = seg[0].to_owned();
58        let group = seg[1].to_owned();
59        let id = seg[2].to_owned();
60        Self { root, group, id }
61    }
62    pub fn root(&self) -> String {
63        self.root.clone()
64    }
65    pub fn group(&self) -> String {
66        self.group.clone()
67    }
68    pub fn id(&self) -> String {
69        self.id.clone()
70    }
71    pub fn new(&self, root: &str, group: &str, id: &str) -> Self {
72        Self {
73            root: root.to_owned(),
74            group: group.to_owned(),
75            id: id.to_owned(),
76        }
77    }
78}
79impl std::fmt::Display for Identifier {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        f.write_str(&format!("{}-{}:{}", self.root(), self.group(), self.id()))
82    }
83}
84
85/// Abstraction of a resource loader
86pub trait ResourceLoader: Send + Sync {
87    type Resource: Send + Sync;
88    type Error: std::error::Error
89        + Send
90        + Sync
91        + 'static
92        + From<std::io::Error>
93        + From<fs_extra::error::Error>;
94    /// Implemented by default, load a file from path
95    fn load_from_path(&self, path: &str) -> Result<Self::Resource, Self::Error> {
96        let file = fs::read(path)?;
97        self.load_from_bytes(file)
98    }
99    /// Load a file system into a hashmap
100    fn load_all(&self, root: &str) -> Result<HashMap<Identifier, Self::Resource>, Self::Error> {
101        let entries = get_dir_content(root)?;
102        let result: HashMap<Identifier, Self::Resource> = entries
103            .files
104            .par_iter()
105            .filter_map(|entry| {
106                debug!(
107                    "READ ENTRY \"{}\" WITH RESOURCE LOADER \"{}\"",
108                    entry,
109                    self.label()
110                );
111                let id = Identifier::parse_from_str(entry);
112                let res = match self.load_from_path(entry) {
113                    Ok(res) => res,
114                    Err(err) => {
115                        error!("Error loading entry in \"{}\" - {}", self.label(), err);
116                        return None;
117                    }
118                };
119                Some((id, res))
120            })
121            .collect();
122        Ok(result)
123    }
124    /// load a file from the bytes
125    fn load_from_bytes(&self, bytes: Vec<u8>) -> Result<Self::Resource, Self::Error>;
126    /// Name of the resource loader
127    fn label(&self) -> &'static str;
128}