use std::{any::Any, cell::Ref, collections::HashMap, fs, path::PathBuf};
use serde::{Deserialize, Serialize};
use crate::{graphics::Graphics, resources::{font::DummyFont, image::Image}};
pub mod image;
pub mod font;
pub struct ResourceManager {
pub resources: HashMap<String, Box<dyn LoadableResource>>,
}
unsafe impl Send for ResourceManager {}
unsafe impl Sync for ResourceManager {}
#[derive(Deserialize, Serialize, Debug)]
enum AssetType {
Image,
Audio,
Font
}
#[derive(Deserialize, Serialize, Debug)]
struct Asset {
name: String,
path: String,
#[serde(rename(deserialize = "type"))]
class: AssetType
}
#[derive(Deserialize, Debug)]
struct Manifest {
pub assets: Vec<Asset>
}
pub trait LoadableResource: Any {
fn load(
path: PathBuf,
name: String,
graphics: &mut Graphics,
size: Option<f32>
) -> anyhow::Result<Box<dyn LoadableResource>>
where
Self: Sized;
fn name(&self) -> String;
fn as_mut_slice(&self) -> Option<&mut [u8]> {
None
}
fn as_slice(&self) -> Option<Ref<'_, [u8]>> {
None
}
}
#[allow(clippy::borrowed_box)] pub fn downcast_ref<T: 'static + LoadableResource>(
b: &Box<dyn LoadableResource>,
) -> anyhow::Result<&T> {
let dyn_ref: &dyn LoadableResource = b.as_ref();
let any_ref = dyn_ref as &dyn Any;
Ok(any_ref
.downcast_ref::<T>()
.expect("incorrect concrete type"))
}
impl ResourceManager {
pub fn new() -> Self {
Self {
resources: HashMap::new(),
}
}
pub fn load_dir(&mut self, path: PathBuf, graphics: &mut Graphics) -> anyhow::Result<()> {
let manifest_file = fs::read(path.join("manifest.toml"))?;
let manifest: Manifest = toml::from_slice(&manifest_file)?;
for asset in manifest.assets {
match asset.class {
AssetType::Image => {
let path = path.join(asset.path);
let image = Image::load(path.clone(), path.to_str().unwrap().to_string(), graphics, None)?;
println!("{:?}", image.name());
self.cache_asset(image)?;
},
AssetType::Audio => {},
AssetType::Font => {
let path = path.join(asset.path);
let font = DummyFont::load(path, asset.name, graphics, None)?;
println!("{:?}", font.name());
self.cache_asset(font)?;
}
}
}
Ok(())
}
pub fn cache_asset(&mut self, asset: Box<dyn LoadableResource>) -> anyhow::Result<()> {
self.resources.insert(asset.name(), asset);
Ok(())
}
#[allow(clippy::borrowed_box)] pub fn get_asset(&self, name: String) -> anyhow::Result<&Box<dyn LoadableResource>> {
let asset = self.resources.get(&name).unwrap_or_else(|| {
panic!("asset {name} not found")
});
Ok(asset)
}
pub fn is_cached(&self, name: String) -> bool {
self.resources.contains_key(&name)
}
}
impl Default for ResourceManager {
fn default() -> Self {
Self::new()
}
}