fennel_core/resources/
mod.rs1use std::{any::Any, cell::Ref, collections::HashMap, fs, path::PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{graphics::Graphics, resources::{font::DummyFont, image::Image}};
6
7pub mod image;
8pub mod font;
9
10pub struct ResourceManager {
12 pub resources: HashMap<String, Box<dyn LoadableResource>>,
14}
15
16unsafe impl Send for ResourceManager {}
17unsafe impl Sync for ResourceManager {}
18
19#[derive(Deserialize, Serialize, Debug)]
20enum AssetType {
21 Image,
22 Audio,
23 Font
24}
25
26#[derive(Deserialize, Serialize, Debug)]
27struct Asset {
29 name: String,
30 path: String,
31 #[serde(rename(deserialize = "type"))]
32 class: AssetType
33}
34
35#[derive(Deserialize, Debug)]
36struct Manifest {
38 pub assets: Vec<Asset>
39}
40
41pub trait LoadableResource: Any {
43 fn load(
53 path: PathBuf,
54 name: String,
55 graphics: &mut Graphics,
56 size: Option<f32>
57 ) -> anyhow::Result<Box<dyn LoadableResource>>
58 where
59 Self: Sized;
60
61 fn name(&self) -> String;
63
64 fn as_mut_slice(&self) -> Option<&mut [u8]> {
68 None
69 }
70
71 fn as_slice(&self) -> Option<Ref<'_, [u8]>> {
75 None
76 }
77}
78
79#[allow(clippy::borrowed_box)] pub fn downcast_ref<T: 'static + LoadableResource>(
84 b: &Box<dyn LoadableResource>,
85) -> anyhow::Result<&T> {
86 let dyn_ref: &dyn LoadableResource = b.as_ref();
87
88 let any_ref = dyn_ref as &dyn Any;
89
90 Ok(any_ref
91 .downcast_ref::<T>()
92 .expect("incorrect concrete type"))
93}
94
95impl ResourceManager {
96 pub fn new() -> Self {
98 Self {
99 resources: HashMap::new(),
100 }
101 }
102
103 pub fn load_dir(&mut self, path: PathBuf, graphics: &mut Graphics) -> anyhow::Result<()> {
108 let manifest_file = fs::read(path.join("manifest.toml"))?;
109 let manifest: Manifest = toml::from_slice(&manifest_file)?;
110 for asset in manifest.assets {
111 match asset.class {
112 AssetType::Image => {
113 let path = path.join(asset.path);
114 let image = Image::load(path.clone(), path.to_str().unwrap().to_string(), graphics, None)?;
115 println!("{:?}", image.name());
116 self.cache_asset(image)?;
117 },
118 AssetType::Audio => {},
119 AssetType::Font => {
120 let path = path.join(asset.path);
121 let font = DummyFont::load(path, asset.name, graphics, None)?;
122 println!("{:?}", font.name());
123 self.cache_asset(font)?;
124 }
125 }
126 }
127 Ok(())
128 }
129
130 pub fn cache_asset(&mut self, asset: Box<dyn LoadableResource>) -> anyhow::Result<()> {
134 self.resources.insert(asset.name(), asset);
135 Ok(())
136 }
137
138 #[allow(clippy::borrowed_box)] pub fn get_asset(&self, name: String) -> anyhow::Result<&Box<dyn LoadableResource>> {
144 let asset = self.resources.get(&name).unwrap_or_else(|| {
145 panic!("asset {name} not found")
146 });
147 Ok(asset)
148 }
149
150 pub fn is_cached(&self, name: String) -> bool {
152 self.resources.contains_key(&name)
153 }
154}
155
156impl Default for ResourceManager {
157 fn default() -> Self {
159 Self::new()
160 }
161}