fennel_engine/resources/mod.rs
1use std::{any::Any, cell::Ref, collections::HashMap, path::PathBuf};
2
3/// Module containing implementations of [`LoadableResource`] such as [`Image`]
4pub mod loadable;
5
6/// Manages a collection of loadable resources indexed by their name
7pub struct ResourceManager {
8 /// Map resource name to a type that implements [`LoadableResource`] trait
9 pub resources: HashMap<String, Box<dyn LoadableResource>>,
10}
11
12/// Trait that all loadable assets must implement
13pub trait LoadableResource: Any {
14 /// Load a resource from `path` and return it boxed
15 ///
16 /// # Errors
17 /// Returns an error if the file cannot be read or parsed
18 fn load(path: PathBuf) -> anyhow::Result<Box<dyn LoadableResource>>
19 where
20 Self: Sized;
21
22 /// Eaasy-to-use identifier for the resource
23 fn name(&self) -> String;
24
25 /// Return a mutable slice that the graphics thread can pass to SDL
26 fn as_mut_slice(&self) -> &mut [u8];
27
28 /// Return an immutable slice for read‑only access
29 fn as_slice(&self) -> Ref<'_, [u8]>;
30}
31
32/// evil &Box<dyn LoadableResource> to &T
33pub fn as_concrete<T: 'static + LoadableResource>(b: &Box<dyn LoadableResource>) -> &T {
34 let dyn_ref: &dyn LoadableResource = b.as_ref();
35
36 let any_ref = dyn_ref as &dyn Any;
37
38 any_ref
39 .downcast_ref::<T>()
40 .expect("incorrect concrete type")
41}
42
43impl ResourceManager {
44 /// Create a new manager with empty `resources` field
45 pub fn new() -> Self {
46 Self {
47 resources: HashMap::new(),
48 }
49 }
50
51 /// Insert a loaded asset into the cache
52 ///
53 /// The asset is stored under the key returned by `asset.name()`
54 pub fn cache_asset(&mut self, asset: Box<dyn LoadableResource>) -> anyhow::Result<()> {
55 self.resources.insert(asset.name(), asset);
56 Ok(())
57 }
58
59 // here i have NO fucking idea should it be `&Box<dyn LoadableResource>` or whatever
60 // self.resources.get returns a reference to the resource, so basically a reference to Box
61 // but afaik Box is a pointer, and for me it feels a bit fucking wrong to uh return a
62 // reference to a pointer >:3 and also clippy is angry at me for doing this
63 pub fn get_asset(&mut self, name: String) -> anyhow::Result<&Box<dyn LoadableResource>> {
64 let asset = self.resources.get(&name).unwrap();
65 Ok(asset)
66 }
67
68 pub fn is_cached(&mut self, name: String) -> bool {
69 self.resources.contains_key(&name)
70 }
71}
72
73impl Default for ResourceManager {
74 /// `default()` is equivalent to `ResourceManager::new()`.
75 fn default() -> Self {
76 Self::new()
77 }
78}