use std::{fs::File, sync::Arc, path::Path, ffi::OsStr, io::Read}; use image::ImageError; use crate::{resource_manager::ResourceStorage, texture::Texture, resource::Resource}; use super::{LoaderError, ResourceLoader}; impl From for LoaderError { fn from(value: ImageError) -> Self { LoaderError::DecodingError(anyhow::Error::from(value)) } } /// A struct that implements the `ResourceLoader` trait used for loading textures. #[derive(Default)] pub struct TextureLoader; impl ResourceLoader for TextureLoader { fn extensions(&self) -> &[&str] { &[ // the extensions of these are the names of the formats "bmp", "dds", "gif", "ico", "jpeg", "jpg", "png", "qoi", "tga", "tiff", "webp", // farbfeld "ff", // pnm "pnm", "pbm", "pgm", "ppm", "pam", ] } fn does_support_file(&self, path: &str) -> bool { match Path::new(path).extension().and_then(OsStr::to_str) { Some(ext) => { self.extensions().contains(&ext) }, _ => false, } } fn load(&self, path: &str) -> Result, LoaderError> { // check if the file is supported by this loader if !self.does_support_file(path) { return Err(LoaderError::UnsupportedExtension(path.to_string())); } // read file bytes let mut file = File::open(path)?; let mut buf = vec![]; file.read_to_end(&mut buf)?; // load the image and construct Resource let image = image::load_from_memory(&buf) .map_err(|e| match e { ImageError::IoError(e) => LoaderError::IOError(e), _ => LoaderError::DecodingError(e.into()), })?; let texture = Texture { image, }; let res = Resource::with_data(path, texture); Ok(Arc::new(res)) } } #[cfg(test)] mod tests { use super::*; fn get_image(path: &str) -> String { let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap(); format!("{manifest}/test_files/img/{path}") } /// Ensure that `does_support_file` works #[test] fn check_support() { let loader = TextureLoader::default(); let extensions = loader.extensions(); let fake_paths: Vec = extensions.iter().map(|e| format!("a.{}", e)).collect(); for path in fake_paths.iter() { assert!(loader.does_support_file(&path)); } } #[test] fn check_unsupport() { let loader = TextureLoader::default(); assert_eq!(loader.does_support_file("test.gltf"), false); } /// Tests loading an image #[test] fn image_load() { let loader = TextureLoader::default(); loader.load(&get_image("squiggles.png")).unwrap(); } #[test] fn image_load_unsupported() { let loader = TextureLoader::default(); assert!(loader.load(&get_image("squiggles.gltf")).is_err()); } }