use dot_vox::DotVoxData; use image::DynamicImage; use lazy_static::lazy_static; use std::{ any::Any, collections::HashMap, fs::File, io::Read, sync::{Arc, RwLock}, }; #[derive(Debug, Clone)] pub enum Error { /// An asset has already been loaded with this specifier but anot type InvalidType, /// Asset does not exist NotFound(String), } impl From> for Error { fn from(_: Arc) -> Self { Error::InvalidType } } impl From for Error { fn from(err: std::io::Error) -> Self { Error::NotFound(format!("{:?}", err)) } } lazy_static! { static ref ASSETS: RwLock>> = RwLock::new(HashMap::new()); } /// Function used to load assets /// loaded assets are cached in a global singleton hashmap /// Example usage: /// ``` /// use image::DynamicImage; /// use common::assets; /// /// let my_image = assets::load::("core.ui.backgrounds.city").unwrap(); /// ``` pub fn load(specifier: &str) -> Result, Error> { Ok(ASSETS .write().unwrap() .entry(specifier.to_string()) .or_insert(Arc::new(A::load(specifier)?)) .clone() .downcast()?) } /// Function used to load assets that will panic if the asset is not found /// Use this to load essential assets /// loaded assets are cached in a global singleton hashmap /// Example usage: /// ``` /// use image::DynamicImage; /// use common::assets; /// /// let my_image = assets::load_expect::("core.ui.backgrounds.city"); /// ``` pub fn load_expect(specifier: &str) -> Arc { load(specifier) .expect(&format!("Failed loading essential asset: {}", specifier)) } /// Asset Trait pub trait Asset: Send + Sync + Sized { fn load(specifier: &str) -> Result; } impl Asset for DynamicImage { fn load(specifier: &str) -> Result { Ok(image::load_from_memory( load_from_path(specifier)?.as_slice() ) .unwrap() ) } } impl Asset for DotVoxData { fn load(specifier: &str) -> Result { Ok(dot_vox::load_bytes( load_from_path(specifier)?.as_slice() ) .unwrap() ) } } // TODO: System to load file from specifiers (eg "core.ui.backgrounds.city") fn try_open_with_path(name: &str) -> Option { // if it's stupid and it works.., [ "assets".to_string(), "../assets".to_string(), /* optimizations */ "../../assets".to_string(), [env!("CARGO_MANIFEST_DIR"), "/../assets"].concat(), [env!("CARGO_MANIFEST_DIR"), "/assets"].concat(), [env!("CARGO_MANIFEST_DIR"), "/../../assets"].concat(), "../../../assets".to_string(), [env!("CARGO_MANIFEST_DIR"), "/../../../assets"].concat(), ] .into_iter() .map(|bp| [bp, name].concat()) .find_map(|ref filename| match File::open(filename) { Ok(file) => { debug!("Loading {} successful", filename); Some(file) } Err(err) => { error!("Loading {} failed: {}", filename, err); None } }) } pub fn load_from_path(name: &str) -> Result, Error> { match try_open_with_path(name) { Some(mut f) => { let mut content = Vec::::new(); f.read_to_end(&mut content)?; Ok(content) }, None => { Err(Error::NotFound(name.to_owned())) } } } /* /// Translation Asset pub struct Translations { pub translations: Value } impl Translations { pub fn get_lang(&self, lang: &str) -> &str { self.translations[lang].as_str().unwrap() } } impl Asset for Translations { type T=Self; fn load(path: &str) -> Result{ let file_out = read_from_path(path).unwrap(); let content = from_utf8(file_out.as_slice()).unwrap(); let value = content.parse::().unwrap(); Ok(Translations{translations: value}) } } */